😎

Ruby の Struct と Data でメンバーを渡さなかった時の挙動の違い

に公開

こんな違いがあったんだ、っていうのを知らなかったので覚書。
StructData は値オブジェクトを簡単に定義するためのクラスです。

# Struct 版
User = Struct.new(:name, :age)

homu = User.new(name: "homu", age: 14)

pp homu
# => #<struct User name="homu", age=14>
# Data
User = Data.define(:name, :age)

homu = User.new(name: "homu", age: 14)

pp homu
# => #<data User name="homu", age=14>

上記のように使い方はほぼ同じ形ですが Struct は mutable で Data は immutable という大きな違いがあります。

# Struct 版
User = Struct.new(:name, :age)

homu = User.new(name: "homu", age: 14)

# Struct で定義したオブジェクトは値を書き換えることができる
homu.name = "mami"

pp homu
# => #<struct User name="mami", age=14>
# Data
User = Data.define(:name, :age)

homu = User.new(name: "homu", age: 14)

# Data で定義したオブジェクトは値を書き換えられない
homu.name = "mami"
# => error: undefined method 'name=' for an instance of User (NoMethodError)

他にも利用できるメソッドが違ったりするんですが今回はそこではない差異の話です。

定義したメンバーを .new に渡さなかった場合の差異

Struct の場合、定義したクラスのインスタンスオブジェクトを生成する場合にメンバーの値を渡さなければ nil として初期値が割り当てられます。

# Struct 版
User = Struct.new(:name, :age)

# メンバーの引数を渡さない
homu = User.new(name: "homu")

# 渡さなかったメンバーは `nil` になる
pp homu
# => #<struct User name="homu", age=nil>

一方で Data の場合は User.new に必要なメンバーがなければ nil になります。

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

# メンバーの引数を渡さないとエラーになる
homu = User.new(name: "homu")
# => error: missing keyword: :age (ArgumentError)

この違いは知らなかった。
User.new(**user_data) みたいな使い方をする場合は user_data にすべてのメンバーが存在する必要があるので微妙に注意する必要がありそうですね。

GitHubで編集を提案

Discussion