如果没有使用`rescue`作为代码块,Ruby的`begin ... end`是否会产生意想不到的后果?

时间:2012-11-07 22:16:47

标签: ruby

我偶尔会看到红宝石中使用的begin...end块,其间没有任何rescueelseensure等语句。例如:

foo = begin
   whatever = 3
   "great"
   42
end

编码人员的意图似乎是仅使用begin...end块来表示其块分组质量(就好像begindo)。就个人而言,我认为这种用法违反了最少惊喜的原则(begin意味着对我的异常处理)。

以这种方式使用begin...end是否会产生任何意外后果? begin...end块是否存在任何语义差异(可能在异常处理中?),这会使这种使用变得危险吗?

Ruby的语法令人难以置信的微妙,如果在这里等待奇怪的陷阱,我不会感到惊讶。

1 个答案:

答案 0 :(得分:33)

我有时会使用它,如果我想为变量赋值,但我必须首先计算我想要分配的值。它使代码更加整洁。我认为这是用户偏好。基本上你说的是:我正在为foo分配一些东西,但为了获得我想要的价值,我首先需要做一些事情。它在进行记忆时特别有用,因此不是

if @cache.nil?
  do_something!
  @cache = read_value
end

你可以做到

@cache ||= begin
  do_something!
  read_value
end

你在这里利用的是Ruby解释器有一个堆栈,每个表达式通常会在堆栈上推送一些东西,或从堆栈中取出一些东西。赋值只需从堆栈中取出最后一个并分配它(在本例中为开始/结束的最后一行)。很多时候知道这一点(Ruby中的堆栈方法)可能很有用。

我认为这并不是最不令人惊讶的,我认为这是用户偏好,而不是你想要使用它。

通过查看它在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]

Trace仅适用于堆栈跟踪,您可以忽略它。 Dup复制堆栈中的最后一项。在此示例中,局部变量a的编号为2,局部变量c的编号为3(因此putobject, 2将分配给变量{ {1}}等)。 与a相比,此唯一的副作用是a = 5; c = 6指令,这意味着您的方法的堆栈大小将增加1个插槽。但这并不是特别重要,因为它只有在解释器在这个特定方法中时才有效,并且堆栈的内存无论如何都是预先保留的,所以它只意味着堆栈指针将比其它方式减少1。所以基本上没有变化。启用优化后,即使dup可能会消失。