Tr

Try/Rescue em Elixir

1 exercício

Sobre Try/Rescue

O try .. rescue pode ser usado para capturar e avaliar erros que são lançados dentro de um bloco.

Por exemplo:

try do
  raise RuntimeError, "error"
rescue
  e in RuntimeError -> :error
end
  • Linha 1: o bloco é declarado com try.
  • Linha 2: a chamada de função que pode gerar um erro é colocada aqui; neste caso, estamos a chamar raise/1.
  • Linha 4: na secção rescue, fazemos correspondência de padrões sobre o nome do Módulo do erro lançado.
    • no lado esquerdo de ->:
      • e corresponde ao struct do erro.
      • in é uma palavra-chave.
      • RuntimeError é o módulo a corresponder, mas podes corresponder a qualquer módulo de erro, ou usar _ para todos os erros.
    • no lado direito:
      • as instruções a executar se o erro corresponder.

Structs de erro

Os erros (por vezes também chamados "exceções") que resgatas desta forma são structs. Structs de erro diferentes têm chaves diferentes. Na secção "exceções" da biblioteca padrão encontras uma lista de todos os erros predefinidos.

# ArithmeticError caused by division by zero
%ArithmeticError{message: "bad argument in arithmetic expression"}

# Protocol.UndefinedError caused by passing `nil` to `Enum.count/1`
%Protocol.UndefinedError{description: "", protocol: Enumerable, value: nil}

Em Elixir, resgatar erros é algo muito raro. Normalmente, o erro resgatado é registado num log ou enviado para um serviço externo de monitorização, e depois volta a ser lançado. Isto significa que, habitualmente, não nos interessa a estrutura interna do struct de erro específico.

O conceito de exceções descreve como definir structs de erro personalizados.

Evita anti-padrões

  • Evita padrões de programação que usam erros para controlar o fluxo lógico. Isto é um anti-padrão em Elixir.

    # Avoid using errors for control-flow.
    try do
      {:ok, value} = MyModule.janky_function()
      "All good! #{value}."
    rescue
      e in RuntimeError ->
        reason = e.message
        "Uh oh! #{reason}."
    end
    
    # Rather, use control-flow structures for control-flow.
    case MyModule.janky_function() do
      {:ok, value} -> "All good! #{value}."
      {:error, reason} -> "Uh oh! #{reason}."
    end
    

Lançar ou não lançar, eis a questão

Como está escrito no guia de introdução do Elixir:

É a tua aplicação que decide se um erro ao [realizar uma ação] é excecional ou não. É por isso que o Elixir não impõe exceções às funções [...]. Em vez disso, deixa ao programador a escolha da melhor forma de proceder.

Editar via GitHub A ligação abre numa nova janela ou separador

Aprende Try/Rescue