如何更改Struct属性的默认值?

根据文档,未设置的Struct属性设置为nil

unset参数默认为nil。

是否可以为特定属性指定默认值?

例如,对于以下Struct

 Struct.new("Person", :name, :happy) 

我希望属性happy默认为true而不是nil 。 我怎样才能做到这一点? 如果我这样做

 Struct.new("Person", :name, :happy = true) 

我明白了

 -:1: syntax error, unexpected '=', expecting ')' Struct.new("Person", :name, :happy = true) ^ -:1: warning: possibly useless use of true in void context 

这也可以通过将Struct创建为子类,并使用默认值覆盖initialize来实现,如下例所示:

 class Person < Struct.new(:name, :happy) def initialize(name, happy=true); super end end 

一方面,这种方法确实导致了一点点样板; 另一方面,它完成了你正在寻找的美好和简洁。

一个副作用(可能是一个好处或烦恼,取决于您的偏好/用例)是您丢失默认为nil的所有属性的默认Struct行为 - 除非您明确地将它们设置为如此。 实际上,除非您将name声明为name=nil否则上面的示例将使name成为必需参数

在@ rintaun的示例之后,您还可以使用Ruby 2+中的关键字参数执行此操作

 A = Struct.new(:a, :b, :c) do def initialize(a:, b: 2, c: 3); super end end A.new # ArgumentError: missing keyword: a A.new a: 1 # => # A.new a: 1, c: 6 # => # 

UPDATE

现在需要编写如下代码才能工作。

 A = Struct.new(:a, :b, :c) do def initialize(a:, b: 2, c: 3) super(a, b, c) end end 

@Linuxios给出了一个覆盖成员查找的答案。 这有几个问题:您无法将成员显式设置为nil,并且每个成员引用都有额外的开销。 在我看来,你真的只想在初始化一个新的struct对象时提供默认值,其中部分成员值提供给::new::[]

这是一个使用附加工厂方法扩展Struct的模块,该方法允许您使用散列描述所需的结构,其中键是成员名称,以及在初始化时未提供时默认填充的值:

 # Extend stdlib Struct with a factory method Struct::with_defaults # to allow StructClasses to be defined so omitted members of new structs # are initialized to a default instead of nil module StructWithDefaults # makes a new StructClass specified by spec hash. # keys are member names, values are defaults when not supplied to new # # examples: # MyStruct = Struct.with_defaults( a: 1, b: 2, c: 'xyz' ) # MyStruct.new #=> # # # MyStruct[-10, 3.5] #=> # def with_defaults(*spec) new_args = [] new_args << spec.shift if spec.size > 1 spec = spec.first raise ArgumentError, "expected Hash, got #{spec.class}" unless spec.is_a? Hash new_args.concat spec.keys new(*new_args) do class << self attr_reader :defaults end def initialize(*args) super self.class.defaults.drop(args.size).each {|k,v| self[k] = v } end end.tap {|s| s.instance_variable_set(:@defaults, spec.dup.freeze) } end end Struct.extend StructWithDefaults 

我也发现了这个:

 Person = Struct.new "Person", :name, :happy do def initialize(*) super self.location ||= true end end 

我认为#initialize方法的覆盖是最好的方法,调用#super(*required_args)

这具有能够使用散列样式参数的额外优点。 请参阅以下完整和编译示例:

散列样式参数,默认值和Ruby结构

 # This example demonstrates how to create Ruby Structs that use # newer hash-style parameters, as well as the default values for # some of the parameters, without loosing the benefits of struct's # implementation of #eql? #hash, #to_s, #inspect, and other # useful instance methods. # # Run this file as follows # # > gem install rspec # > rspec struct_optional_arguments.rb --format documentation # class StructWithOptionals < Struct.new( :encrypted_data, :cipher_name, :iv, :salt, :version ) VERSION = '1.0.1' def initialize( encrypted_data:, cipher_name:, iv: nil, salt: 'salty', version: VERSION ) super(encrypted_data, cipher_name, iv, salt, version) end end require 'rspec' RSpec.describe StructWithOptionals do let(:struct) { StructWithOptionals.new(encrypted_data: 'data', cipher_name: 'AES-256-CBC', iv: 'intravenous') } it 'should be initialized with default values' do expect(struct.version).to be(StructWithOptionals::VERSION) end context 'all fields must be not null' do %i(encrypted_data cipher_name salt iv version).each do |field| subject { struct.send(field) } it field do expect(subject).to_not be_nil end end end end 

你可以用单身人士做到这一点:

 Person = Struct.new("Person", :name, :happy) do @@defaults = {:happy => true} @@vars = [:name, :happy] def [](i) return super if(super) return @@defaults[i] if(@@defaults[i]) return nil end @@vars.each do |v| define_method(v) {return self[v]} end end