🙆‍♀️

【Ruby 3.4 Advent Calender】`**nil` が `**{}` と同等に扱われるようになる【2日目】

2024/12/02に公開

Ruby 3.4 Advent Calender 2日目の記事です。

これはなに

今年 2024年12月25日にリリースされる予定の Ruby 3.4 の新機能や変更点などを1つずつ紹介していく Advent Calender になります。
基本的には NEWS に載っている機能を紹介すると思うんですがここにない機能についても書くかもしれません。
また、記事を書いてる時点ではまだ Ruby 3.4 はリリースされる前なので Ruby 3.4 がリリースされた時点で機能が変わっている 可能性があるかもしれないので注意してください。
記事のまとめは ここを参照 してください。

**nil がサポートされた

** は『 Hash オブジェクトをキーワード引数として渡す』ような用途で利用します。

User = Data.define(:name, :age)

def create_user(name:, age:)
  User.new(name:, age:)
end

users = [
  { name: "homu", age: 14 },
  { name: "mami", age: 15 }
]
data = { name: "homu", age: 14 }

# create_user(name: "homu", age: 14) と同等の呼び出しになる
pp create_user(**data)
# => #<data User name="homu", age=14>

これが Ruby 3.4 からは **nil**{} と同等に扱われるようになります。

def any_method(default: nil)
  { default: }
end

pp any_method(**{ default: false })   # => {:default=>false}

# キーワード引数は何も渡されない
pp any_method(**{})    # => {:default=>nil}
pp any_method(**nil)   # => {:default=>nil}

これにより例えば次のようなコードが簡潔にかけるようになります。

User.create(
  name: "homu",
  age: 14,
  # params に :user_options がない場合でも動作するようになる
  # 今までは **(params[:user_options] || {}) みたいに書く必要があた
  **params[:user_options]
)

**nil を許容する提案は割と前から話が上がっていたので Ruby 3.4 でサポートされたのは個人的にも嬉しい。

余談: NilClass#to_hash は依然として未定義

**obj に関連する話として #to_hash メソッドがあります。
**obj では内部で #to_hash メソッドを呼び、その戻り値の Hash に対して ** を処理します。

class X
  def to_hash
    { x: "test" }
  end
end

# 内部で X#to_hash が呼び出されて変換される
pp **X.new
# => {:x=>"test"}

なので Ruby 3.4 以前でも NilClass#to_hash が定義されていれば **nil が使用できます。

pp RUBY_VERSION # => "3.3.6"

class NilClass
  def to_hash = {}
end

# **nil は **{} と同等になる
pp(a: 42, **nil)
# => {:a=>42}

ただし、今回の Ruby 3.4 の **nil のサポートは NilClass#to_hash メソッドが追加されたわけではないので注意する必要があります。

pp RUBY_VERSION # => "3.4.0"

# 3.4 でも NilClass#to_hash が使えるようになったわけではない
# error: undefined method 'to_hash' for nil (NoMethodError)
nil.to_hash

関連

GitHubで編集を提案

Discussion