Não confunda a declaração ‘jogar’ de Ruby com ‘aumentar’

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çarfinal ou deffinal 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, arremessarpegar é uma forma de escapar das entranhas de múltiplas camadas de controle de fluxo sem abusar do aumentoresgate 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.)