Por que posso me referir a uma variável fora de uma declaração if / unless / case que nunca foi executada?

Por que o código a seguir não gera um erro?

if false x = 0 end x #=> nil 

Considerando que o seguinte apresenta um erro:

 y # NameError: undefined local variable or method `x' for main:Object 

A mesma coisa acontece com a unless case declarações de case .

É por causa de como o analisador de Ruby funciona. Variáveis ​​são definidas pelo analisador, que percorre o código linha por linha, independentemente de ele ser realmente executado.

Uma vez que o analisador vê x = , define a variável local x (com valor nil ) daqui em diante no escopo atual. Como if / a unless / case / for / while não criar um novo escopo, x será definido e estará disponível fora do bloco de códigos. E como o bloco interno nunca é avaliado como a condicional é falsa, x não é atribuído a (e, portanto, é nil ).

Aqui está um exemplo semelhante:

 defined?(x) and x = 0 x #=> nil 

Observe que esta é uma visão geral de alto nível do que acontece e não é necessariamente exatamente como o analisador funciona.

Isso tem a ver com uma peculiaridade das regras de escopo do Ruby.

Em ruby, uma variável undecorated x aparecendo por si só pode ser uma variável local ou uma chamada de método – a gramática não pode dizer qual. Cabe ao analisador descobrir como ele resolve as referências de variables ​​locais. A regra é simples: se uma atribuição a uma variável com o mesmo nome já foi vista no escopo local, a referência é uma variável local e a referência é vinculada a essa variável local. Caso contrário, é uma chamada de método e será visualizada como tal em tempo de execução.

As referências de variables ​​locais no Ruby são otimizadas em pesquisas de matriz (cada variável local é atribuída a um ‘slot’ e as referências de variables ​​locais vinculadas geradas pelo analisador são convertidas em referências de slots). A matriz é inicializada com todos os nil :

 /* initialize local variables */ for (i=0; i < local_size; i++) { *sp++ = Qnil; } 

Portanto, se você se referir a uma variável local que não tenha sido atribuída, por meio de uma referência local vinculada (que só pode acontecer se houver uma atribuição ignorada acima da referência no mesmo escopo local), você obterá nil .

Eu pensei que sua pergunta era interessante, então eu tentei pesquisar e achei isso: eu não entendo o escopo local do ruby

A resposta correta parece ser colocar Jorg.

Vamos ver o que acontece quando você tenta acessar uma variável que não está inicializada:

 NameError: undefined local variable or method `UNDECLAREDVAR' for main:Object 

A exceção declara que não está disponível para avaliar se uma variável ou método. A razão pela qual ele não lança a mesma exceção é porque as variables ​​locais não inicializadas são definidas como nulas. Então puts x é um problema porque o interpretador sabe que x é variável mas não inicializado e não um método.