Upgrade to Pro — share decks privately, control downloads, hide ads and more …

今更聞けない! Struct の使い方と今後の可能性について

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for osyo osyo
September 12, 2020

今更聞けない! Struct の使い方と今後の可能性について

Avatar for osyo

osyo

September 12, 2020
Tweet

More Decks by osyo

Other Decks in Programming

Transcript

  1. ⾃⼰紹介 ⾃⼰紹介 名前:osyo Twitter : github : ブログ : Rails

    歴2 年半 趣味で Ruby にパッチを投げたり bugs.ruby で気になったチケッ トをブログにまとめたりしてる Ruby で⼀番好きな機能は Refinements @pink_bangbi osyo-manga Secret Garden(Instrumental)
  2. Struct とは Struct とは 任意のプロパティを持ったクラスを動的に⽣成する Ruby の標準 ライブラリ # 3

    つのプロパティを持つ疑似クラスを作成する User = Struct.new(:id, :name, :age) # 作成した疑似クラスは通常のクラスと同じように使⽤できる # 引数は Struct.new に渡した順で割り当てられる homu = User.new(1, "homu", 14) # 各プロパティのアクセッサが暗黙的に定義されている p homu.name # => "homu" p homu.age # => 14 homu.age = 15 p homu.age # => 15
  3. 参照⽅法いろいろ 参照⽅法いろいろ # アクセッサメソッドとして参照できる p homu.name # => "homu" p

    homu.age # => 14 # [] メソッドで参照 # Hash みたいにキーを渡してアクセス p homu[:name] # => "homu" # index を渡してアクセス p homu[1] # => 15 # Hash に変換できる p homu.to_h # => {:id=>1, :name=>"homu", :age=>14} # 未定義のプロパティにはアクセスできない p homu.hoge # error: undefined method `hoge' p homu[:foo] # error: `[]': no member 'foo' in struct (NameError)
  4. 定義⽅法いろいろ 定義⽅法いろいろ # 3 つのプロパティを持つ疑似クラスを作成する User = Struct.new(:id, :name, :age)

    # User.new には Struct.new で渡した引数の順番で渡す User.new(1, "homu", 14) # Struct.new に keyword_init: true を渡すと User = Struct.new(:id, :name, :age, keyword_init: true) # User.new にキーワード引数で渡せるようになる p User.new(name: "homu", age: 14, id: 1) # => #<struct User id=1, name="homu", age=14> # Struct.new にブロックを渡し、その中でメソッドを定義すると # インスタンスメソッドとして定義される User = Struct.new(:last_name, :first_name) do def full_name "#{last_name} #{first_name}" end end homu = User.new(" 巴", " マミ") # ユーザが定義したインスタンスメソッドが呼べる p homu.full_name # => " 巴 マミ"
  5. Struct を継承する Struct を継承する Struct.new はクラスオブジェクトを返す なので Struct.new を継承することができる Struct.new

    を継承することで Struct の便利メソッドがそのまま 使える!! class User < Struct.new(:last_name, :first_name, :age) def full_name "#{last_name} #{first_name}" end def to_s "#{full_name} #{age} 歳" end end homu = User.new(" 巴", " マミ", 15) # Struct のメソッドがそのまま使える p homu.last_name # => " 巴" p homu.to_h # => {:last_name=>" 巴", :first_name=>" マ ミ", :age=>15}
  6. 引数や戻り値の擬似オブジェクトとして使う 引数や戻り値の擬似オブジェクトとして使う その場でデータ構造を定義してオブジェクトを⽣成する事がで きる テストとかの mock オブジェクトとかでも利⽤できる 疑似ファイルをテスト上で定義したりとか API モジュールの戻り値として定義したりとか

    def show_file(file) puts "path: #{file.path}" puts "size: #{file.size}" puts "lines:" puts file.readlines end # 普通はファイルを渡して使う show_file File.open("./test.rb") # Tempfile というクラスをその場で定義してそのインスタンスを渡す Tempfile = Struct.new(:path, :size, :readlines) show_file Tempfile.new("./test.rb", 14, %w(homu mami mado))
  7. Anonymous Struct literal という機能が提案されている ${} というリテラルで Struct のオブジェクトを定義できるよう にする提案 これを使うと

    Hash みたいにカジュアルに Struct オブジェク トが使える [Feature #16986] Anonymous Struct literal [Feature #16986] Anonymous Struct literal # 今の書き⽅ Struct.new(:a, :b).new(1, 2) # 提案してるリテラルだと ${} で定義できる ${ a: 1, b: 2 } # さっきのコード例 Tempfile = Struct.new(:path, :size, :readlines) show_file Tempfile.new("./test.rb", 14, %w(homu mami mado)) # ${} ですっきりとかける show_file ${ path: "./test.rb", size: 14, readlines: %w(homu mami mado) }
  8. Hash との⽐較例 Hash との⽐較例 # Hash homu = { id:

    1, name: "homu", age: 14 } # [] でのみ要素にアクセスできる homu[:name] # 新しい要素を追加できる homu[:job] = " 魔法少⼥" # 存在しないキーにアクセスしてもエラーにならない homu[:nmae] # => nil # Anonymous Struct literal homu = ${ id: 1, name: "homu", age: 14 } # [] だけでなくて . で参照できる homu.name # 新しい要素は追加できない # そもそも存在しない要素にアクセスするとエラーになる homu[:job] = " 魔法少⼥" # error: undefined method `[]'
  9. 今どうなってる? 今どうなってる? ⼊るかどうかすら決まってません!! いろんな書き⽅の提案がされている ${} の $ は Struct の

    S です そういえば Rubykaigi で matz が新しいシンタックスを⼊れたく ないって⾔っていたような… ? https://bugs.ruby-lang.org/issues/16986#note-11 ${a:1, b:2} # 元々の提案 {|a:1, b:2|} # <- matz のアイデア struct a: 1, b: 2 # struct キーワードを追加 %o{a:1, b:2} # % 記法で定義 (a:1, b:2) # {} ではなくて () で定義 Struct.anonymous(a:1, b:2) # メソッド定義 Struct(a:1, b:2) Struct[a:1, b:2]