Erros
O Kriol distingue duas espécies de erros de execução:
- Bugs do programa, como um índice fora dos limites, um resto por zero ou uma condição de
konfirmafalsa. Terminam o programa de imediato com um erro de execução. - Falhas esperadas, como uma entrada inválida ou um ficheiro que não existe. A função que falha declara que pode falhar, e quem a chama decide o que fazer.
A diferença é de intenção. Um bug é uma coisa que tu fizeste mal, e não há nada de útil a fazer em execução a não ser parar e mostrar onde. Uma falha esperada vem de fora do programa, e um programa bem feito tem uma resposta para ela.
| Situação | Espécie | O que o Kriol faz |
|---|---|---|
| Índice de array fora dos limites | Bug | Termina o programa |
| Divisão ou resto por zero | Bug | Termina o programa |
konfirma falso, ou paniku | Bug | Termina o programa |
Texto que não é um número em int::konverti | Falha esperada | Devolve um Erru a quem chamou |
Entrada que acabou, em toma | Falha esperada | Devolve um Erru a quem chamou |
lansa numa função tua com : Erru | Falha esperada | Devolve um Erru a quem chamou |
Bugs: konfirma e paniku
Seção intitulada “Bugs: konfirma e paniku”konfirma(idadi >= 0, "a idade não pode ser negativa");
si estadu == 0 { // ...} sinon { paniku(f"estado inválido: {estadu}");}Um erro de execução escreve programa.kriol:N: erro de execução: mensagem, com o ficheiro e a linha, e termina o programa com o código 1. Vê Controlo de fluxo.
Falhas: o tipo Erru
Seção intitulada “Falhas: o tipo Erru”Erru é um molde que já existe em todos os programas:
molda Erru { textu mensage;}Uma função que pode falhar declara-o a seguir ao tipo de retorno, depois de ::
fn metadi(int a, int b) num : Erru { si b == 0 { lansa Erru{mensage = "divizon pa zero"}; } divolvi (num) a / b;}fn nome() int : Errudevolve umintou falha com umErru.fn nome() : Errunão devolve valor, mas pode falhar.lansa <erru>;falha a função de imediato. Só pode ser usado numa função que declare: Erru.- Por agora
Errué o único tipo de erro aceite depois de:. Errué um nome reservado: um programa não pode declarar outromolda Erru.
A mensagem é texto livre, e pode ser uma f-string, o que é a forma de incluir o valor que causou a falha:
lansa Erru{mensage = f"mês inválido: {m}"};Tratar uma falha
Seção intitulada “Tratar uma falha”Quem chama uma função que pode falhar pode tratar o erro de duas formas, descritas a seguir. Se não o tratar, o erro não desaparece: quando a função falhar, o programa termina nessa chamada com programa.kriol:N: erro não tratado: mensagem. O compilador avisa disso, mas continua a compilar.
fn inisiu() { num x = metadi(1, 0); // aviso: o erro de 'metadi' não é tratado} // em execução: programa.kriol:2: erro não tratado: divizon pa zeroPara o compilador tratar os avisos como erros, compila com --strict.
tenta: passar o erro a quem chamou
Seção intitulada “tenta: passar o erro a quem chamou”tenta chamada devolve o valor da chamada, ou, se ela falhar, falha também a função atual com o mesmo erro. Por isso só pode ser usado dentro de uma função que declare : Erru.
fn valida(int n) : Erru { si n < 0 { lansa Erru{mensage = f"negativu: {n}"}; }}
fn kaminhu(int n) num : Erru { tenta valida(n); num m = tenta metadi(n, 4); divolvi m + 1;}sinon: usar um valor por omissão
Seção intitulada “sinon: usar um valor por omissão”chamada sinon valor dá o valor da chamada, ou valor se ela falhar. O valor depois de sinon só é calculado quando a chamada falha, e tem de ser compatível com o tipo devolvido.
num m = metadi(7, 0) sinon -1.0; // -1.0sinon tem a precedência mais baixa de todos os operadores (acima só da atribuição), por isso usa parênteses para o combinar com outros operadores: (metadi(7, 2) sinon 0.0) == 3.5.
Qual escolher
Seção intitulada “Qual escolher”| Queres | Usa |
|---|---|
| Continuar com um valor razoável | chamada sinon valor |
| Desistir e deixar quem chamou decidir | tenta chamada |
| Parar o programa aqui, porque a falha não devia acontecer | não trates, ou converte num paniku |
A função inisiu
Seção intitulada “A função inisiu”inisiu também pode declarar : Erru. Se um erro chegar até ela sem ser tratado, o programa escreve programa.kriol:N: erro não tratado: mensagem, com a linha onde o erro saiu de inisiu, e termina com o código 1:
fn inisiu() : Erru { num x = tenta metadi(1, 0);}É o atalho habitual para programas pequenos: usa tenta em todas as chamadas que podem falhar, e deixa a mensagem aparecer se algo correr mal.
Avisos e --strict
Seção intitulada “Avisos e --strict”Ignorar o erro de uma função declarada por ti com : Erru gera um aviso:
programa.kriol:9: aviso: o erro de 'metadi' não é tratado, por isso o programatermina se a função falhar; usa 'tenta' ou 'sinon'É provável que seja um esquecimento, porque quem escreveu a função previu esse caminho.
As funções embutidas que podem falhar, toma e tipo::konverti, não geram aviso, nem com --strict. Parar quando a entrada acaba, ou quando o texto não é um número, é um comportamento aceitável, como num índice fora dos limites, e mantém simples os primeiros programas.
kriol programa.kriol # os avisos são mostrados, mas o programa compilakriol --strict programa.kriol # qualquer aviso passa a ser um erro de compilaçãoUsa --strict em projetos e na integração contínua para garantir que nenhum erro é ignorado.
- Um erro não tratado termina o programa na chamada que falhou; só
tentaesinono tratam. tentaesinonsó se aplicam a chamadas de funções que podem falhar.tentasó pode ser usado dentro de uma função que declare: Erru.sinonnão se aplica a funções que não devolvem valor.Errunão pode ser redeclarado.- O tipo de uma chamada que pode falhar não se pode escrever numa declaração: o resultado só existe para
tentaesinono consumirem. - Não há forma de ler a mensagem de um erro que apanhaste:
sinondá-te um valor alternativo, não oErru. Se precisares da mensagem, escreve-a nolansaou deixa o erro subir.