Ruby 关于 Ruby 中全局变量 $SAFE 的一点疑惑,求解答

cx1981 · 2013年11月12日 · 最后由 skandhas 回复于 2013年11月12日 · 2943 次阅读

最近在读<>,对 p152 中的一个例子有些疑惑,大致代码如下

puts $SAFE
class ERB
  attr_accessor :safe_level
  def result(b = TOPLEVEL_BINDING)
    puts $SAFE
    if(@safe_level)
      proc {
        $SAFE = @safe_level
        puts   $SAFE
        #eval(@src , b, (@filename || '(erb)'),1)
      }.call
    else
      #eval(@src ,b ,(@filename || '(erb)'), 1)
    end
  end
end

erb = ERB.new
erb.safe_level = 3
erb.result
puts $SAFE

运行结果如下:

0
0
3
0

$SAFE 是个全局变量,照理说在 proc 中修改了值,应该是在所有作用域中都生效的啊,为什么在执行完 proc 后,$SAFE 的值又会变回执行 proc 之前的值?

为此我又写了另外一段类似的代码,但是自定义了一个全局变量$SAFE2

$SAFE2 = 0
puts $SAFE2
class ERB
  attr_accessor :safe_level
  def result(b = TOPLEVEL_BINDING)
    puts $SAFE2
    if(@safe_level)
      proc {
        $SAFE2 = @safe_level
        puts   $SAFE2
        #eval(@src , b, (@filename || '(erb)'),1)
      }.call
    else
      #eval(@src ,b ,(@filename || '(erb)'), 1)
    end
  end
end

erb = ERB.new
erb.safe_level = 3
erb.result
puts $SAFE2

运行结果如下

0
0
3
3

这次我自定义了一个全局变量$SAFE2,在 proc 中修改了值之后,是在之后的作用域内也生效了,难道对$SAFE 这个全局变量,Ruby 有什么特殊处理么?

好问题. https://www.ruby-forum.com/topic/100159

AFAIK, that's the desired behavior - the pr proc was created while $SAFE=0 and will be executed in that $SAFE=0 context. That allows you to give a Safe environment access to unsafe things.

好像搞头书 10.5.2 上面有?

所以说要先读《ruby 编程语言》

看书不仔细,在后面的文字里就有说明

#3 楼 @windwiny <>p152 中后面的文字是有说明,只是说$SAFE 在 proc 调用结束后会恢复到原先的值,但是没有说为什么,我想知道的就是为什么而不是只告诉我一个结果

#1 楼 @zgm 双飞燕 10.5.2 上是有关于$SAFE 的介绍,但是他只是说$SAFE 是线程局部的,没有提及和 proc 的关系

刚 google 了下,stackoverflow 上也有人提和我一样的疑问 http://stackoverflow.com/questions/2278454/setting-a-global-within-a-proc/2278495#2278495

因为 $SAFE 关系到安全

@cx1981

大体来说,有两个方面:

  1. Ruby 确实对 $SAFE 做了特殊处理。所谓的特殊处理就是 $SAFE 是一个 Virtual Variable, 所谓 Virtual Variable,就是这个全局变量的 getter, setter 与 普通全局变量的不一样, Virtual Variable 的 getter, setter 是定制过的。类似的 Virtual Variable 还有:$!, $$, $? ...... 等等。
  2. 对于一个 proc,如果它不是通过 define_method 或是 Method#proc 得来的话,那它在 call 的时候会先保存当前 thread 的 safe level,在 call 要结束时会恢复原来的 safe level。
需要 登录 后方可回复, 如果你还没有账号请 注册新账号