Makoto × Rust
Mapeamento conceito a conceito da linguagem Makoto para Rust. Gerado de
docs/mappings/_gen/em 2026-10-05. Fonte de verdade do design:docs/english/*.md; sintaxe Makoto dos exemplos marcados verificada no compiladormk. 173 conceitos — implementado: 146 · parcial: 7 · planejado: 10 · não existe (por design): 10
Legenda: implementado · parcial · planejado · não existe (por design)
Outras comparações: C · JavaScript · TypeScript · Java · Stdlib · Índice
Sumário
Section titled “Sumário”- Filosofia e modelo mental
- Declarações e tipos básicos
- Controle de fluxo
- Funções, lambdas, métodos e despacho
- Tipos: struct, enum, interface, union
- Coleções e Optional
- Strings e texto
- Erros: Result, conjuntos de erro, catch
- Memória: gerenciamento sem cor, ponteiros, ownership
- Concorrência: processos e canais
- Supervisão e resiliência (let it crash)
- Canais pela rede (
net) - Generics, comptime e reflexão
- Geradores (sequências preguiçosas)
- Contratos (DbC), assert/assume e unsafe
- FFI com C (e C++)
- Assembly inline
- Módulos, visibilidade e pacotes
- Observabilidade do runtime
- Testes, benchmarks, property-based e fuzzing
- Extensão UI (
.mkoui) - Camada de verificação (planejada)
- Ferramentas (
mk)
Filosofia e modelo mental
Section titled “Filosofia e modelo mental”Antes da sintaxe: o que a Makoto (誠, “sinceridade”) recusa de propósito. Quem vem de C/JS/TS/Java/Rust
tropeça mais nestes pontos do que em qualquer palavra-chave. Regra geral: o caso comum e seguro não leva
marcação; só capacidade extra ou perigo é marcado (var, mut, unsafe, @mm(none)…). Cada sistema da
linguagem (memória, concorrência, execução, tipos, FFI) carrega só as suas “cores” e não vaza pros outros.
Sem null / undefined / nil
Section titled “Sem null / undefined / nil”Status: não existe (por design)
Não existe null. Ausência é tipada: Optional[T] com some(v) / none. Um ponteiro *T nunca é nulo;
ponteiro que pode faltar é Optional[*T] (inclusive o NULL que vem do C).
Makoto
fn find(xs: []int, t: int) -> Optional[int] { loop i in 0..xs.len { if xs[i] == t { return some(i) } } return none}Rust
fn find(xs: &[i32], t: i32) -> Option<usize> { xs.iter().position(|&x| x == t)}Diferença: Equivalente direto:
Option<T>Optional[T],Somesome,Nonenone.
Sem exceções (erro é valor)
Section titled “Sem exceções (erro é valor)”Status: não existe (por design)
Não há throw/try/catch de exceção. Falha é valor: Result[T, E] ou um conjunto de erros error{...}.
Propagar é explícito (catch |e| { return e }) — não existe o operador ?. Falha irrecuperável é
runtime.panic(...), que mata só o processo atual (o supervisor decide o que fazer).
Makoto
fn parse(s: string) -> Result[int, error{Empty}] { if s == "" { return Err(error.Empty) } return Ok(42)}v := parse(s) catch |e| { return e } // propaga explicitamenteRust
fn parse(s: &str) -> Result<i32, ParseErr> { if s.is_empty() { return Err(ParseErr::Empty); } Ok(42)}let v = parse(s)?;Diferença: Muito próximo de
Result, mas Makoto não tem?: a propagação é escrita (catch |e| { return e }).
Sem classes nem herança
Section titled “Sem classes nem herança”Status: não existe (por design)
Não há class, extends, this implícito nem herança. Dados ficam em decl (struct); comportamento em
funções com receiver explícito (fn (p: Point) norm()); reuso por composição (@embeds) e
interfaces com satisfação estrutural (estilo Go).
Makoto
decl Point { x: int; y: int }fn (p: Point) sum() -> int { return p.x + p.y }p := Point{x: 1; y: 2}io.println(p.sum())Rust
struct Point { x: i32, y: i32 }impl Point { fn sum(&self) -> i32 { self.x + self.y } }Diferença: Parecido com
impl, mas o método é declarado solto, com o receiver nomeado (p), sem blocoimpl.
Sem async/await (IO sem cor)
Section titled “Sem async/await (IO sem cor)”Status: não existe (por design)
Não existe async, await, Future nem Promise. IO parece síncrono; o scheduler suspende só o
processo que bloqueou. Paralelismo de IO = vários processos (spawn) + canais. Nenhuma função é “colorida”.
Makoto
fn fetch(ch: Channel[int]) -> int { v := -> ch catch |e| { return 0 } // bloqueia só ESTE processo; os outros seguem return v}Rust
async fn handle(p: &str) -> std::io::Result<String> { tokio::fs::read_to_string(p).await}Diferença: Sem
async/.await/runtime externo (tokio): o runtime da Makoto já é o scheduler.
Sem sobrecarga de operadores/funções
Section titled “Sem sobrecarga de operadores/funções”Status: não existe (por design)
Tipo do usuário não sobrecarrega + - * / (use método nomeado: a.add(b)). Duas funções soltas com o mesmo
nome = erro “defined twice”. “Mesmo nome, tipos diferentes” é despacho explícito: fn name = { f1, f2 }
(function set) ou match T dentro de um genérico. Mesmo nome com aridade diferente = parâmetro default ou variádico.
Makoto
fn int_s(i: int) -> string { return "int" }fn bool_s(b: bool) -> string { return b ? "true" : "false" }fn to_s = { int_s, bool_s } // roteador por tipo, resolvido em compile-timeRust
trait ToS { fn to_s(&self) -> String; } // impl por tipoDiferença: Trait por tipo é o mais próximo; sem
impl Addpra tipo do usuário.
Sem retorno implícito
Section titled “Sem retorno implícito”Status: não existe (por design)
Bloco não vale a última expressão. Para produzir valor ou sair: return, break ou panic. Exceções de
glifo, que também são marcadores explícitos: o => do lambda de uma expressão e o ternário ?:. O => de um
braço de match não entrega valor — é separador.
Makoto
fn double(x: int) -> int { return x * 2 } // `x * 2` sozinho não compilav := match n { 0 => return "zero"; _ => return "outro" }Rust
fn dbl(x: i32) -> i32 { x * 2 } // última expressão = retornoDiferença: O estilo ‘última expressão’ do Rust é proibido em Makoto.
Sem zero-value / undefined implícito
Section titled “Sem zero-value / undefined implícito”Status: não existe (por design)
Todo binding nasce com um valor escrito. Não há int x; com lixo, nem zero-value do Go, nem undefined.
0, "" e List.new() só existem quando você escreve.
Makoto
var total := 0 // precisa do RHSvar xs: List[int] = List.new()Rust
let total: i32; // precisa atribuir antes de lerDiferença: Rust permite declarar e atribuir depois; Makoto não.
Declarações e tipos básicos
Section titled “Declarações e tipos básicos”Imutável é o padrão. := declara (tipo inferido), : T = declara com tipo, = sozinho só atribui (e só a
var). Inteiros têm qualquer largura de 1 a 65535 bits e estouro dá trap; floats são sempre finitos
(NaN/overflow = erro).
Binding imutável: := / let
Section titled “Binding imutável: := / let”Status: implementado
x := v (atalho) ou let x := v: imutável em runtime. Tipo explícito estilo Odin: x: int = 5. Redeclarar o
mesmo nome no mesmo escopo é erro (não há shadowing local; parâmetro pode ser sombreado no corpo).
Makoto
x := 5let y: int = 10x = 6 // ERRO: x não é varRust
let x = 5;let y: i32 = 10;Diferença: Igual ao
letdo Rust, mas sem shadowing no mesmo escopo.
Binding mutável: var
Section titled “Binding mutável: var”Status: implementado
var x := v é mutável. Um var que nunca é mutado é erro de compilação (use :=). Mutabilidade é do
lugar (o binding), não do campo: p.x = 1 só vale se p for var.
Makoto
var n := 0n = n + 1n += 2Rust
let mut n = 0; n += 3;Diferença:
varlet mut. Rust só avisa (warning) demutdesnecessário; Makoto dá erro.
Constante de compile-time: const
Section titled “Constante de compile-time: const”Status: implementado
const X := expr exige RHS conhecido em compile-time (pode chamar comptime fn). É o atalho de comptime let.
comptime var é o “rascunho” mutável que só existe durante a compilação.
Makoto
const SIZE := 64comptime fn fact(n: int) -> int { return n <= 1 ? 1 : n * fact(n - 1) }const F := fact(5) // 120, calculado pelo compiladorRust
const SIZE: usize = 64;const F: u64 = fact(5); // const fnDiferença:
const fndo Rustcomptime fn.
Descartar valor: _ :=
Section titled “Descartar valor: _ :=”Status: implementado
Largar um valor de retorno no chão sem _ := é erro: descartar é uma intenção e intenção se escreve.
Makoto
_ := compute()Rust
let _ = compute();Diferença: Igual; Rust só avisa com
#[must_use], Makoto exige sempre.
Múltiplos bindings, tuplas e swap
Section titled “Múltiplos bindings, tuplas e swap”Status: implementado
Tupla (a, b) agrupa valores. Desestruturar: q, r := f(). Troca: a, b = b, a. Função com vários retornos
sem erro devolve tupla.
Makoto
fn divmod(a: int, b: int) -> (int, int) { return (a / b, a % b) }q, r := divmod(7, 2)var a := 1var b := 2a, b = b, aRust
let (q, r) = divmod(7, 2);std::mem::swap(&mut a, &mut b);Inteiros de largura arbitrária iN/uN
Section titled “Inteiros de largura arbitrária iN/uN”Status: implementado
Família paramétrica: i3, u7, u777, i65535… Comuns: i8..i128, u8..u128, isize/usize,
int/uint (largura padrão), byte = u8, bit = u1. Aritmética checada contra a largura declarada.
Makoto
small: u3 = 7b: byte = 255big: u128 = 1Rust
let b: u8 = 255;let big: u128 = 1;Diferença: Rust tem larguras fixas;
u3precisa de crate (ex.:arbitrary-int).
Estouro dá trap; +% -% *% embrulham
Section titled “Estouro dá trap; +% -% *% embrulham”Status: implementado
+ - * fazem panic se o resultado sai da faixa (constante que estoura = erro de compilação). Aritmética
modular é pedida no operador: +%, -%, *%. Shift <</>> mascara pra largura (não dá trap).
Makoto
x: u8 = 250y := x +% 10 // 4 (embrulha)z := x + 10 // PANIC em runtime: estouro de u8Rust
let y = x.wrapping_add(10);let z = x + 10; // panic em debug, embrulha em releaseDiferença: Makoto = checked sempre (debug e release);
+%wrapping_add.
Conversão explícita com as
Section titled “Conversão explícita com as”Status: implementado
Conversões que perdem informação (largura, floatint) são escritas com as. as é só de valor (nunca de
ponteiro — pra ponteiro há mem.reinterpret, planejado).
Makoto
z := 300 as u8 // 44i := 3.9 as i32 // 3Rust
let z = 300u32 as u8; let i = 3.9f64 as i32;Floats IEEE finitos (NaN/inf = erro)
Section titled “Floats IEEE finitos (NaN/inf = erro)”Status: implementado
f16/f32/f64/f128/f256 + bf16; float = padrão. Uma operação que geraria NaN (0.0/0.0, √negativo) ou
estouraria pra inf é erro de runtime — todo float é finito e totalmente ordenado (< é bool puro).
Infinito direcionado de verdade é opt-in: ExtendedFloat.
Makoto
h: f16 = 1.5q: f128 = 1.0z := 0.0 / 0.0 // ERRO em runtime (NaN proibido)Rust
let z = 0.0_f64 / 0.0; // NaN; f64 é só PartialOrdDiferença: Makoto: sem NaN float é ordem total (como
Ord).
Precisão arbitrária: BigInt, Decimal, BigFloat, BigFloatArb
Section titled “Precisão arbitrária: BigInt, Decimal, BigFloat, BigFloatArb”Status: implementado
Core-blessed (usam + - * /), mas vivem fora do escopo global. BigInt cresce sem estourar; Decimal/
Decimal[P, S] base-10 exato (dinheiro); BigFloat[M] com M bits de mantissa (MPFR); BigFloatArb[M] com
cota de erro rigorosa (sem <, usa .cmp() ArbOrdering com Indeterminate). Literal toma o tipo do binding.
Makoto
a: Decimal = 0.10b: Decimal = 0.20io.println(a + b) // 0.30 exatox: BigInt = 123456789012345678901234567890io.println(x * x)Rust
// crates: num-bigint, rust_decimal, rug (MPFR)Diferença: Sem bignum na std do Rust.
Caracteres: byte, codepoint, grapheme (char)
Section titled “Caracteres: byte, codepoint, grapheme (char)”Status: implementado
Literal 'w' não tem tipo fixo: vira byte, codepoint (u32) ou grapheme pelo contexto. char é alias de
grapheme (o caractere visual, pode ter vários codepoints). Sem contexto, qualifique: 'w'.codepoint.
Makoto
k: codepoint = 'w'b: byte = 'w'g: char = 'é'Rust
let k: char = 'w'; // scalar Unicode (≈ codepoint)Diferença:
chardo Rustcodepointda Makoto.
Literais de duração (5s, 300ms)
Section titled “Literais de duração (5s, 300ms)”Status: implementado
Duration é builtin (u64 nanossegundos, nunca negativo). Unidades no lexer, sem import: ns ms s m h d.
Usado em timeout(...) e @supervisor(window: 10s). timeout(_) = sem prazo explícito.
Makoto
t := 300msreply := ch <-> req timeout(5s) catch |e| { return }Rust
let t = Duration::from_millis(300);Literais de tamanho (16mb, 4kib)
Section titled “Literais de tamanho (16mb, 4kib)”Status: implementado
Notação de lexer convertida pra usize em bytes: 512b, 4kb/4kib, 16mb/16mib, 2gb/2gib,
8tb/8tib (base-10 vs base-2). Usado em @limit(16mb).
Makoto
n := 4kib // 4096@limit(16mb)spawn worker(data)Rust
let n: usize = 4 * 1024;Alias de tipo: alias
Section titled “Alias de tipo: alias”Status: implementado
alias UUID = u8 é sinônimo transparente (intercambiável). Tipo distinto é um decl envolvendo o valor.
Makoto
alias UUID = u8u: UUID = 7io.println(u + 1)Rust
type Uuid = u8;Comparação e Ordering
Section titled “Comparação e Ordering”Status: implementado
Tipos numéricos/strings têm ordem total (< > <= >= == != retornam bool). Ordering { Less; Equal; Greater }
é builtin: resultado de .cmp(). Um tipo do usuário vira ordenável implementando cmp() -> Ordering
(interface Comparable).
Makoto
o := 5.cmp(3) // GreaterRust
let o = 5.cmp(&3); // Ordering::GreaterUnit, Never e o Top ausente
Section titled “Unit, Never e o Top ausente”Status: implementado
decl Marker {} é o struct vazio (Unit: um único valor). decl Never { _ } é o enum sem variantes (inabitado).
O “Top” (Any, interface{}, Object) não é escrevível: use um bound de interface.
noreturn é o tipo de retorno de quem nunca volta (panic, loop infinito).
Makoto
decl Marker {}decl Never { _ }fn die(msg: string) -> noreturn { runtime.panic(msg) }Rust
struct Marker;enum Never {}fn die(m: &str) -> ! { panic!("{m}") }Controle de fluxo
Section titled “Controle de fluxo”Um loop para todos os laços, um match para switch/match/select, if é só statement (valor por condição
= ternário). loop e match podem ser valor, entregando por return explícito.
if / else (statement)
Section titled “if / else (statement)”Status: implementado
if não produz valor. Condição é bool estrito (sem truthiness).
Makoto
if n > 0 { io.println("pos") } else if n < 0 { io.println("neg") } else { io.println("zero") }Rust
if n > 0 {...} else if n < 0 {...} else {...}Diferença: Em Rust
ifé expressão; em Makoto não (use o ternário).
Ternário c ? a : b
Section titled “Ternário c ? a : b”Status: implementado
Escolha entre dois valores. Três ou mais = match.
Makoto
icon := liked ? "♥" : "♡"Rust
let icon = if liked { "♥" } else { "♡" };Diferença: Rust não tem
?:.
loop unificado (infinito / while / for-each / range)
Section titled “loop unificado (infinito / while / for-each / range)”Status: implementado
O cabeçalho decide: nada = infinito; expressão booleana = while; x in xs = for-each (coleção, gerador,
range); 0..8 exclusivo, 0..=8 inclusivo.
Makoto
loop { break }loop n > 0 { n -= 1 }loop x in xs { io.println(x) }loop i in 1..=3 { io.println(i) }Rust
loop { break }while n > 0 { n -= 1 }for x in &xs { println!("{x}") }for i in 1..=3 {}Loop estilo C: loop var i := 0; ...
Section titled “Loop estilo C: loop var i := 0; ...”Status: implementado
Três cláusulas separadas por ;, parênteses opcionais. O contador precisa ser var.
Makoto
loop var i := 0; i < 3; i = i + 1 { io.println(i) }Rust
for i in 0..3 { println!("{i}") }Diferença: Rust não tem for estilo C.
break/continue com label
Section titled “break/continue com label”Status: implementado
Label estilo Go antes do loop (outer:). break/continue nunca carregam valor.
Makoto
outer: loop i in 0..3 { loop j in 0..3 { if j == 1 { continue outer }; if i == 2 { break outer } }}Rust
'outer: for i in 0..3 { for j in 0..3 { continue 'outer; } }loop como valor
Section titled “loop como valor”Status: implementado
x := loop { ... return v }: o return dentro do loop-valor entrega ao binding (não sai da função). Todo
caminho de saída precisa entregar.
Makoto
x := loop { return 42 }Rust
let x = loop { break 42; };Diferença: Rust usa
break valor; Makoto usareturn(ebreaknão carrega valor).
match (switch/match) com or-pattern e _
Section titled “match (switch/match) com or-pattern e _”Status: implementado
Sem fallthrough; | junta casos; _ é o default e é obrigatório em domínio aberto (int, string) e
proibido em domínio fechado (enum) — adicionar variante quebra o build até tratar. Braços separados por
quebra de linha (ou ; na mesma linha).
Makoto
match code { 200 | 201 | 204 => ok() 404 | 410 => gone() _ => other()}Rust
match code { 200 | 201 | 204 => ok(), 404 | 410 => gone(), _ => other() }match como valor e return match
Section titled “match como valor e return match”Status: implementado
Braço de match não entrega sozinho. Em posição de valor, cada braço faz return v (entrega ao binding);
return match x { A => B } distribui o return pros braços.
Makoto
v := match n { 0 => return "zero"; _ => return "outro" }fn area(s: Shape) -> float { return match s { Circle(r) => 3.14 * r * r; Rect(w, h) => w * h }}Rust
let v = match n { 0 => "zero", _ => "outro" };defer (limpeza LIFO, args congelados)
Section titled “defer (limpeza LIFO, args congelados)”Status: implementado
Roda na saída do escopo (inclusive return antecipado), vários em LIFO. Os argumentos são avaliados no
defer; só a chamada é adiada. Também roda quando o processo morre e quando um gerador é abandonado.
Makoto
@mm(none)fn work() { let buf: [*]u8 = mm.alloc(64) defer free(buf) // roda em qualquer caminho de saída var n := 0 defer io.println("n={{n}}") // imprime n=0 (arg congelado) n = 9}Rust
let f = File::open("cfg")?; // Drop fecha no fim do escopoDiferença: Rust usa RAII/Drop; Makoto usa
deferexplícito.
runtime.panic (aborta o processo)
Section titled “runtime.panic (aborta o processo)”Status: implementado
panic não é global: vive em runtime (use runtime). Mata só o processo Makoto atual — os defers
rodam, a memória do processo é liberada e o erro chega ao supervisor.
Makoto
use runtimeif bad { runtime.panic("estado inválido") }Rust
panic!("estado inválido");Diferença: Panic do Rust derruba a thread; o da Makoto derruba o processo leve e é supervisionável.
Funções, lambdas, métodos e despacho
Section titled “Funções, lambdas, métodos e despacho”Método = função com receiver explícito; x.f() é UFCS (= f(x)). Lambda só existe como argumento (não
dá pra guardar em variável). Função aninhada é escopo, não closure. “Overload” é explícito (function set /
match T).
Declaração de função
Section titled “Declaração de função”Status: implementado
fn nome(p: T, ...) -> R { ... }. Sem retorno = sem ->. Modificadores ortogonais: pub, comptime,
unsafe, @generator.
Makoto
fn add(a: int, b: int) -> int { return a + b }pub fn greet(name: string) { io.println("oi {{name}}") }Rust
fn add(a: i32, b: i32) -> i32 { a + b }Parâmetros com valor padrão
Section titled “Parâmetros com valor padrão”Status: implementado
fn f(x: int = 5): chamar f() usa o 5. É assim que “mesmo nome, aridade diferente” funciona.
Makoto
fn f(x: int = 5) -> int { return x * 2 }f() // 10f(1) // 2Rust
// sem default: Option<T> ou builderDiferença: Rust não tem parâmetro default.
Variádico ...T (e spread na chamada)
Section titled “Variádico ...T (e spread na chamada)”Status: parcial
Parâmetro xs: ...int junta N argumentos (vira slice). Heterogêneo e tipado: ...Display. O spread no
ponto de chamada f(...xs) está desenhado mas ainda não implementado (parser rejeita ... na chamada).
Makoto
fn sum(xs: ...int) -> int { var t := 0; loop x in xs { t += x }; return t }sum(1, 2, 3, 4)fn log(items: ...Display) { loop i in items { io.println(i.to_string()) } }Rust
fn sum(xs: &[i32]) -> i32 { xs.iter().sum() } // sem variádico (macro)Diferença: Rust só tem variádico em macros.
Funções não se guardam em variável
Section titled “Funções não se guardam em variável”Status: não existe (por design)
f := fn(x) => ... e g: fn(int) -> int = double são erro. Quem guarda função é um parâmetro de tipo
função (callback). Quer reusar? Dê nome (fn nome) e chame pelo nome.
Makoto
fn apply(f: fn(int) -> int, x: int) -> int { return f(x) }fn double(n: int) -> int { return n * 2 }apply(double, 5)Rust
let double = |n: i32| n * 2;Lambda (só como argumento)
Section titled “Lambda (só como argumento)”Status: implementado
Duas formas, ambas só em posição de argumento: fn(x) => expr (o => entrega) e fn(x) { ...; return v }.
Tipo de retorno inferido ou anotado (fn(x: int) -> int => x + 1). Captura por referência o escopo.
Makoto
use collectionsys := collections.map(xs, fn(n: int) => n * 10) // xs: List[int]zs := collections.filter(xs, fn(n: int) { return n % 2 == 0 })Rust
let ys: Vec<_> = xs.iter().map(|n| n * 10).collect();Função aninhada (escopo, não captura)
Section titled “Função aninhada (escopo, não captura)”Status: implementado
fn a() { fn b() {...} } é válido: b só é visível dentro de a, pode recursar e ser chamada antes da
declaração — mas não enxerga os locais de a (tudo chega por parâmetro). Capturar escopo é exclusivo do lambda.
Makoto
fn outer(x: int) -> int { fn helper(v: int) -> int { return v + 1 } return helper(x)}Rust
fn outer(x: i32) -> i32 { fn helper(v: i32) -> i32 { v + 1 } helper(x) }Diferença: Idêntico ao Rust: fn interna não captura.
Métodos: receiver explícito + UFCS
Section titled “Métodos: receiver explícito + UFCS”Status: implementado
fn (p: Point) norm() (por valor) ou fn (p: *Point) move() (por ponteiro — acesso a campo exige deref
explícito (*p).x, não há auto-deref). p.norm() é UFCS: igual a norm(p). Pipeline com . — não existe |>.
Makoto
decl Counter { n: int }fn (c: Counter) get() -> int { return c.n }fn (c: *Counter) inc() { (*c).n = (*c).n + 1 } // ponteiro NÃO auto-deref: `*` explícitoRust
impl Counter { fn get(&self) -> i32 { self.n } fn inc(&mut self) { self.n += 1 } }Função associada / construtor: fn (T) new()
Section titled “Função associada / construtor: fn (T) new()”Status: implementado
Receiver sem nome = associada ao tipo (sem instância). Chamada por ponto: Arena.new(). Não existe static.
Makoto
decl Stack { n: int }fn (Stack) new() -> Stack { return Stack{n: 0} }s := Stack.new()Rust
impl Stack { fn new() -> Self { Stack { n: 0 } } }Function set: fn nome = { f1, f2 }
Section titled “Function set: fn nome = { f1, f2 }”Status: implementado
Junta funções existentes num roteador por tipo, resolvido em compile-time (análogo ao proc{} do Odin). As
assinaturas precisam ser distinguíveis; ambiguidade é erro na declaração do set.
Makoto
fn bool_s(b: bool) -> string { return b ? "true" : "false" }fn int_s(i: int) -> string { return "int" }fn to_s = { bool_s, int_s }to_s(true); to_s(3)Rust
trait ToS { fn to_s(&self) -> String; } impl ToS for bool {...} impl ToS for i32 {...}match T: despacho por tipo em genérico
Section titled “match T: despacho por tipo em genérico”Status: implementado
Genérico com whitelist [T: {bool, int}] + match T no corpo: o compilador escolhe o braço e monomorfiza.
Domínio fechado _ proibido.
Makoto
fn show[T: {bool, int}](x: T) -> string { match T { bool => return "B" int => return "I" }}Rust
// trait com impl por tipo, ou TypeId em runtimeTipos: struct, enum, interface, union
Section titled “Tipos: struct, enum, interface, union”Uma palavra só, decl, declara os três contratos — o corpo diz qual: campos nome: T = struct (produto),
variantes Nome(...) = enum (soma), assinaturas fn ... = interface (comportamento). union fica de fora
(é outro sistema: reinterpretação de bytes, unsafe).
Struct: decl Nome { campo: T; ... }
Section titled “Struct: decl Nome { campo: T; ... }”Status: implementado
Produto. Campos separados por ; (ou quebra de linha). Literal nomeado P{x: 1; y: 2} ou posicional
P{1; 2} — atenção: ; entre campos (vírgula é pra grupo de valores, como []int{1, 2}).
O compilador reordena os campos pra reduzir padding (use @repr(c) pra layout fiel ao C).
Makoto
decl Vec2 { x: float; y: float }a := Vec2{1.0; 2.0}b := Vec2{x: 1.0; y: 2.0}Rust
struct Vec2 { x: f64, y: f64 }let b = Vec2 { x: 1.0, y: 2.0 };Diferença: Rust também reordena campos por padrão (
#[repr(C)]@repr(c)).
Visibilidade de campo: privado por padrão, pub expõe
Section titled “Visibilidade de campo: privado por padrão, pub expõe”Status: implementado
Código colabora (fn sem marca = visível no módulo); dados se protegem: campo sem marca é privado ao módulo,
pub campo expõe pra outros módulos. Campo nunca tem var (mutabilidade é do lugar).
Makoto
decl User { id: int // privado ao módulo pub email: string // visível fora do módulo}Rust
pub struct User { id: i64, pub email: String }Enum / tipo soma com payload
Section titled “Enum / tipo soma com payload”Status: implementado
Cada variante pode carregar dados (como uma struct atrás de um rótulo). Construção com o tipo: Shape.Circle(2.0);
no padrão o Shape. some. Exaustividade: match sobre enum cobre todas as variantes e proíbe _.
Makoto
decl Shape { Circle(float); Rect(float, float) }s: Shape = Shape.Circle(2.0)match s { Circle(r) => io.println(r) Rect(w, h) => io.println(w * h)}Rust
enum Shape { Circle(f64), Rect(f64, f64) }match s { Shape::Circle(r) => ..., Shape::Rect(w, h) => ... }Conjunto de variantes: Shape{Circle}
Section titled “Conjunto de variantes: Shape{Circle}”Status: implementado
Em posição de tipo, Nome{Variantes} é um subconjunto do enum: Shape{Circle} = “um Shape, garantidamente
Circle”. Construir uma variante dá o tipo mais estreito, que alarga sozinho pro superconjunto. É a mesma
mecânica dos conjuntos de erro error{...}.
Makoto
fn only_circle(s: Shape{Circle}) -> float { return 1.0 }c := Shape.Circle(1.0) // tipo: Shape{Circle}only_circle(c) // ok; um Shape.Rect não compilariaRust
// sem equivalente direto (variantes não são tipos); use struct por varianteDiferença: Rust não tem refinamento de enum por variante.
Literal de enum com ponto: .Red
Section titled “Literal de enum com ponto: .Red”Status: implementado
Onde o tipo esperado é um enum conhecido, .Variante basta (inferido). Sem contexto, escreva completo (Color.Red).
Makoto
decl Color { Red; Green; Blue }fn paint(c: Color) -> int { return match c { Red => 1; Green => 2; Blue => 3 } }paint(.Green)let c: Color = .BlueRust
paint(Color::Green);Interface estrutural (satisfação implícita)
Section titled “Interface estrutural (satisfação implícita)”Status: implementado
decl Nome { fn metodo() -> T } — só assinaturas, sem métodos default. Um tipo satisfaz implementando os
métodos (estilo Go), sem implements. Comportamento derivado = função genérica livre sobre o bound.
Makoto
decl Shower { fn show() -> string }decl P { x: int }fn (p: P) show() -> string { return "P{{p.x}}" }fn greet[T + Shower](v: T) -> string { return "hi " + v.show() }Rust
trait Shower { fn show(&self) -> String; }impl Shower for P { fn show(&self) -> String {...} }Valor de interface (vtable) × bound genérico (monomorfização)
Section titled “Valor de interface (vtable) × bound genérico (monomorfização)”Status: implementado
d: Shower = p guarda um fat pointer {dados, vtable} (tipo concreto apagado; lista heterogênea possível).
[T + Shower] monomorfiza (chamada direta, sem vtable). Método genérico só via bound, nunca via valor de interface.
Makoto
d: Shower = p // dinâmico (vtable)xs := []Display{p, 3, "s"} // heterogêneo e tipadogreet(p) // estático (monomorfizado)Rust
let d: &dyn Shower = &p; // vtablefn greet<T: Shower>(v: T) // monomorfizadoDiferença: Igual a
dyn Trait×T: Trait.
Sem Any: ‘qualquer tipo’ é um bound
Section titled “Sem Any: ‘qualquer tipo’ é um bound”Status: não existe (por design)
Não existe Any/Object/interface{}/void* seguro. Você diz “qualquer tipo que faça X”: ...Display,
[T + Serializable]. Interpolação {{x}} exige Display em compile-time.
Makoto
fn log(xs: ...Display) { loop x in xs { io.println(x.to_string()) } }log(1, "a", true)Rust
fn log(xs: &[&dyn Display]) {...}Interface Display (to_string)
Section titled “Interface Display (to_string)”Status: implementado
decl Display { fn to_string() -> string }. Primitivos já implementam; implemente pro seu tipo e ganhe
interpolação {{p}} e qualquer genérico sobre Display.
Makoto
fn (p: P) to_string() -> string { return "P({{p.x}})" }io.println("{{p}}") // P(1)Rust
impl fmt::Display for P { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "P({})", self.x) } }Composição: @embeds(Tipo, methods|fields|all)
Section titled “Composição: @embeds(Tipo, methods|fields|all)”Status: implementado
Sem herança: embutir um tipo e declarar o que é promovido. Promoção é transitiva, passa por campo ponteiro, e
colisão entre irmãos é erro (resolve pelo caminho nomeado). b as A extrai a parte embutida por valor
(tipagem nominal: B não vira A sozinho).
Makoto
decl A { v: int }@embeds(A, all)decl B { a: A; w: int }fn take(value: A) -> int { return value.v }b := B{a: A{v: 7}; w: 1}io.println(b.v) // campo promovidotake(b as A) // extração explícitaRust
struct B { a: A, w: i32 } // sem promoção (Deref é o hack)Diferença: Go-like embedding, mas a promoção é anunciada no decorator.
Tipos recursivos exigem indireção
Section titled “Tipos recursivos exigem indireção”Status: implementado
Struct/enum não pode conter a si mesmo por valor (tamanho infinito). A auto-referência passa por *T, [*]T,
[]T ou Optional[*T]. O compilador reporta o ciclo, nunca faz boxing escondido.
Makoto
decl Node { val: int; next: Optional[*Node] }Rust
struct Node { val: i32, next: Option<Box<Node>> }Tuplas (a, b)
Section titled “Tuplas (a, b)”Status: implementado
Grupo posicional de valores: (10, "x"). Desestruturação x, y := t. Retorno sem erro com vários valores.
Makoto
t := (1, "x")x, y := tRust
let t = (1, "x"); let (x, y) = t;unsafe union (reinterpretação de bytes)
Section titled “unsafe union (reinterpretação de bytes)”Status: implementado
Campos sobrepostos, sem discriminante (quem sabe o campo ativo é você). Construir e ler são operações unsafe
com assume "motivo" (ou assert provável). Union com ponteiro não pode viver sob @mm(gc).
Makoto
unsafe union FloatBits { f: f32; bits: u32 }fb := unsafe FloatBits{f: 1.5} assume "construir a union pra ler os bits"y: u32 = unsafe fb.bits assume "reinterpretação intencional float->bits"Rust
union FloatBits { f: f32, bits: u32 }let y = unsafe { FloatBits { f: 1.5 }.bits }; // ou f32::to_bitsLayout: @repr(c) e @repr(packed)
Section titled “Layout: @repr(c) e @repr(packed)”Status: implementado
@repr(c) desliga a reordenação e dá layout fiel ao C (obrigatório pra struct que cruza a fronteira FFI).
@repr(packed) = bit-packed estilo Zig (cada campo com sua largura exata; u3+u5 num byte). Layout
observável em compile-time: mem.size_of[T](), mem.align_of[T](), mem.offset_of[T]("campo").
Makoto
@repr(c)decl CPoint { x: i32; y: i32 }@repr(packed)decl Flags { a: u3; b: u5 }Rust
#[repr(C)] struct CPoint { x: i32, y: i32 }// bitfields: crate bitfield/modular-bitfieldEnum de flags: @flags
Section titled “Enum de flags: @flags”Status: planejado
Desenhado (§17): @flags decl Perms { Read; Write; Exec } tornaria o enum int-backed e bitmaskável
(Read | Write). Ainda não implementado (o checker rejeita o decorator). Hoje: constantes inteiras ou enum
vindo do C (que já é bitmaskável).
Makoto
@flags decl Perms { Read; Write; Exec } // planejadop := Perms.Read | Perms.WriteRust
bitflags! { struct Perms: u8 { const READ = 1; const WRITE = 2; } }Coleções e Optional
Section titled “Coleções e Optional”Coleções são valores: bind copia (o compilador elide a cópia quando ninguém escreve). Compartilhar é pedido
explicitamente: view xs[a..b] ou ponteiro. []T é slice (ponteiro + tamanho), [N]T array fixo, [*]T
buffer cru sem tamanho. List[T] é builtin; Map/Set e as HOFs vêm de collections.
Slice e array: []T, [N]T, [_]T
Section titled “Slice e array: []T, [N]T, [_]T”Status: implementado
Literal com vírgula: []int{1, 2, 3}, [3]int{...}, [_]int{...} (tamanho inferido). Indexação checada
(panic fora dos limites). Tamanho é o campo .len.
Makoto
xs := []int{1, 2, 3}arr := [3]int{1, 2, 3}inf := [_]int{4, 5}io.println(xs.len)Rust
let xs: Vec<i32> = vec![1, 2, 3];let arr: [i32; 3] = [1, 2, 3];Semântica de valor (bind copia)
Section titled “Semântica de valor (bind copia)”Status: implementado
var ys := xs é cópia independente; escrever em ys não muda xs. Construir struct com slice copia pro campo.
Makoto
xs := []int{1, 2, 3}var ys := xsys[0] = 9 // xs continua [1, 2, 3]Rust
let mut ys = xs.clone(); // `let ys = xs` MOVEDiferença: Rust move; Makoto copia (semântica), sem invalidar
xs.
View xs[a..b] × cópia xs[a:b]
Section titled “View xs[a..b] × cópia xs[a:b]”Status: implementado
.. = view O(1) (compartilha o backing; escrever pela view escreve no original). : = cópia O(n), independente.
Mesma regra pra array, slice e string (em string com unidade: .bytes/.chars/.codepoints).
Makoto
var xs := []int{1, 2, 3}var v := xs[..] // view do slice inteirov[0] = 9 // xs agora é [9, 2, 3]c := xs[0:2] // cópia de 2 elementosRust
let v = &mut xs[..]; // viewlet c = xs[0..2].to_vec(); // cópiaList[T] (vetor dinâmico)
Section titled “List[T] (vetor dinâmico)”Status: implementado
Builtin: List.new(), push, len(), at(i) (garante; panic fora) e get(i) ( Optional[T]). Nomes
honestos: get = “talvez”, at = “eu garanto”.
Makoto
var xs: List[int] = List.new()xs.push(1)xs.push(2)io.println(xs.at(0))io.println(xs.get(9).or(-1))Rust
let mut xs = Vec::new(); xs.push(1); xs[0]; xs.get(9).copied().unwrap_or(-1);Map e Set (collections)
Section titled “Map e Set (collections)”Status: parcial
Implementado como tipos + funções livres em collections: map_new/map_put/map_get/map_has/map_size,
set_new/set_add/set_has/set_size. Qualquer tipo com == serve de chave (struct incluída, sem declarar nada).
Implementação atual: lista associativa (busca linear) — o hash-map do design, com hash derivado por reflexão,
e a API em métodos (Map[K, V](), m.set(k, v), get_or_insert) ainda não existem.
Makoto
use collectionsvar m := collections.map_new[string, int]()collections.map_put(mut m, "a", 1)v := collections.map_get(m, "a").or(0)var s := collections.set_new[int]()collections.set_add(mut s, 3)Rust
let mut m = HashMap::new(); m.insert("a", 1); *m.get("a").unwrap_or(&0);Funções de alta ordem (collections.map/filter/reduce...)
Section titled “Funções de alta ordem (collections.map/filter/reduce...)”Status: implementado
Sobre List[T]: map filter reduce any all count reverse find find_index index_of contains take drop concat flat_map. Use a forma qualificada collections.filter(xs, f) (o UFCS xs.filter(f) ainda falha no lower).
Makoto
use collectionsys := collections.map(xs, fn(n: int) => n * 10)evens := collections.filter(xs, fn(n: int) => n % 2 == 0)total := collections.reduce(xs, 0, fn(a: int, n: int) => a + n)Rust
xs.iter().map(|n| n * 10).collect::<Vec<_>>();xs.iter().fold(0, |a, n| a + n);Optional[T]: some/none, .or, .or_panic
Section titled “Optional[T]: some/none, .or, .or_panic”Status: implementado
Ausência tipada. Abrir com match (some(v) => / none =>), com default .or(d), ou abortar honestamente
.or_panic() (o nome diz a consequência — não existe unwrap). .is_present() testa.
Makoto
match find(xs, 9) { some(i) => io.println(i) none => io.println("ausente")}v := find(xs, 5).or(-1)w := find(xs, 5).or_panic()Rust
let v = find(&xs, 5).unwrap_or(-1);let w = find(&xs, 5).unwrap(); // ↔ or_panicStrings e texto
Section titled “Strings e texto”string é sempre UTF-8 válido (tipo builtin composto, não é []byte). Não existe s[i]: a unidade é
sempre explícita (.bytes, .codepoints, .chars = grafemas). Comparação por operador é byte-a-byte;
semântica/locale é por método.
string = UTF-8 válido
Section titled “string = UTF-8 válido”Status: implementado
Bytes possivelmente inválidos são []byte. Validação na fronteira (string.from[u8](bytes) Result).
Makoto
s := "héllo"io.println(s.bytes.len) // 6 bytesio.println(s.codepoints.len) // 5 codepointsRust
let s: String = "héllo".into(); // UTF-8 garantidoDiferença: Modelo do Rust: String UTF-8 + Vec
pra bytes.
Acesso por unidade: .bytes[i], .chars[i], .codepoints[i]
Section titled “Acesso por unidade: .bytes[i], .chars[i], .codepoints[i]”Status: implementado
s[2] e s[2..6] puros são erro de compilação (“sobre O QUÊ?”). Cada acesso tem forma curta e método:
s.bytes[2]/s.byte_at(2), s.chars[2..6]/s.chars_view(2, 6), s.codepoints[2:6]/s.codepoints_copy(2, 6).
Makoto
s := "héllo"io.println(s.chars[1]) // é (grafema)b := s.bytes[0] // bytev := s.bytes[0..2] // view por bytesRust
s.chars().nth(1) // grafema: crate unicode-segmentations.as_bytes()[0]Interpolação {{ expr }}
Section titled “Interpolação {{ expr }}”Status: implementado
Qualquer expressão dentro de {{ }}; formata via Display (checado em compile-time). \{{ é chave literal.
Formatação por método ({{x.pad(8)}}, {{pi.fixed(2)}}, {{n.hex()}}) está desenhada, ainda não implementada.
Makoto
name := "Ana"io.println("Olá {{name}}, {{ 2 + 3 }} itens")Rust
println!("Olá {name}, {} itens", 2 + 3);Comparação: operadores = bytes; .equals(_, .semantic) = Unicode
Section titled “Comparação: operadores = bytes; .equals(_, .semantic) = Unicode”Status: implementado
==/< são igualdade/ordem lexicográfica de bytes. Igualdade com normalização Unicode: a.equals(b, .semantic).
Ordem alfabética por idioma: locale.Collator.new(.pt).order(a, b) Ordering. Não existe ===.
Makoto
a == b // bytes"é".equals("é", .semantic) // NFCuse localec := locale.Collator.new(.en)o := c.order("a", "b") // Ordering.LessRust
a == b // normalização/colação: crates unicode-normalization, icuMutação e crescimento (var + append)
Section titled “Mutação e crescimento (var + append)”Status: implementado
Não há StringBuilder: var s + s.append(...) cresce dobrando o buffer. Concatenação com +.
Makoto
var s := "ab"s.append("c")t := s + "!"Rust
let mut s = String::from("ab"); s.push_str("c");Conversões de encoding: to[u16], string.from[u16]
Section titled “Conversões de encoding: to[u16], string.from[u16]”Status: implementado
UTF-8 sai direto de .bytes. Outros encodings são explícitos e alocam: s.to[u16]() []byte (LE, sem BOM);
string.from[u8|u16|u32](bytes) Result. bytes.as_string_unchecked() pula a validação (unsafe).
BOM/endianness: pacote unicode (swap16, with_bom16, strip_bom16…).
Makoto
e := "hé".to[u16]()back := string.from[u16](e) catch |err| { return }Rust
s.encode_utf16().collect::<Vec<u16>>();String::from_utf16(&v)?;Ponte com C: .to_cstring() / from_cstring
Section titled “Ponte com C: .to_cstring() / from_cstring”Status: parcial
s.to_cstring() dá bytes NUL-terminados (implementado). A volta, from_cstring(p) (char* Result[string]
validando UTF-8), está desenhada mas não implementada. Hoje um char* vindo do C chega cru (*c.<mod>.char).
Makoto
v := unsafe c.stdlib.getenv("HOME".to_cstring()) assume "getenv com string válida"match v { some(p) => io.println("definida"); none => io.println("ausente") }Rust
CString::new("HOME")?; CStr::from_ptr(p).to_str()?Utilitários: pacote strings
Section titled “Utilitários: pacote strings”Status: implementado
is_empty byte_len starts_with ends_with index_of contains repeat split join trim_start trim_end trim replace to_upper to_lower (escritos em Makoto, na stdlib).
Makoto
use stringsparts := strings.split("a,b,c", ",")s := strings.join(parts, "-")u := strings.to_upper("abc")Rust
"a,b,c".split(',').collect::<Vec<_>>().join("-"); "abc".to_uppercase();Erros: Result, conjuntos de erro, catch
Section titled “Erros: Result, conjuntos de erro, catch”Dois eixos separados: Result[T, E] (deu certo ou não) e error{...} (quais falhas existem — conjunto fechado
e estrutural). Trailers: catch |e| trata como unidade; match |e| ramifica por variante; match sobre o
Result inteiro é o fork que recupera com valor.
Result[T, E] com Ok/Err
Section titled “Result[T, E] com Ok/Err”Status: implementado
Enum builtin { Ok(T), Err(E) }. Sucesso é sempre explícito (return Ok(x)); erro propagado ganha açúcar
(return e embrulha em Err).
Makoto
fn risky(n: int) -> Result[int, error{Neg, Zero}] { if n < 0 { return Err(error.Neg) } if n == 0 { return Err(error.Zero) } return Ok(n)}Rust
fn risky(n: i32) -> Result<i32, RiskErr> {...}Fork: match sobre o Result (recuperar com valor)
Section titled “Fork: match sobre o Result (recuperar com valor)”Status: implementado
Cobre Ok e cada variante de erro, exaustivo (sem _ em conjunto fechado). É o lugar de recuperar com
um fallback.
Makoto
match load(p) { Ok(v) => io.println(v) Err(error.NotFound) => io.println("não achou") Err(error.Denied) => io.println("negado")}Rust
match load(p) { Ok(v) => ..., Err(E::NotFound) => ..., Err(E::Denied) => ... }Trailer catch |e| (propagar / abortar)
Section titled “Trailer catch |e| (propagar / abortar)”Status: implementado
O Ok vai pro binding; o bloco trata o erro e precisa escapar (return, break, panic) quando há
binding. return e propaga (vira Err(e)). Não existe ?.
Makoto
v := risky(x) catch |e| { return e } // propagaw := risky(x) catch |e| { runtime.panic("falhou") } // abortaRust
let v = risky(x)?;let w = risky(x).expect("falhou");Trailer match |e| (ramificar por variante)
Section titled “Trailer match |e| (ramificar por variante)”Status: implementado
Açúcar de catch |e| { match e {...} }. Só vale com união (≥2 variantes); cada braço escapa quando há binding.
Makoto
v := risky(n) match |e| { Neg => { io.println("negativo"); return e } Zero => return e}Rust
let v = match risky(n) { Ok(v) => v, Err(E::Neg) => { ...; return Err(E::Neg) }, Err(e) => return Err(e) };Conjuntos de erro error{...} e união com spread
Section titled “Conjuntos de erro error{...} e união com spread”Status: implementado
Estruturais e fechados; o estreito cabe no largo sem conversão. Compor = unir: error{open..., parse...} junta
as variantes dos retornos de open e parse.
Makoto
fn load(p: string) -> Result[int, error{open..., parse...}] { f := open(p) catch |e| { return e } d := parse(f) catch |e| { return e } return Ok(d)}Rust
enum LoadErr { Open(OpenErr), Parse(ParseErr) } // + impl FromDiferença: Em Rust você escreve o enum e os
From; em Makoto a união é estrutural.
Sucesso sem valor: retornar só o conjunto de erro
Section titled “Sucesso sem valor: retornar só o conjunto de erro”Status: implementado
Não há unit/(): fn send() -> error{Full} — sucesso é a ausência de erro (return puro). Descartar
sem tratar é erro; trate com catch (o sucesso segue pra próxima linha).
Makoto
fn send_it(ok: bool) -> error{Full} { if !ok { return error.Full } return}send_it(false) catch |e| { io.println("cheio") }Rust
fn send_it(ok: bool) -> Result<(), Full> {...}Erro aberto error (FFI / terceiros)
Section titled “Erro aberto error (FFI / terceiros)”Status: implementado
Sem {...}, error é o domínio aberto (conjunto desconhecido): não-exaustivo, exige _/Err(e) genérico.
Makoto
fn ext() -> Result[int, error] { return Err(error.Whatever) }match ext() { Ok(v) => io.println(v); Err(e) => io.println("erro externo") }Rust
fn ext() -> Result<i32, Box<dyn Error>>error{}: nunca falha
Section titled “error{}: nunca falha”Status: implementado
Conjunto vazio (inabitado): Result[T, error{}] diz na assinatura que o Err é impossível — útil pra
implementar interface falível com um tipo que nunca erra.
Makoto
fn never_fails() -> Result[int, error{}] { return Ok(1) }Rust
fn never_fails() -> Result<i32, Infallible> { Ok(1) }Variante de erro com payload inline
Section titled “Variante de erro com payload inline”Status: planejado
Desenhado (§7): error{HTTP500, PortError(int)} inline, aberto com match como qualquer variante. Ainda não
parseia. Hoje: declare um decl enum com payload e use-o como E do Result.
Makoto
fn port(n: int) -> Result[int, error{HTTP500, PortError(int)}] // planejadoRust
enum E { Http500, PortError(i32) }Memória: gerenciamento sem cor, ponteiros, ownership
Section titled “Memória: gerenciamento sem cor, ponteiros, ownership”Cada alocação carrega o seu MemoryManager (a estratégia viaja com o objeto), então gc, arena, none e
c se misturam sem colorir assinaturas. @mm(...) define a estratégia padrão para novas alocações num
contexto (processo, função, bloco, objeto). Sem borrow checker: um gate local decidível pega leak/double-free/
use-after-free no caminho manual, e @consume/@transfer movem ownership.
Padrão: @mm(gc) (coletor)
Section titled “Padrão: @mm(gc) (coletor)”Status: implementado
Sem anotação, tudo é gerenciado pelo coletor (mark-sweep, raízes precisas via shadow stack). free(p) sob gc
é no-op silencioso — o mesmo código roda em qualquer estratégia.
Makoto
xs := []int{1, 2, 3} // coletado automaticamenteRust
let xs = vec![1, 2, 3]; // ownership + Drop, sem GCDiferença: Rust não tem GC; Makoto tem GC por padrão e manual opt-in.
Manual: @mm(none) + mm.alloc + free
Section titled “Manual: @mm(none) + mm.alloc + free”Status: implementado
Estilo C, opt-in. mm.alloc(n) (ou mm.alloc(n, align)) dá um [*]T; free(p) devolve pelo manager do
próprio objeto (o compilador preenche o tamanho).
Makoto
use mm@mm(none)fn main() { let a: [*]int = mm.alloc(4) a[0] = 7 free(a)}Rust
let a = unsafe { std::alloc::alloc(layout) as *mut i32 };unsafe { std::alloc::dealloc(a as *mut u8, layout) };Gate local de ownership (leak/double-free/UAF = erro de compilação)
Section titled “Gate local de ownership (leak/double-free/UAF = erro de compilação)”Status: implementado
Onde alocação e free estão na mesma função e o ponteiro não escapa, o tempo de vida é decidível: esquecer o
free num caminho, liberar duas vezes ou usar depois de liberar não compila. Sem anotação nenhuma.
Makoto
@mm(none)fn main() { let a: [*]int = mm.alloc(4) io.println("sem free")}// erro: memory leak: "a" was allocated with @mm(none) and is never freed on some pathRust
// o borrow checker pega isso (e muito mais, com anotações de lifetime)Diferença: Makoto prova só o fragmento decidível, sem lifetimes; o resto vira aviso (analyzer) ou é sua responsabilidade.
Arena: @mm(arena) { ... }
Section titled “Arena: @mm(arena) { ... }”Status: implementado
Bloco com uma arena anônima nova: nasce no {, morre no } (liberação em massa). Sub-arena = bloco aninhado.
Operações de região ancoradas num ponteiro: mm.used(p), mm.reset(p), mm.release(p), mm.resize(p, n).
Makoto
use mmfn handle() { @mm(arena) { let a: [*]int = mm.alloc(8) io.println(mm.used(a)) } // tudo liberado aqui}Rust
let bump = bumpalo::Bump::new(); let x = bump.alloc(5); // crate bumpalo@mm por processo / função / objeto
Section titled “@mm por processo / função / objeto”Status: implementado
Mesma diretiva em escopos diferentes; objeto recebido mantém o manager de origem (“colorless”).
Makoto
@mm(arena)spawn worker(data) // tudo que worker aloca vai pra uma arena@mm(none)fn low_level() { ... }Rust
// allocator por coleção: Vec::new_in(alloc) (nightly allocator_api)Diferença: Makoto NÃO passa allocator por parâmetro: isso colore a função.
@consume: parâmetro linear (ownership atravessa a função)
Section titled “@consume: parâmetro linear (ownership atravessa a função)”Status: implementado
Opt-in: o chamado passa a ser dono e precisa liberar/retornar/transferir exatamente uma vez; o chamador não pode usar depois. Guardar num campo de struct que escapa também quita a obrigação.
Makoto
fn free_it(p: [*]int @consume) { free(p) }Rust
fn free_it(p: Box<[i32]>) { drop(p) } // move por valorDiferença: Equivale a receber por valor (move) em Rust.
@transfer: mover entre processos (invalida a origem)
Section titled “@transfer: mover entre processos (invalida a origem)”Status: implementado
Valor enviado por canal/spawn é copiado profundamente por padrão (heaps isolados). @transfer move em vez
de copiar: o objeto viaja com o seu manager e a variável de origem fica inválida (usar depois = erro).
Ponteiro cru *T nunca cruza processo.
Makoto
r := Rec{amount: 84; tags: []int{1, 2}}spawn worker(out, r @transfer)ch <- b @transferRust
tx.send(v).unwrap(); // move; v não pode mais ser usadoDiferença: Igual ao move do
senddo Rust, mas checado só no ponto de transferência.
@promote(mm, shallow|deep): copiar pra outro manager
Section titled “@promote(mm, shallow|deep): copiar pra outro manager”Status: implementado
Otimização (não correção): copia o objeto para outro MemoryManager — ex.: tirar da arena antes de enviá-la, ou
trazer memória do C pro GC (p @promote(gc)). deep copia o subgrafo; a origem não é liberada sozinha.
Makoto
mine := p @promote(gc) // repatria memória vinda do Cchan <- result @promote(gc) @transferRust
let mine = src.clone();@limit(16mb): teto de memória por processo
Section titled “@limit(16mb): teto de memória por processo”Status: implementado
Estourar o teto mata o processo; o motivo (OOM) chega ao supervisor no catch |e|. OOM é evento de processo,
não erro em cada push (isso colore tudo). Alocação com orçamento recuperável = descer pro mm.alloc.
Makoto
@limit(1kb)spawn worker() catch |e| { io.println("morreu por limite") }Rust
// sem equivalente por tarefaPonteiros *T: &lugar, *p, sem &local
Section titled “Ponteiros *T: &lugar, *p, sem &local”Status: implementado
Um só tipo de ponteiro (sem *const/*mut). Nasce de & sobre um lugar mutável com identidade no heap — um
var de módulo, ou o elemento/apontado de outro ponteiro (esse não exige var no handle: o binding do ponteiro e a
mutabilidade do apontado são independentes) — nunca de um local na pilha (pra mutar local use mut). Deref
explícito em toda leitura e escrita (sem auto-deref). Ponteiro nunca é nulo: Optional[*T]. Sem aritmética com +.
Makoto
let a: [*]int = mm.alloc(4)p := &a[1]*p = 5decl Node { val: int; next: Optional[*Node] }Rust
let p: &mut i32 = &mut a[1];*p = 5;Pointer-to-many [*]T (buffer cru)
Section titled “Pointer-to-many [*]T (buffer cru)”Status: implementado
N elementos contíguos que você possui, sem tamanho. Indexar é unchecked (unsafe); items[a..b] vira
slice (onde o tamanho nasce); avançar a base é p.offset(k) — nunca p + k.
Makoto
b := a.offset(1)x := unsafe b[0] assume "b aponta dentro do bloco de 4"Rust
let b = unsafe { a.add(1) };let x = unsafe { *b };mut: passagem com write-back (inout)
Section titled “mut: passagem com write-back (inout)”Status: implementado
Argumento vai por valor por padrão. mut no parâmetro e na chamada = a função altera a variável do
chamador (que precisa ser var). Não é tipo, não escapa, é exclusivo (f(mut x, mut x) = erro); implementado
por referência (zero cópia).
Makoto
fn bump(v: mut int) { v = v + 1 }var x := 5bump(mut x) // x == 6Rust
fn bump(v: &mut i32) { *v += 1 }bump(&mut x);Autofree: free inserido após o último uso
Section titled “Autofree: free inserido após o último uso”Status: implementado
Onde a análise prova tempo de vida linear, o compilador insere o free logo após o último uso (tira o
objeto do mundo do GC). Nunca muda o que o programa computa. Ligado por padrão; --no-autofree desliga;
--autofree=default|conservative|optimized.
Makoto
// nada a escrever: é otimização do compilador// mk build --autofree=optimized app.mkoRust
// Drop determinístico no fim do escopoAnalisador de memória consultivo (mk analyze, -Wmm, --strict-mm)
Section titled “Analisador de memória consultivo (mk analyze, -Wmm, --strict-mm)”Status: implementado
Um borrow-checker que você consulta: provado-fatal = erro; provável = aviso; poderia-ser-melhor = dica.
--strict-mm promove avisos a erro; falso positivo é silenciado no local com assume "motivo".
Makoto
// mk analyze app.mko -> relatório (erros, avisos, dicas)// mk build -Wmm app.mko -> liga os avisos// strict-mm true (no mk.project)Rust
// o borrow checker é obrigatórioDiferença: Rust: obrigatório em todo programa. Makoto: opt-in, e só o provado bloqueia.
Opções do coletor (--gc-roots, --gc-collector, --gc=wasmgc)
Section titled “Opções do coletor (--gc-roots, --gc-collector, --gc=wasmgc)”Status: parcial
--gc-roots=shadow (padrão, portável) ou stackmaps (LLVM statepoints, só host); --gc-collector=moving
(compactador; objetos fixos com @pin); --gc=wasmgc usa o GC do host WebAssembly (trilha em andamento).
Makoto
// mk build --gc-roots=stackmaps --gc-collector=moving app.mkoRust
Concorrência: processos e canais
Section titled “Concorrência: processos e canais”A unidade de concorrência é o processo (estilo Erlang, compilado nativamente): heap privado, ciclo de vida
gerenciado, falha contida. Comunicação só por canal tipado (não há memória compartilhada nem @shared).
IO sem cor: o scheduler suspende só o processo que bloqueou.
spawn f(x) (fire-and-forget)
Section titled “spawn f(x) (fire-and-forget)”Status: implementado
Cria um processo leve. Não devolve handle (referência é por registro, @register). Argumentos cruzam por cópia
profunda (ou @transfer).
Makoto
spawn worker(data)Rust
std::thread::spawn(move || worker(data)); // ou tokio::spawnCanal: Channel.new() / Channel.new(64)
Section titled “Canal: Channel.new() / Channel.new(64)”Status: implementado
Primitiva do core (sem literal, sem zero-value). Sem argumento = rendezvous (send bloqueia até o receive); com capacidade = buffer (send em buffer cheio bloqueia = backpressure). Buffer pertence ao runtime.
Makoto
c: Channel[int] = Channel.new()b: Channel[int] = Channel.new(64)Rust
let (tx, rx) = std::sync::mpsc::sync_channel::<i32>(0); // 64 = bufferDireção no tipo: Channel[<-A, ->B]
Section titled “Direção no tipo: Channel[<-A, ->B]”Status: implementado
Perspectiva de quem segura: <-T = “eu envio T”, ->T = “eu recebo T”. Channel[T] = simétrico. O compilador
checa que os dois lados são complementares no spawn.
Makoto
fn worker(ch: Channel[<-string, ->int]) { // envia string, recebe int v := -> ch catch |e| { return } ch <- "got {{v}}"}Rust
fn worker(rx: Receiver<i32>, tx: Sender<String>)Enviar ch <- v / receber -> ch
Section titled “Enviar ch <- v / receber -> ch”Status: implementado
Receber devolve Result[T, error{ProcessDown}] (os remetentes morreram). Não existe close: o fim do
stream é a morte do processo que alimenta. Entrega FIFO por canal.
Makoto
ch <- 42v := -> ch catch |e| { return }loop { x := -> ch catch |e| { break } // produtor morreu: fim process(x)}Rust
tx.send(42)?;let v = rx.recv()?; // Err quando todos os Sender caemRequest-reply síncrono <-> com timeout obrigatório
Section titled “Request-reply síncrono <-> com timeout obrigatório”Status: implementado
ch <-> req timeout(5s) envia e espera a resposta. Sem timeout(...) = erro de compilação (dois <-> cruzados
seriam deadlock garantido). Falha: Timeout | ProcessDown.
Makoto
reply := c <-> 21 timeout(1s) catch |e| { return }Rust
rx.recv_timeout(Duration::from_secs(1))?;Select = match { ... } timeout(d) { ... }
Section titled “Select = match { ... } timeout(d) { ... }”Status: implementado
match sem sujeito espera vários canais. Trailer timeout(d) roda se nada chega; timeout(0) = poll não
bloqueante. Com vários prontos, os braços se revezam (sem inanição e reprodutível — nem sempre o 1º, nem aleatório).
Makoto
match { v := -> a => io.println(v) v := -> b => io.println(v)} timeout(100ms) { io.println("nada chegou")}Rust
crossbeam::select! { recv(a) -> v => ..., recv(b) -> v => ..., default(Duration::from_millis(100)) => ... }Referência por nome: @register("nome") e grupos
Section titled “Referência por nome: @register("nome") e grupos”Status: implementado
spawn não devolve pid. Para inspecionar depois, registre: nome único que sobrevive a restart; ou
@register("handlers", group) pra endereçar por índice. Lookup: runtime.process("nome") ( Optional),
runtime.group("nome"), runtime.self().
Makoto
@register("db_writer")spawn db_writer(conn)match runtime.process("db_writer") { some(h) => io.println("vivo"); none => io.println("sumiu") }Rust
// sem registro nativo (ractor/actix têm)Scheduler: --scheduler=throughput|deterministic
Section titled “Scheduler: --scheduler=throughput|deterministic”Status: implementado
Mesma API nos dois. throughput (padrão): work-stealing multi-core. deterministic: time-slicing rígido
estilo BEAM (tempo real). Ambos preemptam (reduções em back-edges de loop + monitor de tempo). FFI bloqueante é
absorvido no work-stealing; no determinístico, isole num processo.
Makoto
// mk build --scheduler=deterministic app.mkoRust
// tokio multi-thread / current_threadSem memória compartilhada entre processos
Section titled “Sem memória compartilhada entre processos”Status: não existe (por design)
Não há @shared, mutex nem atomics de usuário. Dado compartilhado = um processo dono que serve pedidos via canal
(padrão “actor”). Dentro de um processo não há aliasing concorrente, por isso não há borrow checker.
Makoto
fn counter(ch: Channel[->int, <-int]) { var n := 0 loop { d := -> ch catch |e| { return }; n += d; ch <- n }}Rust
let n = Arc::new(Mutex::new(0)); *n.lock().unwrap() += d;Supervisão e resiliência (let it crash)
Section titled “Supervisão e resiliência (let it crash)”Modelo BEAM sem expor link/monitor crus: a árvore de supervisão é a estrutura do código. O catch |e|
de um spawn é o monitor; morte de processo é um valor de erro. Ordem da morte: defers libera o @mm
entrega o erro.
Monitor = spawn f() catch |e| { ... }
Section titled “Monitor = spawn f() catch |e| { ... }”Status: implementado
O filho morre, quem fez spawn não morre e recebe o motivo. Sem anotação = one_for_one sem limite de restart.
Makoto
spawn worker(data) catch |e| { io.println("worker caiu") }Rust
let h = thread::spawn(f); if h.join().is_err() { /* panicou */ }Motivo da morte: match |e| no spawn
Section titled “Motivo da morte: match |e| no spawn”Status: implementado
O erro é estruturado: Crashed(msg), Normal, Timeout, Overrun, OutOfMemory…
Makoto
spawn crasher() match |e| { Crashed(msg) => io.println("crashed: {{msg}}") error.Normal | error.Timeout | error.Overrun | error.OutOfMemory => io.println("outro")}Rust
match h.join() { Ok(_) => normal, Err(payload) => crashed }@supervisor(max_restarts: N, window: T)
Section titled “@supervisor(max_restarts: N, window: T)”Status: implementado
Sem catch: o runtime reinicia sozinho, limitado pela janela. Com catch: você decide
(runtime.supervisor.restart() / escalate(); não fazer nada = fica morto). Restart re-invoca com os mesmos
argumentos — canal passado como argumento reconecta; nome registrado sobrevive.
Makoto
@supervisor(max_restarts: 3, window: 10s)spawn worker(data) catch |e| { runtime.supervisor.restart()}Rust
// sem std (crates ractor/bastion)Estratégias: one_for_one / one_for_all / rest_for_one
Section titled “Estratégias: one_for_one / one_for_all / rest_for_one”Status: implementado
spawn individual = one_for_one; bloco spawn { a(); b() } catch |e| {...} = one_for_all (vivem e morrem
juntos; o catch externo dispara quando o grupo esgota); @supervisor(strategy: rest_for_one) = reinicia quem
veio depois (pipeline).
Makoto
spawn { stage_a() stage_b()} catch |e| { io.println("grupo esgotado") }
@supervisor(strategy: rest_for_one, max_restarts: 5, window: 30s)spawn { db_connection(); db_writer(); db_reader() } catch |e| { ... }Rust
// manual / cratesÁrvore por aninhamento
Section titled “Árvore por aninhamento”Status: implementado
Qualquer processo que faz spawn é supervisor implícito dos filhos. escalate() mata o atual e entrega o erro a
quem o supervisiona; em main sem supervisor, o programa sai com o erro.
Makoto
spawn http_server(cfg) catch |e| { runtime.supervisor.restart() }Rust
// manualCanais pela rede (net)
Section titled “Canais pela rede (net)”Não há “tipo canal de rede”: net.connect[T]/net.accept[T] devolvem um Channel[T] comum. A física aparece
nos tipos que já existem: o payload precisa ser Serializable (derivado automaticamente por reflexão) e
estabelecer a conexão devolve Result.
Servidor: net.listen + net.accept[T]
Section titled “Servidor: net.listen + net.accept[T]”Status: implementado
Cada conexão aceita vira um Channel[T]; trate cada cliente como um processo.
Makoto
use netlistener := net.listen("0.0.0.0:8080") catch |e| { return }loop { conn := net.accept[Msg](listener) catch |e| { continue } spawn handle(conn)}Rust
let l = TcpListener::bind("0.0.0.0:8080")?; for s in l.incoming() { /* serde */ }Cliente: net.connect[T]
Section titled “Cliente: net.connect[T]”Status: implementado
Mesma API de canal: <-, ->, select, timeout.
Makoto
chan := net.connect[Msg]("127.0.0.1:8080") catch |e| { return }chan <- requestreply := -> chan catch |e| { return }Rust
let mut s = TcpStream::connect("127.0.0.1:8080")?;Generics, comptime e reflexão
Section titled “Generics, comptime e reflexão”Generics e metaprogramação são a mesma coisa por baixo: comptime. [...] = parâmetros de compile-time
(tipos e valores), (...) = de runtime. type é um valor de compile-time; decl Box[T] é açúcar pra uma
comptime fn que devolve type. Restrições declaradas: + Interface (capacidade) e : {tipos} (whitelist).
Função genérica fn f[T](x: T)
Section titled “Função genérica fn f[T](x: T)”Status: implementado
Monomorfizada por tipo concreto. Argumentos de tipo inferidos onde os valores fixam; senão explícitos. Tudo ou
nada: não existe aplicação parcial (f[i32](p) pra f[T, V] é erro).
Makoto
fn id[T](x: T) -> T { return x }id(5) // T = int (inferido)id[int](5) // explícitoRust
fn id<T>(x: T) -> T { x }Tipo genérico decl Box[T] { v: T }
Section titled “Tipo genérico decl Box[T] { v: T }”Status: implementado
Box{5} infere T pelo campo; Box[int]{5} explícito. Enum genérico: Tree[int].Leaf(5) ou Tree.Leaf(5).
Makoto
decl Box[T] { v: T }b := Box{5}c := Box[int]{6}Rust
struct Box<T> { v: T }let b = Box { v: 5 };Restrição por capacidade: [T + Display]
Section titled “Restrição por capacidade: [T + Display]”Status: implementado
“T precisa saber fazer X”. Checado na fronteira da instanciação com erro limpo (não 3 camadas dentro).
Makoto
fn show[T + Display](x: T) -> string { return x.to_string() }Rust
fn show<T: Display>(x: T) -> String { x.to_string() }Whitelist de tipos: [T: {i8, u16}]
Section titled “Whitelist de tipos: [T: {i8, u16}]”Status: implementado
“T só pode ser um destes” — de primeira classe (Rust precisaria de sealed trait). Combina com interface:
[T: {i8, u16} + Display]. Forma plana: nada de |, & ou parênteses.
Makoto
fn small[T: {i8, u16}](x: T) -> T { return x }fn both[T: {i8, u16} + Display](x: T) -> string { return x.to_string() }Rust
trait Small: sealed::Sealed {} impl Small for i8 {} impl Small for u16 {}Parâmetros de valor: [N: int]
Section titled “Parâmetros de valor: [N: int]”Status: implementado
[...] aceita valores de compile-time, não só tipos: cada valor distinto gera uma especialização
(monomorfização por valor). Base da camada prática de tipos dependentes (Vec[T, n]).
Makoto
fn zeros[N: int]() -> int { return N * 2 }zeros[4]() // especializado com N = 4Rust
fn zeros<const N: usize>() -> usize { N * 2 }zeros::<4>();Famílias de tipo / HKT raso: C[_]
Section titled “Famílias de tipo / HKT raso: C[_]”Status: implementado
Abstrair sobre o construtor não aplicado (List, Box) pra escrever map/collect uma vez. Cortes: só aridade
1 e 2, unificação de 1ª ordem, sem busca implícita de instância (resolvido por interface + monomorfização).
Makoto
decl Mappable[C[_]] { fn fmap[A, B](xs: C[A], f: fn(A) -> B) -> C[B] }decl Box[T] { v: T }fn (b: Box[T]) fmap[U](f: fn(T) -> U) -> Box[U] { return Box[U]{f(b.v)} }fn pmap[C[_] + Mappable, A, B](c: C[A], f: fn(A) -> B) -> C[B] { return c.fmap(f) }Rust
// sem HKT (GATs cobrem parte)Diferença: Rust não tem HKT; Makoto tem uma versão limitada de propósito.
comptime fn, const e comptime var
Section titled “comptime fn, const e comptime var”Status: implementado
comptime fn roda inteira no compilador e some do binário (só em contexto de compile-time: const, [...],
outra comptime fn). comptime var = rascunho mutável da compilação. Comptime é puro: sem IO, spawn, canais.
Makoto
comptime fn repeat(s: string, n: int) -> string { comptime var acc := "" comptime var i := 0 loop { if i == n { break }; acc = acc + s; i = i + 1 } return acc}const R := repeat("ab", 3) // "ababab" embutido no binárioRust
const fn repeat(...) // const fn é mais restritacomptime if / comptime match / comptime loop / comptime {}
Section titled “comptime if / comptime match / comptime loop / comptime {}”Status: implementado
Resolvidos pelo compilador: só o ramo escolhido é emitido (compilação condicional). comptime loop desenrola
(necessário quando o tipo varia por iteração, ex.: campos de struct).
Makoto
use archcomptime if arch.current == .x86_64 { io.println("x86") }Rust
#[cfg(target_arch = "x86_64")]println!("x86");Reflexão de compile-time: reflect(T)
Section titled “Reflexão de compile-time: reflect(T)”Status: implementado
use compiler.{reflect, fail}. reflect(T).kind é um enum (Struct(s), Enum(e), Slice(elem), Array(elem, n),
Optional(inner), Tuple(parts), Int(i), Float(f), Bool, String, Ptr…). s.fields com f.name,
f.type, f.get(v); construir com reflect(T).construct(fn(f) => ...); fail("msg") aborta a compilação.
Reflexão de tipo, não de valor em runtime (não existe any).
Makoto
use compiler.{reflect, fail}fn count_fields[T](v: T) -> int { var n := 0 comptime match reflect(T).kind { Struct(s) => { comptime loop f in s.fields { n += 1 } } _ => { } } return n}Rust
// proc-macro (derive) em compile-timeAuto-derivação: serialize / deserialize / hash
Section titled “Auto-derivação: serialize / deserialize / hash”Status: implementado
Sem macro nem anotação: a stdlib encoding deriva pela reflexão. Struct vira chave de Map/Set e payload de
rede (Serializable) sem declarar nada. Tipo com invariante pode definir validate (checado no decode).
Makoto
use encoding.{serialize, deserialize, hash}decl User { id: u64; name: string; admin: bool }u := User{id: 42; name: "ana"; admin: true}bytes := serialize(u)back := deserialize[User](bytes) catch |e| { return }let h: u64 = hash(u)Rust
#[derive(Serialize, Deserialize, Hash)] struct User {...} // serdeGeradores (sequências preguiçosas)
Section titled “Geradores (sequências preguiçosas)”@generator fn + yield = máquina de estados stackless no mesmo processo (zero alocação por item). Pra
suspensão arbitrária entre chamadas, use um processo. Abandonar um gerador roda os defers dele.
@generator fn + yield Generator[T]
Section titled “@generator fn + yield Generator[T]”Status: implementado
yield só no nível do próprio gerador (não dentro de uma função que ele chama).
Makoto
@generator fn nat() -> int { var i := 0 loop { yield i; i += 1 }}loop n in nat().take(5) { io.println(n) }Rust
// gen blocks ainda instáveis; impl Iterator manual ou (0..).take(5)Pipeline preguiçoso: .filter .map .take .collect
Section titled “Pipeline preguiçoso: .filter .map .take .collect”Status: parcial
Cada estágio é outro Generator (sem alocação intermediária). Implementados: map, filter, take,
collect. Desenhados e ainda não implementados: drop, reduce, enumerate, zip.
Makoto
fn even(n: int) -> bool { return n % 2 == 0 }loop v in nat().filter(even).map(fn(n: int) => n * 10).take(3) { io.println(v) }xs := upto(4).collect()Rust
(0..).filter(|n| n % 2 == 0).map(|n| n * 10).take(3)Contratos (DbC), assert/assume e unsafe
Section titled “Contratos (DbC), assert/assume e unsafe”Dois níveis de contenção: o contrato na fronteira da função (@requires/@ensures) e o abate da
operação perigosa no corpo (assert (cond) checa; assume "motivo" confia). unsafe não é região de permissão:
é uma dívida que alguém paga explicitamente.
Pré-condição @requires(cond)
Section titled “Pré-condição @requires(cond)”Status: implementado
Checada em cada chamada: estática onde prova, runtime (panic determinístico) onde não. Empilha (uma por linha; o compilador diz qual falhou). Predicado reutilizável = função booleana comum. Pré-condição checável torna a função segura.
Makoto
decl Buffer { data: []u8 }@requires(i < buf.data.len)@requires(i >= 0)fn at(buf: Buffer, i: int) -> u8 { return buf.data[i] }Rust
debug_assert!(i < buf.len()); // contratos: crate `contracts`Pós-condição: -> T == expr (terse) ou @ensures
Section titled “Pós-condição: -> T == expr (terse) ou @ensures”Status: implementado
Terse no tipo de retorno (retorno único, o tipo é o slot). @ensures(...) pra várias condições/retornos; quando
um tipo se repete, o retorno ganha nome (rótulo do contrato, não variável).
Makoto
fn at(buf: Buffer, i: int) -> u8 == buf.data[i] { return buf.data[i] }@ensures(lo < hi)fn split(a: int) -> (lo: int, hi: int) { return (a, a + 1) }Rust
// crate contracts: #[ensures(ret > 0)]assert (cond) e assert (cond) else return E
Section titled “assert (cond) e assert (cond) else return E”Status: implementado
Checa em runtime; falso = panic (troca UB por falha limpa). Com else return devolve um erro/valor em vez de panic.
Makoto
assert (x > 0)fn parse(v: int) -> int { assert (v >= 0) else return -1 return v}Rust
assert!(x > 0);unsafe (expr, bloco, fn, union) + handlers
Section titled “unsafe (expr, bloco, fn, union) + handlers”Status: implementado
Toda operação perigosa (deref cru, [*]T[i], union, chamada C, fn unsafe) exige um handler: assert (cond)
(checa), assume "motivo" (confia; motivo não-vazio e conhecido em compile-time) ou passar adiante
(unsafe fn). Função que abateu tudo no corpo é fn normal — o contágio para na primeira camada que prova.
Makoto
unsafe fn raw_at(xs: []int, i: int) -> int { return xs[i] }v := unsafe raw_at(xs, 1) assert (1 < xs.len)y := unsafe fb.bits assume "reinterpretação intencional"r := unsafe { a := raw_at(xs, 0) return a * 2} assume "índice 0 existe"Rust
unsafe fn raw_at(xs: &[i32], i: usize) -> i32 { *xs.get_unchecked(i) }let v = unsafe { raw_at(&xs, 1) };Diferença: Rust:
unsafe {}é região de permissão silenciosa. Makoto: cada operação declara como o perigo foi neutralizado.
Predicado comptime fn em contrato (prova em compile-time)
Section titled “Predicado comptime fn em contrato (prova em compile-time)”Status: implementado
Se os argumentos são conhecidos em compile-time, o predicado dobra: verdadeiro = contrato quitado sem custo;
falso = erro de compilação. Com argumento de runtime vira checagem normal. Transforma assume em assert provado.
Makoto
comptime fn same_size[A, B]() -> bool { return mem.size_of[A]() == mem.size_of[B]() }y: u32 = unsafe fb.bits assert (same_size[f32, u32]()) // provado pelo compiladorRust
const _: () = assert!(size_of::<f32>() == size_of::<u32>());FFI com C (e C++)
Section titled “FFI com C (e C++)”@cimport("header.h") roda o clang embutido, traduz o header inteiro e cria o submódulo c.<basename>.
Tudo do C fica qualificado sob c. (inclusive tipos fundamentais, nomeados por ABI: c.systemv.int). Toda
chamada C é unsafe e precisa de handler. Toolchain hermético (musl embutido, mk cc = cc drop-in).
@cimport("x.h") c.x.*
Section titled “@cimport("x.h") c.x.*”Status: parcial
Funções, structs, typedefs, enums, macros objeto (literais) e função (via shim). as renomeia o submódulo.
Lacunas atuais (registradas): não há como passar -D (ex.: _GNU_SOURCE — some unshare/clone/CLONE_*
do musl); macros com expressão ((1<<18)) são descartadas; errno ainda não é exposto.
Makoto
@cimport("stdlib.h")n := unsafe c.stdlib.atol("100kg".to_cstring()) assume "string válida"Rust
// bindgen gera bindings; chamadas em unsafe {}Tipos C qualificados por ABI (c.systemv.int)
Section titled “Tipos C qualificados por ABI (c.systemv.int)”Status: implementado
Não existe c_int solto: a largura depende da psABI. c.systemv.long = 64 bits, c.win64.long = 32;
c.gcc.int/c.clang.int seguem o alvo. Nomes de um token só (unsigned long use u64).
Makoto
let n: c.systemv.int = 5sz: c.systemv.size_t = 16Rust
let n: std::ffi::c_int = 5;NULL Optional
Section titled “NULL Optional”Status: implementado
Ponteiro C que pode ser nulo chega como Optional[*...] e é aberto com match. char* chega cru (não vira
string sozinho).
Makoto
v := unsafe c.stdlib.getenv("HOME".to_cstring()) assume "getenv"match v { some(p) => io.println("definida"); none => io.println("ausente") }Rust
let p = unsafe { libc::getenv(k.as_ptr()) }; if !p.is_null() {...}Struct para o C: @repr(c)
Section titled “Struct para o C: @repr(c)”Status: implementado
Suas structs são reordenadas; pra cruzar a fronteira use @repr(c) (ordem e padding do C). Structs vindas do
@cimport já são @repr(c). Enums C são int-compatíveis (aceitam |/&).
Makoto
@repr(c)decl Point { x: c.systemv.int; y: c.systemv.int }Rust
#[repr(C)] struct Point { x: c_int, y: c_int }Memória do C: @mm(c), @promote, @pin/@unpin
Section titled “Memória do C: @mm(c), @promote, @pin/@unpin”Status: implementado
Memória que o C te dá fica sob @mm(c) (liberada com o free do C). @promote(gc) repatria pro seu GC.
Objeto @mm(gc) passado ao C exige @pin (senão erro de compilação); @unpin é responsabilidade sua.
Makoto
p: *u8 @mm(c) = (unsafe c.stdlib.malloc(16) assume "c").or_panic()mine := p @promote(gc)@pin head// ... C segura o ponteiro ...@unpin headRust
let p = unsafe { libc::malloc(16) }; unsafe { libc::free(p) };@callback (função sua chamada pelo C)
Section titled “@callback (função sua chamada pelo C)”Status: implementado
Ganha ABI C pra ser passada como ponteiro de função. Corpo não aloca heap nem faz IO (não há processo na thread
crua do C); pode enviar em canal e fazer spawn (roteado pelo runtime).
Makoto
@cimport("stdlib.h")@callback fn bye() {}_ := unsafe c.stdlib.atexit(bye) assume "registrar handler de saída"Rust
extern "C" fn bye() {}unsafe { libc::atexit(bye) };@extern (exportar pro C / declarar símbolo)
Section titled “@extern (exportar pro C / declarar símbolo)”Status: implementado
Expõe função com ABI C e nome cru (sem mangling), sem genéricos. Também declara símbolo externo sem corpo.
mk build --emit=lib gera .a + header C; mk export cmake|ninja gera a cola de build.
Makoto
@extern fn my_library_init() -> i32 { return 0 }@extern fn puts(s: [*]u8) -> i32Rust
#[no_mangle] pub extern "C" fn my_library_init() -> i32 { 0 }mem.reinterpret[T, V](p) (void* T*)
Section titled “mem.reinterpret[T, V](p) (void* T*)”Status: planejado
Primitiva unsafe dedicada pra reinterpretar ponteiro (ex.: comparador do qsort lendo void*). Desenhada,
ainda não implementada (reinterpret não é membro de mem).
Makoto
ia := unsafe mem.reinterpret[c.gcc.void, i32](a) assume "a aponta um i32" // planejadoRust
let ia = a as *const i32;Dependências C: cdeps no mk.project + mk cc
Section titled “Dependências C: cdeps no mk.project + mk cc”Status: implementado
O .c entra no build (estilo Zig). Libs C declaradas com versão e checksum (sem auto-descoberta de -l).
mk cc é um cc/c++ drop-in com o sysroot musl embutido.
Makoto
cdeps { sqlite3 3.45.0 sha256:...}// shell: mk cc -c shim.c -o shim.oRust
# Cargo.toml + build.rs (cc crate)Assembly inline
Section titled “Assembly inline”Fronteira mais baixa, sempre unsafe. Duas formas: função-asm (corpo inteiro em asm, params/retorno
ligados a registradores) e bloco inline (instruções no meio do código, com in/out/clobber). Dialeto
nativo de cada arquitetura (Intel/AT&T, ARM, RISC-V, WASM); seleção por arquitetura com comptime if.
Função-asm: @asm(x86, att) unsafe fn ...
Section titled “Função-asm: @asm(x86, att) unsafe fn ...”Status: implementado
Parâmetro e retorno ligados a registrador no próprio parâmetro. Clobbers implícitos (é fronteira de chamada).
Makoto
@asm(x86, att)unsafe fn mask(a: u64 @asm(in, rax)) -> u64 @asm(out, rax) { andq $255, %rax}r := unsafe mask(x) assume "x86_64"Rust
unsafe { asm!("and {0}, 255", inout(reg) a); }Bloco inline com in/out/clobber
Section titled “Bloco inline com in/out/clobber”Status: implementado
Decoradores empilhados acima do unsafe {}; operando pode ser local, campo ou elemento. Clobbers explícitos
(correção, não verbosidade). É statement, não expressão.
Makoto
var lo: u32 = 0var hi: u32 = 0@asm(x86, intel)@asm(out, rax) lo@asm(out, rdx) hi@asm(clobber, "memory")unsafe { rdtsc}Rust
unsafe { asm!("rdtsc", out("eax") lo, out("edx") hi); }Registrador escolhido pelo compilador: xreg
Section titled “Registrador escolhido pelo compilador: xreg”Status: implementado
@asm(in, xreg): o alocador escolhe; no corpo use o nome simbólico do operando.
Makoto
@asm(x86, intel)unsafe fn double(value: u32 @asm(in, xreg)) -> u32 @asm(out, xreg) { add value, value}Rust
asm!("add {0:e}, {0:e}", inout(reg) v);Seleção por arquitetura + WASM
Section titled “Seleção por arquitetura + WASM”Status: implementado
use arch + comptime if arch.current == .x86_64 { ... }; asm da arquitetura errada não compila (sem fallback
mágico). WASM não tem registradores: liga pelos próprios parâmetros.
Makoto
use archfn timestamp() -> u64 { comptime if arch.current == .x86_64 { return unsafe rdtsc_x86() assume "rdtsc disponível" } return portable_clock()}@asm(wasm, wasm)unsafe fn add(a: u32, b: u32) -> u32 { local.get a local.get b i32.add}Rust
#[cfg(target_arch = "x86_64")] fn timestamp() -> u64 {...}Módulos, visibilidade e pacotes
Section titled “Módulos, visibilidade e pacotes”use é a única palavra de import. Nada é “fundamental demais pra importar”: print vive em io, panic em
runtime. Visibilidade em 3 níveis com o meio como padrão. mk.project = binário/composição; mk.mod = módulo
(lib) com as próprias deps; mk.sum = checksums. Resolução MVS (estilo Go), sem ciclos.
use (módulo, seletivo, alias, inline)
Section titled “use (módulo, seletivo, alias, inline)”Status: implementado
use json (acesso qualificado), use strings.{to_upper} (traz nomes), use x as y, e use math.abs(-5.0)
inline (chamada avulsa sem poluir o escopo). Prefixo decide a origem: nome puro = stdlib; app/auth = local;
github.com/... = externo (precisa estar em deps).
Makoto
use iouse strings.{to_upper}use collections as colx := use math.abs(-5.0)Rust
use std::io;use crate::strings::to_upper;use std::collections as col;Visibilidade: priv / (nada) / pub
Section titled “Visibilidade: priv / (nada) / pub”Status: implementado
priv fn = só o arquivo; sem marca = o módulo inteiro (padrão); pub fn = exportado. Campo de struct inverte o
padrão (privado; pub expõe). Capitalização não importa (convenção: snake_case valores, PascalCase tipos).
Makoto
priv fn helper() {}fn internal() {}pub fn api() {}Rust
fn helper() {} pub(crate) fn internal() {} pub fn api() {}mk.project, mk.mod, mk.sum
Section titled “mk.project, mk.mod, mk.sum”Status: implementado
Projeto (bin) declara versão da linguagem, deps, cdeps, tasks e strict-mm. Cada mk.mod é um módulo-lib
com as próprias deps. Pasta sem mk.mod funde no módulo pai. Import absoluto (nunca ../).
Makoto
package appmakoto 0.1.0deps { github.com/acme/jwt 1.4.0}[tasks.greet]run = echo hisubprocess = [echo]
// auth/mk.modmodule authRust
# Cargo.toml + Cargo.lockMódulo de arquivo único (module Nome)
Section titled “Módulo de arquivo único (module Nome)”Status: parcial
Um arquivo transportável com module Nome no topo; o design prevê deps com versão e checksum embutidos no
cabeçalho (implementação parcial).
Makoto
module quickscriptdeps { github.com/foo/json 0.9.0 sha256:a1b2c3...}Rust
// cargo script (-Zscript)Sem imports circulares; init topológica
Section titled “Sem imports circulares; init topológica”Status: implementado
ABA é erro. Inicialização top-level é const (comptime) ou pura; efeito de boot vai num processo explícito.
Makoto
// a.mko: use app/b + b.mko: use app/a -> erro de compilaçãoRust
// ciclos entre módulos de um crate são permitidos; entre crates nãoMétodos em tipo de outro módulo (órfãos)
Section titled “Métodos em tipo de outro módulo (órfãos)”Status: implementado
Pode definir fn (p: other.Point) norm(). Se dois módulos visíveis definem o mesmo, a chamada sem qualificar é
ambígua (erro) — qualifique (A.norm(p)).
Makoto
pub fn (p: geo.Point) norm() -> float { ... }Rust
// regra órfã proíbe impl de trait externo em tipo externoDiferença: Makoto permite e resolve por visibilidade.
Observabilidade do runtime
Section titled “Observabilidade do runtime”Pull e desligado por padrão: você só paga pelo que liga. Introspecção (snapshot barato) e trace (stream de eventos num ring buffer). Telemetria de aplicação (métricas, spans, logs) é stdlib.
Introspecção: runtime.processes/process/group/channels/stats/self
Section titled “Introspecção: runtime.processes/process/group/channels/stats/self”Status: implementado
Handle de processo expõe nome, estado, memória por estratégia de @mm, restarts, supervisor/filhos…
Makoto
use runtimeprocs := runtime.processes()stats := runtime.stats()n := runtime.self().restarts()Rust
// sem equivalente na std (tokio-console)Trace de eventos: runtime.trace(alvo, eventos, ...)
Section titled “Trace de eventos: runtime.trace(alvo, eventos, ...)”Status: implementado
Eventos .send .receive .spawn .exit .schedule_in .gc .restart .channel_block… Consumo via .buffer (padrão),
.channel, .callback(fn), .endpoint; opções sample: N, duration, inherit.
Makoto
t := runtime.trace(.all, .spawn)spawn worker()io.println(t.count())Rust
// tracing crate (instrumentação manual)Telemetria: metrics, tracing, log
Section titled “Telemetria: metrics, tracing, log”Status: implementado
Stdlib escrita em Makoto: metrics.counter/gauge/increment/read, tracing.span/set_attribute/finish,
log.debug/info/warn/error.
Makoto
use metricsuse tracinguse logc := metrics.counter("requests")s := tracing.span("http.request")log.info("worker started")Rust
log::info!("worker started"); // crates log/tracing/metricsTestes, benchmarks, property-based e fuzzing
Section titled “Testes, benchmarks, property-based e fuzzing”mk test roda @test/@bench; @property e @fuzz são da extensão de testes (--ext=test ou no
mk.project). Matriz de execução por arquivo com @testmode(ordem, estado).
@test fn + assert
Section titled “@test fn + assert”Status: implementado
Teste solto ao lado do código, ou arquivos x_test.mko / pasta tests/. Cada teste roda como processo.
Makoto
@test fn soma() { assert (1 + 1 == 2) }Rust
#[test] fn soma() { assert_eq!(1 + 1, 2); }@bench fn
Section titled “@bench fn”Status: implementado
Estilo Go: esforço configurável, comparador estatístico fixo (anti p-hacking).
Makoto
@bench fn bench_spin() { spin(50) }Rust
#[bench] fn spin(b: &mut Bencher) {...} // nightly; criterion na prática@testmode(ordem, estado) (matriz)
Section titled “@testmode(ordem, estado) (matriz)”Status: implementado
order: sequential | parallel; state: isolated | shared. Listas geram o produto cartesiano, com relatório por
combinação (acha bugs de transição e races em estado lógico/externo).
Makoto
@testmode(sequential, [isolated, shared])@test fn s_one() { assert (1 == 1) }Rust
// cargo test -- --test-threads=1@property(runs: N) (property-based com shrinking)
Section titled “@property(runs: N) (property-based com shrinking)”Status: implementado
Argumentos gerados (gen), falha reduzida ao menor contraexemplo.
Makoto
use test@property(runs: 50) fn prop_commutes(a: int, b: int) { test.expect(a + b == b + a) }Rust
proptest! { #[test] fn commutes(a: i32, b: i32) { prop_assert_eq!(a.wrapping_add(b), b.wrapping_add(a)); } }@fuzz fn f(data: []u8) (guiado por cobertura)
Section titled “@fuzz fn f(data: []u8) (guiado por cobertura)”Status: implementado
Corpus e crashes persistidos; alvo recebe bytes arbitrários.
Makoto
use test@fuzz fn fuzz_utf8(data: []u8) { match string.from[u8](data) { Ok(s) => test.expect(s.bytes.len == data.len); Err(e) => { let _ := e } }}Rust
fuzz_target!(|data: &[u8]| { ... }); // cargo-fuzzExtensão UI (.mkoui)
Section titled “Extensão UI (.mkoui)”Primeira extensão oficial do compilador (não é core): arquivo .mkoui liga a gramática de markup. Reatividade
fina estilo SolidJS (sem Virtual DOM), ilhas (só o reativo vira WASM/JS no cliente), SSR, CSS com escopo
verificado, @server/@client, @jsimport, e transpile pra JS (--target=js).
@component fn retornando markup
Section titled “@component fn retornando markup”Status: implementado
Componente = função; props = parâmetros; markup vai no return { ... }. {{ }} = conteúdo entre tags;
{ } = valor depois de = (prop, atributo dinâmico, handler).
Makoto
@componentfn Greeting(name: string, count: int) { return { <div class="card"><h1>Olá, {{name}}</h1><p>{{count}} mensagens</p></div> }}// uso: <Greeting name="Ana" count={5} />Rust
#[component] fn Greeting(name: String, count: i32) -> impl IntoView { view! { <div>...</div> } } // Leptos@state, @derived, @effect, @context
Section titled “@state, @derived, @effect, @context”Status: implementado
@state = variável reativa; @derived = recomputada quando dependências mudam; @effect = roda quando
dependências mudam (só no cliente); @context = injeção pela árvore (consumidor sem provider = erro de compilação).
Makoto
@client@componentfn Counter() { @state n := 0 @derived doubled := n * 2 @effect { io.println("n = {{n}}") } return { <p>{{doubled}}</p> }}Rust
let (n, set_n) = signal(0); let doubled = move || n.get() * 2; // Leptos@component(layout) + slot
Section titled “@component(layout) + slot”Status: implementado
Layout é componente cujos parâmetros slot recebem o conteúdo filho (como children). @component(page) gera
documento HTML completo.
Makoto
@component(layout)fn Shell(body: slot) { return { <main>{{body}}</main> } }@componentfn Page() { return { <Shell><h1>Oi</h1></Shell> } }Rust
#[component] fn Shell(children: Children) -> impl IntoView { view! { <main>{children()}</main> } }@server / @client e ilhas
Section titled “@server / @client e ilhas”Status: implementado
Restrições, não marcas obrigatórias: @server nunca vai pro cliente (segredos, DB); @client só roda no
browser (vira ilha). Sem marca = roda onde for chamado. A restrição propaga (chamar @server de @client = erro).
Makoto
@serverfn load_count() -> int { return 7 }@componentfn Badge() { return { <span>{{load_count()}}</span> } }Rust
#[server] async fn load_count() -> Result<i32, ServerFnError> { Ok(7) } // LeptosCSS com escopo, @scope(global), @css("arquivo.css")
Section titled “CSS com escopo, @scope(global), @css("arquivo.css")”Status: implementado
Classes do <style> do componente ganham hash (não colidem); class="X" sem .X definido = erro de compilação
(inclusive classes de CSS importado, ex. Tailwind). @scope(global) desliga o hash; class={expr} dinâmico.
Makoto
@componentfn Pill(active: bool) { return { <span class={ active ? "pill-on" : "pill" }>x</span><style>.pill { color: gray } .pill-on { color: green }</style> }}Rust
// stylers / leptos-style@jsimport("mod", browser|node) js.mod.*
Section titled “@jsimport("mod", browser|node) js.mod.*”Status: implementado
Análogo ao @cimport: tipos lidos de .d.ts, cola WASMJS gerada. Ambiente declarado propaga (usar dom em
código @server = erro de compilação, não window is not defined em runtime).
Makoto
@jsimport("dom", browser)el := js.dom.getElementById("app")Rust
use web_sys::window; // wasm-bindgenloop dentro do markup
Section titled “loop dentro do markup”Status: implementado
O loop do core reaproveitado em posição de conteúdo; forma funcional equivalente com {{ xs.map(...) }}.
Makoto
@componentfn Lista(xs: []int) { return { <ul>loop x in xs { <li>{{x}}</li> }</ul> } }Rust
view! { <ul>{xs.into_iter().map(|x| view! { <li>{x}</li> }).collect_view()}</ul> }Camada de verificação (planejada)
Section titled “Camada de verificação (planejada)”Desenhada (§21 + verification.md), não implementada ainda (épico MKO-499). Ideia central: toda garantia é
uma obrigação sobre um predicado, descarregada no degrau mais alto possível — tipo (0) dobra comptime (1)
extensão SMT/model-checker (2) checagem runtime (3) aviso (4) assume humano (5). Nunca cai “pra fora da escada”.
Linear/afim no tipo: @must_consume / @consume_once
Section titled “Linear/afim no tipo: @must_consume / @consume_once”Status: planejado
Recurso que precisa ser consumido exatamente uma vez (transação) ou no máximo uma (buffer liberado). Typestate
via value-params: File[Open] File[Closed].
Makoto
decl Transaction @must_consume { conn: *Connection }fn close(f: File[Open] @consume) -> File[Closed] { ... }Rust
// ownership afim nativo; #[must_use] só avisaDiferença: Rust é afim por padrão; linear de verdade não existe.
Tipos refinados: @where
Section titled “Tipos refinados: @where”Status: planejado
Tipo + predicado: alias Balance = i64 @where(i64 >= 0). Subtipagem por implicação; narrowing após if.
Makoto
alias Balance = i64 @where(i64 >= 0)alias NonEmpty[T] = (xs: List[T]) @where(xs.len > 0)Rust
// crates: refinement, nutype (checagem em runtime)Cercas: @pure e @total
Section titled “Cercas: @pure e @total”Status: planejado
@pure: sem efeitos (alocação, spawn, FFI, IO, panic). @total: termina (recursão estrutural / medida
decrescente). Obrigatórias em comptime fn usada em posição de tipo.
Makoto
@pure @totalfn fact(n: int) -> int { return n <= 1 ? 1 : n * fact(n - 1) }Rust
// const fn aproxima pureza; sem checagem de terminaçãoTipos de sessão: @protocol, @sends/@receives, dual
Section titled “Tipos de sessão: @protocol, @sends/@receives, dual”Status: planejado
Canal tipado por protocolo vira typestate linear que o checker percorre; dual P gera o lado espelhado.
Makoto
@protocol decl Auth { Credentials @sends @receives match { Ok => Token @receives Deny => Reason @receives }}let c: Channel[Auth] // o servidor recebe Channel[dual Auth]Rust
// crates: session_types, ferriteTipos dependentes: camada prática e @proof
Section titled “Tipos dependentes: camada prática e @proof”Status: planejado
Prática: value-params (decl Vec[T, n: usize] { data: [n]T }) — os value-params já existem. Prova (@proof):
famílias indexadas/GADTs, normalização simbólica, Σ/Π; prova = programa total (Curry-Howard).
Makoto
decl Vec[T, n: usize] { data: [n]T }@proof decl Eq[A, x: A, y: A] -> prop { Refl: fn[a: A]() -> Eq[A, a, a] }Rust
struct Vec<T, const N: usize> { data: [T; N] } // const generics = camada práticaModel checking: @spec (extensão) e táticas @by
Section titled “Model checking: @spec (extensão) e táticas @by”Status: planejado
@spec verifica propriedades temporais de um sistema de processos ancorado num @supervisor (safety/liveness).
@by { induction(xs); simp } (táticas) fica pro self-hosting.
Makoto
@spec(spec.always(spec.implies(submitted(req), spec.eventually(committed(req)))))Rust
// kani (model checker)Ferramentas (mk)
Section titled “Ferramentas (mk)”Um binário só. Compilador nativo: Go LLVM IR clang embutido; toolchain hermético (musl). Subcomandos atuais:
run, build, check, test, analyze, get, export, cc; mk sem argumentos abre o REPL.
mk run | build | check
Section titled “mk run | build | check”Status: implementado
-O0/-O2/--release, --target=<triple> (x86_64, aarch64, riscv64 via qemu, wasm32, js),
--emit=lib (estática + header C), flags de GC/scheduler/autofree.
Makoto
// mk run app.mko// mk build --release --target=aarch64-linux-musl app.mko// mk check app.mkoRust
cargo run --releasemk test, mk analyze, mk get, mk export, mk cc
Section titled “mk test, mk analyze, mk get, mk export, mk cc”Status: implementado
Testes; relatório do analisador de memória; baixar deps (Git, MVS); gerar cola CMake/Ninja; cc/c++ drop-in.
Makoto
// mk test// mk analyze app.mko// mk get github.com/acme/jwt// mk export cmake// mk cc -c shim.c -o shim.oRust
cargo test; cargo add; cargo clippyFormatter, LSP, linter, doc (///), migração, observer, debugger
Section titled “Formatter, LSP, linter, doc (///), migração, observer, debugger”Status: planejado
Desenhados (§22): compilador como processador de queries incremental (base do LSP), formatter sem config,
linter fechado, doc comments /// HTML/Markdown, ferramenta de migração, observer/debugger com breakpoints ligados.
Makoto
/// Soma dois inteiros.fn add(a: int, b: int) -> int { return a + b }Rust
rustfmt; rust-analyzer; clippy; rustdoc