覆盖散列并使 [] 运算符私有 - 不能再使用 ||=
Overriding a hash and making [] operators private - cant use ||= anymore
测试代码:
class PrivHash < Hash
def set(key, val)
self[key] = val
end
def set_maybe(key, val)
self[key] ||= val
end
private
def []= key, value
end
def [] key
super
end
end
使用此代码,我希望 set
和 set_maybe
都能正常工作。但是,只有 set
有效,而 set_maybe
失败:
[30] pry(#<TranslationInfo>):1> ph.set_maybe(:a, 1)
NoMethodError: private method `[]' called for {:a=>2}:#Class:0x007f99c5924c38>::PrivHash
from (pry):56:in `set_maybe'
我假设 self[:b] ||= <x>
只是 self[:b] || self[:b] = <x>
的语法糖,但我想这不是因为它有效。
让我感到困扰的是为什么我会收到此错误。我是从 class 中执行的,所以为什么我会收到私有方法错误?
我试着反编译了它。
code = <<CODE
class PrivHash < Hash
def set(key, val)
self[key] = val
end
def set_maybe(key, val)
self[key] ||= val
end
private
def []= key, value
end
def [] key
super
end
end
CODE
disasm = RubyVM::InstructionSequence.compile(code).disasm
File.write("#{RUBY_VERSION}.txt", disasm)
根据结果,我得出的结论是:2.2.0 调用
0010 opt_aref <callinfo!mid:[], argc:1, FCALL|ARGS_SIMPLE>
...
0013 branchif 25
...
0020 opt_aset <callinfo!mid:[]=, argc:2, FCALL|ARGS_SIMPLE>
基本上,评估[]
,看看它是否为假,如果是,则调用[]=
。但是 2.3.0 没有在 []
调用中使用 FCALL
标志:
0010 opt_aref <callinfo!mid:[], argc:1, ARGS_SIMPLE>, <callcache>
...
0014 branchif 27
...
0021 opt_aset <callinfo!mid:[]=, argc:2, FCALL|ARGS_SIMPLE>, <callcache>
FCALL
标志标识带有隐式接收器的调用 (foo()
);没有 FCALL
,调用是对显式接收者(self.foo()
或 bar.foo()
)的调用,这是 private
方法所禁止的。
现在,为什么 2.3.0 会这样做...不知道。
I assumed that self[:b] ||= <x>
is just syntactic sugar for self[:b] || self[:b] = <x>
是的。但这并不意味着它会像预处理器一样重写代码。它在 Ruby 核心代码中进行了扩展,我想这不受允许使用私有方法显式 self
的规则的约束。也许你可以再看看that feature。
目前私有方法的处理有点乱。
原始规则是:
private methods can only be called without an explicit receiver.
这是一个很好、简单、易于理解的规则。它也是一个 static 规则,即它可以在没有 运行 代码的情况下进行检查,实际上,它甚至是一个 syntactic 规则,它甚至不需要复杂的静态分析,它可以在解析器中检查。
然而,很快就注意到这条规则使得调用私有 setters 变得不可能,因为 setters 不能在没有显式接收者的情况下被调用(foo = bar
是a 设置局部变量,而不是调用 setter)。因此,规则已扩展:
private methods can only be called without an explicit receiver, unless the method call is an assignment method call, in which case the method can also be called with an explicit receiver as long as that explicit receiver is the literal pseudo-variable self
.
这允许您使用文字值 self
:
的显式接收者调用私有 setters
self.foo = bar
但不是 self
的动态值
baz = self
baz.foo = bar # NoMethodError: private method `foo=' called
这仍然保留了 属性 可以在解析时检测到私有方法调用。
两年前,I filed a bug关于缩写方法赋值不起作用,即:
self.foo += bar # NoMethodError
通过再次扩展私有方法调用的规则修复了该错误(现在规则已经变得如此复杂以至于我不打算详细说明)。
但是,仍然有很多情况没有被现有规则涵盖,在这些情况下,方法在没有显式接收者的情况下根本无法在句法上被调用,因此不能是私有的:
self[foo]
!self
self + foo
等等
有些已经修复,有些还没有。问题是规则现在变得如此复杂以至于很难正确实施。 There have been proposals to change the rule 像这样:
private methods can only be called without an explicit receiver or an explicit receiver which is the literal pseudo-variable self
.
这是一个很好、简单、易于理解的规则,可以在解析时进行静态检查,并且有 none 我们目前拥有的复杂异常和极端情况。但是,它尚未实施 AFAIK。
测试代码:
class PrivHash < Hash
def set(key, val)
self[key] = val
end
def set_maybe(key, val)
self[key] ||= val
end
private
def []= key, value
end
def [] key
super
end
end
使用此代码,我希望 set
和 set_maybe
都能正常工作。但是,只有 set
有效,而 set_maybe
失败:
[30] pry(#<TranslationInfo>):1> ph.set_maybe(:a, 1)
NoMethodError: private method `[]' called for {:a=>2}:#Class:0x007f99c5924c38>::PrivHash
from (pry):56:in `set_maybe'
我假设 self[:b] ||= <x>
只是 self[:b] || self[:b] = <x>
的语法糖,但我想这不是因为它有效。
让我感到困扰的是为什么我会收到此错误。我是从 class 中执行的,所以为什么我会收到私有方法错误?
我试着反编译了它。
code = <<CODE
class PrivHash < Hash
def set(key, val)
self[key] = val
end
def set_maybe(key, val)
self[key] ||= val
end
private
def []= key, value
end
def [] key
super
end
end
CODE
disasm = RubyVM::InstructionSequence.compile(code).disasm
File.write("#{RUBY_VERSION}.txt", disasm)
根据结果,我得出的结论是:2.2.0 调用
0010 opt_aref <callinfo!mid:[], argc:1, FCALL|ARGS_SIMPLE>
...
0013 branchif 25
...
0020 opt_aset <callinfo!mid:[]=, argc:2, FCALL|ARGS_SIMPLE>
基本上,评估[]
,看看它是否为假,如果是,则调用[]=
。但是 2.3.0 没有在 []
调用中使用 FCALL
标志:
0010 opt_aref <callinfo!mid:[], argc:1, ARGS_SIMPLE>, <callcache>
...
0014 branchif 27
...
0021 opt_aset <callinfo!mid:[]=, argc:2, FCALL|ARGS_SIMPLE>, <callcache>
FCALL
标志标识带有隐式接收器的调用 (foo()
);没有 FCALL
,调用是对显式接收者(self.foo()
或 bar.foo()
)的调用,这是 private
方法所禁止的。
现在,为什么 2.3.0 会这样做...不知道。
I assumed that
self[:b] ||= <x>
is just syntactic sugar forself[:b] || self[:b] = <x>
是的。但这并不意味着它会像预处理器一样重写代码。它在 Ruby 核心代码中进行了扩展,我想这不受允许使用私有方法显式 self
的规则的约束。也许你可以再看看that feature。
目前私有方法的处理有点乱。
原始规则是:
private methods can only be called without an explicit receiver.
这是一个很好、简单、易于理解的规则。它也是一个 static 规则,即它可以在没有 运行 代码的情况下进行检查,实际上,它甚至是一个 syntactic 规则,它甚至不需要复杂的静态分析,它可以在解析器中检查。
然而,很快就注意到这条规则使得调用私有 setters 变得不可能,因为 setters 不能在没有显式接收者的情况下被调用(foo = bar
是a 设置局部变量,而不是调用 setter)。因此,规则已扩展:
private methods can only be called without an explicit receiver, unless the method call is an assignment method call, in which case the method can also be called with an explicit receiver as long as that explicit receiver is the literal pseudo-variable
self
.
这允许您使用文字值 self
:
self.foo = bar
但不是 self
baz = self
baz.foo = bar # NoMethodError: private method `foo=' called
这仍然保留了 属性 可以在解析时检测到私有方法调用。
两年前,I filed a bug关于缩写方法赋值不起作用,即:
self.foo += bar # NoMethodError
通过再次扩展私有方法调用的规则修复了该错误(现在规则已经变得如此复杂以至于我不打算详细说明)。
但是,仍然有很多情况没有被现有规则涵盖,在这些情况下,方法在没有显式接收者的情况下根本无法在句法上被调用,因此不能是私有的:
self[foo]
!self
self + foo
等等
有些已经修复,有些还没有。问题是规则现在变得如此复杂以至于很难正确实施。 There have been proposals to change the rule 像这样:
private methods can only be called without an explicit receiver or an explicit receiver which is the literal pseudo-variable
self
.
这是一个很好、简单、易于理解的规则,可以在解析时进行静态检查,并且有 none 我们目前拥有的复杂异常和极端情况。但是,它尚未实施 AFAIK。