为什么我不应该扩展由 Struct.new 初始化的实例?
Why shouldn't I extend an instance initialized by Struct.new?
我们有一个遗留代码库,其中 rubocop 报告了一些我永远无法解决的错误:
Don't extend an instance initialized by Struct.new
.
Extending it introduces a superfluous class level and may also introduce
weird errors if the file is required multiple times.
"a superfluous class level"到底是什么意思,可以引入什么样的"weird errors"?
(问是因为显然我们在过去几年没有遇到任何此类问题。)
多余的class关卡就是这个class到Struct.new
.
Here is the reference更详细的解释和源代码。
pull request on this cop还包含一个有价值的例子:
Person = Struct.new(:first, :last) do
SEPARATOR = ' '.freeze
def name
[first, last].join(SEPARATOR)
end
end
不等同于:
class Person < Struct.new(:first, :last)
SEPARATOR = ' '.freeze
def name
[first, last].join(SEPARATOR)
end
end
前者创建::Person
和::SEPARATOR
,后者创建::Person
和::Person::SEPARATOR
。
我认为不断查找主要被称为“奇怪的错误”。
Struct.new
创建一个匿名 class 恰好是 Struct
:
的子class
s = Struct.new(:foo)
#=> #<Class:0x00007fdbc21a0270>
s.ancestors
#=> [#<Class:0x00007fdbc21a0270>, Struct, Enumerable, Object, Kernel, BasicObject]
您可以将匿名 class 分配给常量以命名它:
Foo = Struct.new(:foo)
#=> Foo
Foo.ancestors
#=> [Foo, Struct, Enumerable, Object, Kernel, BasicObject]
这是创建 Struct
subclass 的常规方法。
另一方面,您的遗留代码似乎包含如下内容:
class Foo < Struct.new(:foo)
end
Struct.new
创建一个匿名的 class (它没有分配给常量)并且 Foo
subclasses 它,结果是:
Foo.ancestors
#=> [Foo, #<Class:0x00007fee94191f38>, Struct, Enumerable, Object, Kernel, BasicObject]
显然,匿名 class 没有任何作用。
就像:
class Bar
end
class Foo < Bar # or Foo = Class.new(Bar)
end
Foo.ancestors
#=> [Foo, Bar, Object, Kernel, BasicObject]
相对于:
class Bar
end
class Foo < Class.new(Bar)
end
Foo.ancestors
#=> [Foo, #<Class:0x00007fdb870e7198>, Bar, Object, Kernel, BasicObject]
在后一个例子中,Class.new(Bar)
返回的匿名 class 没有分配给常量,因此既不使用也不需要。
我们有一个遗留代码库,其中 rubocop 报告了一些我永远无法解决的错误:
Don't extend an instance initialized by
Struct.new
. Extending it introduces a superfluous class level and may also introduce weird errors if the file is required multiple times.
"a superfluous class level"到底是什么意思,可以引入什么样的"weird errors"?
(问是因为显然我们在过去几年没有遇到任何此类问题。)
多余的class关卡就是这个class到Struct.new
.
Here is the reference更详细的解释和源代码。
pull request on this cop还包含一个有价值的例子:
Person = Struct.new(:first, :last) do
SEPARATOR = ' '.freeze
def name
[first, last].join(SEPARATOR)
end
end
不等同于:
class Person < Struct.new(:first, :last)
SEPARATOR = ' '.freeze
def name
[first, last].join(SEPARATOR)
end
end
前者创建::Person
和::SEPARATOR
,后者创建::Person
和::Person::SEPARATOR
。
我认为不断查找主要被称为“奇怪的错误”。
Struct.new
创建一个匿名 class 恰好是 Struct
:
s = Struct.new(:foo)
#=> #<Class:0x00007fdbc21a0270>
s.ancestors
#=> [#<Class:0x00007fdbc21a0270>, Struct, Enumerable, Object, Kernel, BasicObject]
您可以将匿名 class 分配给常量以命名它:
Foo = Struct.new(:foo)
#=> Foo
Foo.ancestors
#=> [Foo, Struct, Enumerable, Object, Kernel, BasicObject]
这是创建 Struct
subclass 的常规方法。
另一方面,您的遗留代码似乎包含如下内容:
class Foo < Struct.new(:foo)
end
Struct.new
创建一个匿名的 class (它没有分配给常量)并且 Foo
subclasses 它,结果是:
Foo.ancestors
#=> [Foo, #<Class:0x00007fee94191f38>, Struct, Enumerable, Object, Kernel, BasicObject]
显然,匿名 class 没有任何作用。
就像:
class Bar
end
class Foo < Bar # or Foo = Class.new(Bar)
end
Foo.ancestors
#=> [Foo, Bar, Object, Kernel, BasicObject]
相对于:
class Bar
end
class Foo < Class.new(Bar)
end
Foo.ancestors
#=> [Foo, #<Class:0x00007fdb870e7198>, Bar, Object, Kernel, BasicObject]
在后一个例子中,Class.new(Bar)
返回的匿名 class 没有分配给常量,因此既不使用也不需要。