hacer - ¿Hay consecuencias no intencionales de que Ruby ''comience... final'' sin ''rescate'' usado como bloque de código?
como hacer comentarios en ruby (1)
Ocasionalmente veo begin...end
bloques begin...end
usados en ruby sin ningún tipo de rescue
, de lo else
, ensure
, etc. Por ejemplo:
foo = begin
whatever = 3
"great"
42
end
La intención del codificador, al parecer, es usar el bloque begin...end
solo por su calidad de agrupación de bloques (como si begin
fuera do
). Personalmente creo que este uso viola el principio de menos sorpresa ( begin
implica un manejo de excepciones para mí).
¿Hay alguna consecuencia involuntaria del uso de begin...end
de esta manera? ¿Los bloques de begin...end
tienen diferencias semánticas (tal vez en el manejo de excepciones?) Que hacen que este uso sea peligroso?
La sintaxis de Ruby es increíblemente sutil, y no me sorprendería si hubiera extrañas trampas en espera aquí.
Utilizo esto a veces si quiero asignar algo a una variable pero tengo que calcular el valor que quiero asignar primero. Hace el código un poco más ordenado de esta manera. Creo que es la preferencia del usuario. Básicamente estás diciendo: le estoy asignando algo a foo, pero para obtener el valor que quiero, primero necesito hacer algunas cosas. Es particularmente útil cuando se hace una memoria, así que en lugar de
if @cache.nil?
do_something!
@cache = read_value
end
Tu puedes hacer
@cache ||= begin
do_something!
read_value
end
Lo que está aprovechando aquí es que el intérprete de Ruby tiene una pila, y cada expresión usualmente empuja algo en la pila, o toma algo de la pila. La asignación simplemente toma lo último de la pila y lo asigna (en este caso, la última línea desde principio / final). Muchas veces saber esto (enfoque de pila en Ruby) puede ser útil.
Sin embargo, no creo que viole la menor sorpresa, creo que es la preferencia del usuario si quieres usarlo o no.
Puede ver que no hace nada inesperado al observar qué instrucciones de código de bytes genera en Ruby MRI 1.9:
RubyVM::InstructionSequence::compile("c = begin; a = 5; 6; end").to_a
[:trace, 1],
[:trace, 1],
[:putobject, 5],
[:setlocal, 2],
[:trace, 1],
[:putobject, 6],
[:dup],
[:setlocal, 3],
[:leave]
La traza es solo para trazas de pila, puede ignorar eso. Dup duplica el último elemento de la pila. En este ejemplo, el número de la variable local a
es 2
y el número de la variable local c
es 3
(por putobject, 2
tanto putobject, 2
asignará a la variable a
, etc.). El único efecto secundario de esto comparado con a = 5; c = 6
a = 5; c = 6
es la instrucción dup
, lo que significa que el tamaño de pila de su método será mayor en 1 espacio. Pero esto no es particularmente importante porque solo tiene efecto mientras el intérprete está dentro de este método en particular, y la memoria para la pila está reservada de todos modos, por lo que solo significa que el puntero de pila se reducirá en 1 más de lo que sería de otra manera. Así que básicamente no hay cambio en absoluto. Con las optimizaciones activadas, incluso el dup
probablemente desaparecerá.