Erros
Objetivo: tratar falha como valor (
Resulte error-sets), propagar comcatch(sem operador?), e conhecernoreturn.
Dois eixos: Result e error{...}
Seção intitulada “Dois eixos: Result e error{...}”Falha tem duas perguntas, e Makoto dá um construto pra cada:
Result[T, E]: a resolução, ou seja, deu certo (Ok(T)) ou não (Err(E)).error{...}: a união de erros, ou seja, quais falhas existem (um error-set fechado e estrutural).
Os dois compõem: o E de um Result é um error{...}.
fn open(path: string) -> Result[File, error{NotFound, PermissionDenied}] { ... }error pelado é aberto (FFI/terceiros, exige _ no match); error{A, B} é fechado (exaustivo,
proíbe _); error{} é o conjunto vazio, ou seja, “não erra” (um Result[T, error{}] cujo Err é
inconstruível, útil quando um concreto satisfaz uma interface falível mas nunca falha).
Casar o Result (fork)
Seção intitulada “Casar o Result (fork)”Casar o Result inteiro cobre o Ok e cada variante de erro, de forma exaustiva:
match open(path) { Ok(f) => use(f) Err(error.NotFound) => criar_default() Err(error.PermissionDenied) => abortar()}Trailers: catch e match |e| (sem ?)
Seção intitulada “Trailers: catch e match |e| (sem ?)”No caso sequencial, quando o sucesso continua e só o erro precisa de atenção, use os trailers. O Ok é
resolvido pela atribuição; o trailer lida só com o erro. Não existe operador ?: a propagação é
explícita.
v := do_it(x) catch |e| { return Err(e) } // propaga (explícito)v := do_it(x) catch |e| { return e } // açúcar: 'return e' → Err(e)v := do_it(x) catch |e| { runtime.panic(e) } // abortacatch |e| trata o erro como unidade; match |e| ramifica por variante (só faz sentido com
erro união):
cfg := load() match |e| { // Ok → cfg; cada braço de erro ESCAPA error.NotFound => return new_config() // early-return por variante error.Corrupted => runtime.panic("config ilegível")}Em value-binding, cada braço do trailer escapa (return/break/panic): o trailer não dá
valor ao v. Quando você quer que o erro produza um valor (um default que vira o v), o
construto certo é o match sobre o Result inteiro (o fork acima), não o trailer.
Compor error-sets é unir, com o spread nome...:
fn load(p: string) -> Result[Data, error{open..., parse...}] { // une as variantes de open e parse f := open(p) catch |e| { return e } d := parse(f) catch |e| { return e } return Ok(d)}Sucesso sem valor: o error-set sozinho
Seção intitulada “Sucesso sem valor: o error-set sozinho”Como não há unit, uma operação que dá certo sem produzir valor devolve só o error-set:
fn send(msg: Msg) -> error{ProcessDown} // sucesso = ausência de erro
chan <- data catch |e| { return e } // erro → propaga; sucesso → cai pra próxima linhanoreturn: divergência
Seção intitulada “noreturn: divergência”Uma função que nunca devolve o controle tem o tipo bottom noreturn. É o que deixa panic/exit
servirem de “escape” num catch em value-binding (o caminho de erro diverge, então o v ainda é o
tipo do Ok):
fn panic(msg: string) -> noreturn // runtime.panic; idem os.exit, event-loops infinitos
x := parse(s) catch |e| { runtime.panic(e) } // x tipa como int: o braço de erro divergePróximo: 05 · Coleções e strings