Memória
Objetivo: entender por que você quase nunca pensa em memória (colorless + GC default), e como descer ao metal (
@mm,@transfer/@promote) quando precisa.
Colorless: o default é não pensar
Seção intitulada “Colorless: o default é não pensar”Alocação é colorless: cada objeto carrega um ponteiro implícito pro seu MemoryManager, e o
runtime chama a interface sem saber qual estratégia está por baixo. Com o GC default, você escreve
código normal e nunca toca @mm:
fn build() -> Report { r := Report.new() // aloca pelo @mm do contexto (GC, por default) r.add(linha) return r // sem free manual; o GC cuida}Mutabilidade segue o capítulo 01: var muta, imutável aliasa de graça (compartilhado sem cópia).
Para mutar o valor de quem chama, use mut (write-back, estilo inout):
fn accumulate(total: mut int, values: []int) { loop v in values { total += v }}var soma := 0accumulate(mut soma, dados) // 'mut' no call site; o alvo tem que ser 'var'Ponteiro *T (um só tipo, sem *const/*mut) serve para segurar uma referência persistente
(grafos, FFI); nasce de &lugar com identidade no heap, e deref é explícito (*p). Coleções usam
[*]T (ponteiro-de-muitos) como backing, algo que você raramente vê direto.
@mm: escolher a estratégia
Seção intitulada “@mm: escolher a estratégia”@mm define o allocator do contexto (processo, função ou objeto). As estratégias: gc (default),
arena (libera em bloco), none (manual, estilo C), c (memória vinda do C).
fn handle(req: Request) -> Response { arena := Arena.new() defer arena.free_all() // tudo daqui morre de uma vez @mm(arena) { ... // alocações deste bloco usam a arena }}Sob arena/none você é dono do lifetime (território unsafe); o GC é a rede segura por construção.
Mover entre processos: @transfer / @promote
Seção intitulada “Mover entre processos: @transfer / @promote”Como processos não compartilham memória, passar um dado de um pro outro é explícito. @transfer
move (invalida a origem; o objeto viaja com seu @mm intacto); @promote(mm) copia pra
outro MemoryManager:
@mm(arena) spawn producer(data @transfer) // move; 'data' fica inválido aqui@mm(gc) spawn consumer(chan)
result @promote(gc, deep) // copia o subgrafo pro GC (corta o cordão com a arena)(Cópia simples é o default não-marcado; não há @copy.)
@promote copia o objeto no ponteiro. Um bloco gc carrega seu tamanho no header, então promover um [*]T gc
copia o bloco inteiro, com quantos elementos tiver. Um [*]T cru (many-pointer non-gc) não carrega length –
como um ponteiro C – então promove como um único objeto. Para promover uma coleção multi-elemento de fora de
uma região non-gc, promova um valor que carrega length (uma List, ou um bloco gc), não um many-pointer cru.
OOM é evento de processo, não erro por-chamada
Seção intitulada “OOM é evento de processo, não erro por-chamada”As conveniências de alto nível (List.push, string.append, encode) não threadam OOM, porque
isso seria coloring. A regra é a do BEAM: a alocação que falha mata o processo, e o supervisor trata
(cap. 07). O caso recuperável com orçamento (uma arena com teto, um @limit) desce ao
mem.alloc, que devolve Result[Ptr, error{OOM}]:
@limit(16mb)spawn worker(data) // estourou o teto → erro de OOM vira o |e| do supervisorDois mundos sem conflito: alto nível colorless que crasha (99% do código), e baixo nível com
Result explícito (autor de allocator, OOM com orçamento).
Próximo: 09 · Channels de rede