Feature #16806
closedStruct#initialize accepts keyword arguments too by default
Description
Proposal¶
Post = Struct.new(:id, :name)
# In addition to this,
Post.new(1, "hello") #=> #<struct Post id=1, name="hello">
# Let the following initialization also work
Post.new(id: 1, name: "hello") #=> #<struct Post id=1, name="hello">
Known incompatibility¶
-
Post.new(id: 1, name: "hello")
will be#<struct Post id=1, name="hello">
instead of#<struct Post id={:id=>1, :name=>"hello"}, name=nil>
- Struct initialization only using keyword arguments should be warned in Ruby 3.0. This feature should be introduced in Ruby 3.1 or later.
Edge cases¶
- When keyword arguments and positional arguments are mixed:
Post.new(1, name: "hello")
- This should continue to work like Ruby 2:
#<struct Post id=1, name={:name="hello"}>
- This should continue to work like Ruby 2:
- Only keywords are given but they include an invalid member:
Post.new(foo: "bar")
- ArgumentError (unknown keywords: foo)
- When
keyword_init
is used- nil: default behavior. Positional arguments given use positional init. Keyword arguments without positional arguments treated as positional in 3.0 with warning, and treated as keyword init in Ruby 3.1.
- true: Require keyword init, disallow positional init.
- false: Treat keywords as positional hash.
Use cases¶
- Simplify a struct definition where [Feature #11925] is used.
- When we introduced [Feature #11925], @mame (Yusuke Endoh) thought we don't need
keyword_init: true
once keyword args are separated (https://docs.google.com/document/d/1XbUbch8_eTqh21FOwj9a_X-ZyJyCBjxkq8rWwfpf5BM/edit#). That's what this ticket is all about.- However, the keyword arguments separation was done differently from what we expected at the moment. So we need to deal with the "Known incompatibility".
- Matz objected to having a new keyword argument (
immutable: true
) inStruct.new
at https://bugs.ruby-lang.org/issues/16769#note-8. Sokeyword_init: true
seems also against Ruby's design. Now we should be able to skip specifying the option for consistency in the language design.
- When we introduced [Feature #11925], @mame (Yusuke Endoh) thought we don't need
Updated by k0kubun (Takashi Kokubun) over 4 years ago
- Related to Feature #11925: Struct construction with kwargs added
Updated by k0kubun (Takashi Kokubun) over 4 years ago
- Description updated (diff)
Updated by jeremyevans0 (Jeremy Evans) over 4 years ago
I'm OK with the basic idea of allowing keyword init for Structs by default. However, because this changes behavior, I think we should keep existing behavior and warn in Ruby 3.0 and not make this change until Ruby 3.1. I think this change should only affect cases where keywords are passed without positional arguments during Struct initialization.
For Post.new(foo: "bar")
, this should still be treated as keyword init, even though foo is not a member of Post. In Ruby 3.0, that would warn and use {:foo=>"bar"}
as the first member of Post. In Ruby 3.1, that would raise ArgumentError, since foo is not a member of Post and the keywords will be treated as named members.
For Post.new(1, foo: "bar")
, Post.new(1, name: "hello")
, Post.new(1, id: 1)
: I think these should be treated the same as Ruby 2. Any non-keyword argument should treat keywords as a positional hash argument. I think allowing mixing of the arguments would result in confusion, brings up additional edge cases, and there is no practical use case for it.
Do we want to support Post = Struct.new(:id, :name, keyword_init: false)
to disallow keyword initialization and always treat keywords as a positional hash? I think we should as that is the intent of the code. Basically, keyword_init
would allow 3 values:
- nil: default behavior. Positional arguments given use positional init. Keyword arguments without positional arguments treated as positional in 3.0 with warning, and treated as keyword init in Ruby 3.1.
- true: Require keyword init, disallow positional init.
- false: Treat keywords as positional hash.
Updated by k0kubun (Takashi Kokubun) over 4 years ago
- Description updated (diff)
I agreed with your ideas and reflected them to the description.
Updated by matz (Yukihiro Matsumoto) almost 4 years ago
I am OK with 3.1 to warn, 3.2 to change.
Matz.
Updated by k0kubun (Takashi Kokubun) almost 4 years ago
- Status changed from Open to Closed
Applied in changeset git|8d099aa040427aede04e42c3ec9380afd431ffe3.
Warn Struct#initialize with only keyword args (#4070)
- Warn Struct#initialize with only keyword args
A part of [Feature #16806]
- Do not warn if
keyword_init: false
is explicitly specified
-
Add a NEWS entry
-
s/in/from/
-
Make sure all fields are initialized
Updated by k0kubun (Takashi Kokubun) almost 4 years ago
- Status changed from Closed to Assigned
- Assignee set to k0kubun (Takashi Kokubun)
Updated by k0kubun (Takashi Kokubun) almost 3 years ago
- Status changed from Assigned to Closed
@nobu (Nobuyoshi Nakada) already committed the last step at c956f979e5d05900315d2753d5c3b1389af8dae4. Closing.