Ruby の Data と Struct の違い

2024/01/26に公開

Ruby 3.2 で新たに Data クラスが追加されました。
これは既存の Struct クラスと似た機能になるんですが少し異なる部分もあるのでまとめました。

定義メソッドが異なる

Struct では .new メソッドで新しくクラスオブジェクトを定義するんですが Data.define で定義します。

# .new で User クラスを定義する
User = Struct.new(:name, :age)

# .define で Blog クラスを定義する
Blog = Data.define(:ttile, :owner)

Data.define ではいくつかのメソッドがオミットされている

Struct.new では Enumerable を mix していたり Hash ぽいメソッドがいくつか定義されています。

User = Struct.new(:id, :name, :age)
pp User.ancestors
# => [User, Struct, Enumerable, Object, Kernel, BasicObject]

homu = User.new(1, "homu", 14)

# 値だけ抽出する
pp homu.values
# => [1, "homu", 14]

# String の値だけ抽出する
pp homu.select { String === _1 }

しかし Data では Enumerable は mixin されておらず #values のようなメソッドも定義されていません。

User = Data.define(:id, :name, :age)
pp User.ancestors
# => [User, Data, Object, Kernel, BasicObject]

homu = User.new(1, "homu", 14)

# イテレーションしたい場合は #to_h などを経由してアクセスする
pp homu.to_h.values
pp homu.to_h.values.select { String === _1 }

Data.define ではクラス名を指定できない

Struct.new の第一引数に文字列を渡すことでその名前で Struct の内部クラスとして定義されます。

user = Struct.new("User", :name, :age)
pp user.name
# => "Struct::User"

# クラスを直接参照する事もできる
pp Struct::User

ただし、Data にはこの機能はありません。

# この場合は単純に "User" という名前のメンバーが定義されるだけ
user = Data.define("User", :name, :age)
pp user.members
# => [:User, :name, :age]

一旦こんなところですかね?細かいところで他にもあるかも。

GitHubで編集を提案

Discussion