Feature #11925
closedStruct construction with kwargs
Description
Propose to make Struct subclass constructors which accept keyword arguments. Not sure, if it's reasonable to allow .new accept kwargs, so may be should use different method named like .create:
  Point = Struct.new(:x,:y, :color)
  pt_1 = Point.create(x: 1, y: 2) # => Point<x: 1, y: 2, color: nil>
  pt_2 = Point.create!(x: 1, y: 2) # => ArgumentError, color not specified.
It will greatly simplify work with big structures, especially in cases when struct layout changes and for cases when structure can have lots of non-significant values. It also allows simpler ways to use implement default values for struct members.
        
           Updated by ksss (Yuki Kurihara) almost 10 years ago
          Updated by ksss (Yuki Kurihara) almost 10 years ago
          
          
        
        
      
      Hi.
I have thought the same thing that want to use kwargs in Struct class.
But I can't come up with a good API.
Finally, I made a gem. https://github.com/ksss/type_struct
        
           Updated by naruse (Yui NARUSE) over 9 years ago
          Updated by naruse (Yui NARUSE) over 9 years ago
          
          
        
        
      
      - Assignee deleted (core)
        
           Updated by shyouhei (Shyouhei Urabe) over 9 years ago
          Updated by shyouhei (Shyouhei Urabe) over 9 years ago
          
          
        
        
      
      We looked at this issue in yesterday's developer meeting.  Nobody there was against the functionality -- but the name.  create! doesn't sound appropriate at all.  create also not that obvious for non-English speakers like us that it expects keywords.
        
           Updated by herwinw (Herwin Quarantainenet) about 9 years ago
          Updated by herwinw (Herwin Quarantainenet) about 9 years ago
          
          
        
        
      
      What about new_from_kwargs(**kwargs) ? It's a bit long, but it describes the functionality exactly.
        
           Updated by herwin (Herwin W) about 9 years ago
          Updated by herwin (Herwin W) about 9 years ago
          
          
        
        
      
      https://github.com/ruby/ruby/pull/1468
A proposal for an implementation.
irb(main):001:0> MyClass = Struct.new(:a, :b, :c)
=> MyClass
irb(main):002:0> MyClass.new_from_kwargs(a:1, c: 3)
=> #<struct MyClass a=1, b=nil, c=3>
irb(main):003:0> MyClass.new_from_kwargs(1, 2, 3, b: 3)
=> #<struct MyClass a=1, b=3, c=3>
irb(main):004:0> MyClass.new_from_kwargs(d: 4)
NameError: no member 'd' in struct
	from (irb):4:in `new_from_kwargs'
	from (irb):4
	from ./irb:11:in `<main>'
irb(main):005:0> MyClass.new_from_kwargs(1, 2, 4, 5, b: 3)
IndexError: offset 3 too large for struct(size:3)
	from (irb):5:in `new_from_kwargs'
	from (irb):5
	from ./irb:11:in `<main>'
irb(main):006:0> 
        
           Updated by nobu (Nobuyoshi Nakada) about 9 years ago
          Updated by nobu (Nobuyoshi Nakada) about 9 years ago
          
          
        
        
      
      Herwin W wrote:
irb(main):003:0> MyClass.new_from_kwargs(1, 2, 3, b: 3) => #<struct MyClass a=1, b=3, c=3>
Why does new_from_kwargs accept other than keyword arguments?
        
           Updated by herwin (Herwin W) about 9 years ago
          Updated by herwin (Herwin W) about 9 years ago
          
          
        
        
      
      To be prepared for "the great unification of constructors" of course.
It looked like a pretty logical step to support while I was coding this. It's also pretty easy to remove again
        
           Updated by shyouhei (Shyouhei Urabe) almost 9 years ago
          Updated by shyouhei (Shyouhei Urabe) almost 9 years ago
          
          
        
        
      
      - Has duplicate Feature #9209: Struct instances creatable with named args added
        
           Updated by ko1 (Koichi Sasada) almost 9 years ago
          Updated by ko1 (Koichi Sasada) almost 9 years ago
          
          
        
        
      
      Another idea is introducing another method to define own struct, such as T = Struct.define(:a, :b); T.new(a: 1, b: 2) and so on.
