Ruby QuickRef, de Ryan Davis, diz (sem explicação):
Não salve Exceção. SEMPRE. ou eu te apunhalarei.
Por que não? Qual é a coisa certa a fazer?
ruby
exception-handling
John
fonte
fonte
Respostas:
TL; DR : use
StandardError
para captura de exceção geral. Quando a exceção original é gerada novamente (por exemplo, ao resgatar para registrar apenas a exceção), o resgateException
provavelmente está correto.Exception
é a raiz da hierarquia exceção de Ruby , então quando vocêrescue Exception
você salva de tudo , incluindo subclasses tais comoSyntaxError
,LoadError
, eInterrupt
.O resgate
Interrupt
evita que o usuário use CTRLCpara sair do programa.O resgate
SignalException
impede que o programa responda corretamente aos sinais. Será inviável, exceto porkill -9
.Resgatando
SyntaxError
significa que oseval
que falharem o farão silenciosamente.Tudo isso pode ser mostrado executando este programa e tentando CTRLCou
kill
:O resgate de
Exception
nem é o padrão. Fazendonão resgata
Exception
, resgata deStandardError
. Você geralmente deve especificar algo mais específico do que o padrãoStandardError
, mas resgatando a partirException
amplia escopo vez de estreitá-lo, e pode ter resultados catastróficos e tornar extremamente difícil a busca de bugs.Se você tem uma situação em que deseja resgatar
StandardError
e precisa de uma variável com a exceção, pode usar este formulário:que é equivalente a:
Um dos poucos casos comuns em que é prudente resgatar
Exception
é para fins de registro / relatório; nesse caso, você deve imediatamente levantar novamente a exceção:fonte
Throwable
em javaADAPTER_ERRORS = [::ActiveRecord::StatementInvalid, PGError, Mysql::Error, Mysql2::Error, ::ActiveRecord::JDBCError, SQLite3::Exception]
e depoisrescue *ADAPTER_ERRORS => e
A regra real é: não jogue fora as exceções. A objetividade do autor da sua citação é questionável, como evidenciado pelo fato de que termina com
Obviamente, esteja ciente de que os sinais (por padrão) geram exceções e, normalmente, os processos de execução longa são encerrados por meio de um sinal; portanto, capturar Exceção e não encerrar em exceções de sinal dificultará a interrupção do programa. Então não faça isso:
Não, sério, não faça isso. Nem execute isso para ver se funciona.
No entanto, diga que você possui um servidor encadeado e deseja que todas as exceções não sejam:
thread.abort_on_exception = true
).Isso é perfeitamente aceitável no segmento de manipulação de conexões:
O exemplo acima funciona com uma variação do manipulador de exceção padrão do Ruby, com a vantagem de que ele também não mata o seu programa. O Rails faz isso em seu manipulador de solicitações.
Exceções de sinal são geradas no encadeamento principal. Os threads de plano de fundo não os receberão, portanto, não faz sentido tentar pegá-los lá.
Isso é particularmente útil em um ambiente de produção, no qual você não deseja que seu programa simplesmente pare sempre que algo der errado. Em seguida, você pode pegar os despejos de pilha em seus logs e adicionar ao seu código para lidar com exceções específicas mais adiante na cadeia de chamadas e de uma maneira mais elegante.
Observe também que há outro idioma Ruby que tem o mesmo efeito:
Nesta linha, se houver
do_something
uma exceção, ela é capturada por Ruby, jogada fora ea
é atribuída"something else"
.Geralmente, não faça isso, exceto em casos especiais em que você sabe que não precisa se preocupar. Um exemplo:
o
debugger
função é uma maneira bastante agradável de definir um ponto de interrupção no seu código, mas se estiver executando fora de um depurador e do Rails, isso gera uma exceção. Agora, teoricamente, você não deve deixar o código de depuração no seu programa (pff! Ninguém faz isso!), Mas você pode mantê-lo por um tempo por algum motivo, mas não executar continuamente o seu depurador.Nota:
Se você executou o programa de outra pessoa que captura exceções de sinal e as ignora (diga o código acima), então:
pgrep ruby
oups | grep ruby
procure o PID do seu programa incorreto e executekill -9 <PID>
.Se você estiver trabalhando com o programa de outra pessoa que, por qualquer motivo, estiver repleto desses blocos de exceção de ignorar, colocar isso no topo da linha principal é uma possível cópia:
Isso faz com que o programa responda aos sinais normais de finalização, finalizando imediatamente, ignorando os manipuladores de exceção, sem limpeza . Portanto, pode causar perda de dados ou similar. Seja cuidadoso!
Se você precisar fazer isso:
você pode realmente fazer isso:
No segundo caso,
critical cleanup
será chamado sempre, independentemente de uma exceção ser lançada ou não.fonte
kill -9
.ensure
será executado independentemente de haver uma exceção gerada ou não, enquanto orescue
será executado apenas se uma exceção for gerada.TL; DR
Não faça
rescue Exception => e
(e não aumente a exceção) - ou você pode sair de uma ponte.Digamos que você esteja em um carro (executando Ruby). Recentemente, você instalou um novo volante com o sistema de atualização sem fio (que usa
eval
), mas não sabia que um dos programadores estragou a sintaxe.Você está em uma ponte e percebe que está indo um pouco em direção ao corrimão, então vire à esquerda.
oops! Provavelmente isso não é bom ™, felizmente, Ruby levanta a
SyntaxError
.O carro deve parar imediatamente - certo?
Não.
Você percebe que algo está errado, e você pisar os freios de emergência (
^C
:Interrupt
)Sim - isso não ajudou muito. Você está bem perto do trilho, então você estaciona o carro (
kill
ing:)SignalException
.No último segundo, você puxa as teclas (
kill -9
) e o carro para, bate no volante (o airbag não pode ser inflado porque você não interrompeu graciosamente o programa - você o encerrou) e o computador na parte de trás do seu carro bate no banco em frente a ele. Uma lata pela metade de Coca-Cola derrama sobre os papéis. Os mantimentos na parte de trás são esmagados, e a maioria é coberta com gema de ovo e leite. O carro precisa de reparos e limpeza sérios. (Perda de dados)Espero que você tenha seguro (Backups). Ah, sim - porque o airbag não inflou, você provavelmente está ferido (ser demitido, etc.).
Mas espere! Há
Maisrazões pelas quais você pode querer usarrescue Exception => e
!Digamos que você é o carro e deseja garantir que o airbag se esgote se o carro estiver excedendo seu momento de parada segura.
Aqui está a exceção à regra: Você pode capturar
Exception
apenas se você aumentar novamente a exceção . Portanto, uma regra melhor é nunca engolirException
e sempre aumentar novamente o erro.Mas é fácil esquecer o acréscimo de resgate em um idioma como Ruby, e colocar uma declaração de resgate logo antes de voltar a levantar um problema parece um pouco não-SECO. E você não quer esquecer a
raise
afirmação. E se o fizer, boa sorte tentando encontrar esse erro.Felizmente, Ruby é incrível, você pode simplesmente usar a
ensure
palavra - chave, o que garante que o código seja executado. Aensure
palavra-chave executará o código, não importa o quê - se uma exceção for lançada, se não houver, a única exceção será se o mundo terminar (ou outros eventos improváveis).Estrondo! E esse código deve ser executado de qualquer maneira. O único motivo que você deve usar
rescue Exception => e
é se você precisa acessar a exceção ou se deseja que o código seja executado apenas em uma exceção. E lembre-se de aumentar novamente o erro. Toda vez.Nota: Como o @Niall apontou, verifique sempre a execução. Isso é bom porque, às vezes, seu programa pode mentir para você e não gerar exceções, mesmo quando ocorrem problemas. Em tarefas críticas, como inflar airbags, você precisa garantir que isso aconteça, não importa o quê. Por isso, verificar sempre que o carro parar, se uma exceção é lançada ou não, é uma boa idéia. Embora inflar airbags seja uma tarefa pouco comum na maioria dos contextos de programação, isso é bastante comum na maioria das tarefas de limpeza.
fonte
ensure
alternativarescue Exception
é enganosa - o exemplo implica que são equivalentes, mas, como afirmadoensure
, acontecerá se há uma exceção ou não, então agora seus airbags inflarão porque você ultrapassou os 5 km / h, mesmo que nada tenha dado errado.Porque isso captura todas as exceções. É improvável que seu programa possa se recuperar de qualquer um deles.
Você deve lidar apenas com exceções das quais sabe se recuperar. Se você não antecipar um certo tipo de exceção, não lide com isso, bata alto (grave detalhes no log), faça o diagnóstico dos logs e corrija o código.
Engolir exceções é ruim, não faça isso.
fonte
Esse é um caso específico da regra que você não deve capturar nenhuma exceção que não sabe como lidar. Se você não sabe como lidar com isso, é sempre melhor deixar outra parte do sistema capturar e lidar com isso.
fonte
Acabei de ler um ótimo post sobre isso no honeybadger.io :
Exceção de Ruby vs StandardError: Qual é a diferença?
fonte