Project

General

Profile

Actions

Feature #16806

closed

Struct#initialize accepts keyword arguments too by default

Added by k0kubun (Takashi Kokubun) almost 4 years ago. Updated about 1 year ago.

Status:
Closed
Target version:
[ruby-core:98014]

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"}>
  • 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


Related issues 1 (0 open1 closed)

Related to Ruby master - Feature #11925: Struct construction with kwargsClosedActions
Actions #1

Updated by k0kubun (Takashi Kokubun) almost 4 years ago

Actions #2

Updated by k0kubun (Takashi Kokubun) almost 4 years ago

  • Description updated (diff)

Updated by jeremyevans0 (Jeremy Evans) almost 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) almost 4 years ago

  • Description updated (diff)

I agreed with your ideas and reflected them to the description.

Updated by matz (Yukihiro Matsumoto) about 3 years ago

I am OK with 3.1 to warn, 3.2 to change.

Matz.

Actions #6

Updated by k0kubun (Takashi Kokubun) about 3 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) about 3 years ago

  • Status changed from Closed to Assigned
  • Assignee set to k0kubun (Takashi Kokubun)

Updated by k0kubun (Takashi Kokubun) about 2 years ago

  • Status changed from Assigned to Closed

@nobu (Nobuyoshi Nakada) already committed the last step at c956f979e5d05900315d2753d5c3b1389af8dae4. Closing.

Actions #9

Updated by Eregon (Benoit Daloze) about 1 year ago

  • Target version set to 3.2
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0