(just idea) Moreover we can extend Struct  with some properties, like: Struct.define(:a, b: :read_only).
These ideas are provided by another person.
        
           Updated by nobu (Nobuyoshi Nakada) over 8 years ago
          Updated by nobu (Nobuyoshi Nakada) over 8 years ago
          
          
        
        
      
      - Has duplicate Feature #13272: Keyword argument to instantiate a subclass of Struct added
        
           Updated by k0kubun (Takashi Kokubun) almost 8 years ago
          Updated by k0kubun (Takashi Kokubun) almost 8 years ago
          
          
        
        
      
      Similar to one commented by ko1, how about this interface?
T = Struct.new(:a, :b, keyword_argument: true)
T.new(a: 1, b: 2)
As keyword_argument is long, another option is:
Struct.new(:a, :b, keyword_args: true)
        
           Updated by k0kubun (Takashi Kokubun) almost 8 years ago
          Updated by k0kubun (Takashi Kokubun) almost 8 years ago
          
          
        
        
      
      In case that my suggestion of the name is accepted, I wrote a patch for Struct.new(:a, :b, keyword_args: true).
https://github.com/ruby/ruby/pull/1771
        
           Updated by knu (Akinori MUSHA) almost 8 years ago
          Updated by knu (Akinori MUSHA) almost 8 years ago
          
          
        
        
      
      What if Struct.new([:a, :b]) created a class with the desired constructor?
        
           Updated by matz (Yukihiro Matsumoto) almost 8 years ago
          Updated by matz (Yukihiro Matsumoto) almost 8 years ago
          
          
        
        
      
      I vote for the keyword argument (e.g. keyword_init:) to Struct#new.
Matz.
        
           Updated by herwin (Herwin W) almost 8 years ago
          Updated by herwin (Herwin W) almost 8 years ago
          
          
        
        
      
      knu (Akinori MUSHA) wrote:
What if
Struct.new([:a, :b])created a class with the desired constructor?
If you'd compare the two possible constructors:
Struct.new(:a, :b)
Struct.new([:a, :b])
There is nothing in the second one that would indicate the second creates a keyword constructor. If I hadn't read this discussion, I would just expect them to behave the same.
        
           Updated by k0kubun (Takashi Kokubun) almost 8 years ago
          Updated by k0kubun (Takashi Kokubun) almost 8 years ago
          
          
        
        
      
      - Status changed from Open to Closed
Applied in changeset trunk|r61137.
struct.c: add keyword_init option to Struct.new
to initialize struct with keyword arguments.
[Feature #11925] [close GH-1771]
        
           Updated by k0kubun (Takashi Kokubun) almost 8 years ago
          Updated by k0kubun (Takashi Kokubun) almost 8 years ago
          
          
        
        
      
      As Matz approved, I committed only keyword_init option which is equivalent to "Point.create(x: 1, y: 2)" in original suggestion. If you still want "Point.create!" version which raises ArgumentError (keyword_init initializes unspecified fields with nil), please file another ticket.
        
           Updated by nobu (Nobuyoshi Nakada) about 7 years ago
          Updated by nobu (Nobuyoshi Nakada) about 7 years ago
          
          
        
        
      
      - Related to Feature #15076: Struct to raise error when keyword arguments used but not enabled added
        
           Updated by k0kubun (Takashi Kokubun) about 7 years ago
          Updated by k0kubun (Takashi Kokubun) about 7 years ago
          
          
        
        
      
      - Related to Feature #15222: Add a way to distinguish between Struct classes with and without keyword initializer added
        
           Updated by k0kubun (Takashi Kokubun) over 5 years ago
          Updated by k0kubun (Takashi Kokubun) over 5 years ago
          
          
        
        
      
      - Related to Feature #16806: Struct#initialize accepts keyword arguments too by default added