Os programadores que vêm para Ruby de outras linguagens – particularmente C ++, C♯ ou Java – tendem a usar indevidamente a instrução throw do Ruby . Eles datilografam lance quando pretendem digitar aumento .
Qual é a diferença?
Como C ++, C♯ e Java, Ruby tem suporte de primeira classe para exceções. Exceções podem ser lançadas usando a instrução raise e capturadas usando a palavra-chave rescue . Os rubistas costumam falar de “erros” em vez de “exceções”. Isso ocorre porque é aceito que os nomes das classes de exceção Ruby geralmente terminam em Erro ao invés de Exceção, exceto a classe base para exceções que é chamada de Exceção. (Ainda comigo?)
Você pode dizer: “Ok, então vou começar a dizer ‘erro’ quando quero dizer ‘exceção’. Mas por que não posso, pelo menos, digite os lance e captura palavras-chave que eu estou acostumado a digitar?”
Ruby tem um recurso de linguagem distinto que consiste em pares de instruções de lançamento e captura . O paradigma de jogar e pegar funciona de maneira semelhante para levantar e resgatar . Quando Ruby encontra uma declaração de lançamento , como um bom casamenteiro, ela caminha de volta para cima na pilha de execução (“pegue-me uma captura!”) Procurando uma captura adequada . Aqui está um código para ilustrar.
class Tevye
def live
sunrise
sunset
l_chaim!
toil_in_anatevka
:pauper
end
def rich_man?
false
end
private
def method_missing(method, *args)
nil
end
def toil_in_anatevka
if self.rich_man?
throw :miracle_of_miracles,
'Emigrated to the Cayman Islands'
end
end
end
poor_tevye = Tevye.new
result = catch(:miracle_of_miracles) do
poor_tevye.live
end
Qual é o resultado inevitável de trabalhar em Anatevka? Nada muito surpreendente: é ser um : mendigo .
Mas vamos mudar um pouco a sorte de Tevye e recompensá-lo ricamente por seu trabalho. Dê uma olhada.
rich_tevye = Tevye.new
def rich_tevye.rich_man?
true
end
result = catch(:miracle_of_miracles) do
rich_tevye.live
end
O que acontecerá com o trabalho árduo de Tevye se deus ex machina descer sobre a aldeia? Tevye vai se aposentar para o oeste do Caribe, e sua esposa, Golde, ganha uma papada.
Assim como um erro não recuperado faz com que o processo Ruby trave, um lançamento sem uma captura correspondente gerará um erro (que por sua vez você pode resgatar).
Não é apenas semântica
Você não pode simplesmente jogar e pegar onde quer que você levantaria e salvaria. Existem diferenças além do último ser para representar condições de exceção e o primeiro para outros tipos de stack popping.
Para começar, (como você esperaria de suas experiências com outras linguagens) resgatar NameError resgata erros do tipo NameError , bem como subclasses como NoMethodError. Por analogia, você pode presumir que catch Entertainer lidaria com todas essas declarações de lançamento:
• jogue Entertainer
• jogue Entertainer.new
• jogue BroadwayStar
• jogue BroadwayStar.new
Você estaria errado: catch Entertainer lida com apenas arremessá-lo.
Outra diferença importante é que o lançamento pode ter dois argumentos, conforme demonstrado na aula Tevye acima. O segundo argumento a lançar é o que Ruby retorna da instrução catch correspondente (também demonstrada acima).
Uma distinção adicional é o fato de que resgate pode aparecer mais de uma vez em um começar – final ou def – final par. O primeiro resgate correspondente em uma sequência vence.
begin
do_something_error_prone
rescue AParticularKindOfError
# Insert heroism here.
rescue Exception
write_to_error_log
raise
end
Em contraste, catch é uma construção em bloco. Várias instruções catch devem ser aninhadas para aparecerem juntas.
catch :foo do
catch :bar do
do_something_that_can_throw_foo_or_bar
end
end
Antes do Ruby v1.9, apenas símbolos (por exemplo ,: pauper ) podiam ser lançados e pegos. Esta restrição foi removida, então agora você pode lançar qualquer objeto Ruby e capturá-lo com sucesso.
O resultado final
Quando você deve usar este recurso de idioma? Não muito frequentemente. Como você provavelmente notou, meu exemplo inventado sobre a vida camponesa na Rússia czarista é uma aplicação questionável do padrão de arremesso e captura . Existem dois exemplos plausíveis no livro Pickaxe ( http://ruby-doc.org/docs/ProgrammingRuby/html/tut_exceptions.html#S4 ). Fundamentalmente, arremessar – pegar é uma forma de escapar das entranhas de múltiplas camadas de controle de fluxo sem abusar do aumento – resgate para esse propósito.
Mas se você tem tantas camadas de controle de fluxo que fica tentado a jogar para sair delas rapidamente, considere tentar eliminar as camadas primeiro.
(Eu adaptei este protocolo de uma postagem do blog que escrevi em maio de 2010.)