Rubyのsetterメソッドを呼ぶ時はレシーバをつける

『Effective Ruby』を読むメモ。前回の日記はこちら。

コードを書いていたら、さっそくこの本の内容が役立ちました。eqlとhashを上書きする話です。

3章の途中まで読んだ後、後ろの方の気になるところをチラ見しています。今日の復習は、2章の項目9です。

Effective Ruby

Effective Ruby

項目9 Rubyの最悪に紛らわしい構文に注意しよう

まずは「Rubyプログラマが比較的よくしたがっている緩やかなガイドライン」の紹介がある。

  • メソッド名の末尾に?をつける: 真偽値を返す
  • メソッド名の末尾に!をつける: レシーバを書き換えることを示す。あるいは有害な副作用の警告
  • メソッド名の末尾に=をつける: setterになる

なるほど。項目9では、主にsetterの話をしている。

setterを使う時にはレシーバを指定しなければならない、というのが今回のポイントだった。また、setter以外の場所で不要なレシーバselfをたくさん書いて、コードをごちゃごちゃさせるのはやめようとも、筆者は主張している。

サンプルコード

次のようなname_bad.rbがあるとする。ちなみに、attr_writerとattr_accessorは、setterを書いてくれるヘルパーメソッド。

class Name
    attr_accessor(:first, :last)

    def initialize(first, last)
        first = first
        last = last
    end

    def full
        first + " " + last
    end
end

man0 = Name.new("Taro", "Yamada")
puts man0.full

initializeの中でsetterを呼んでいる。と思いきや、呼べていない。

レシーバがないので、first、lastというinitializeメソッド内のローカル変数に、引数の値を代入することになる。レシーバという言い方、なんかトラディショナルですね。

上のコードを実行すると、fullメソッドを呼び出した時に、firstとlastがnilでエラーになる。

% ruby name_bad.rb
name_bad.rb:10:in `full': undefined method `+' for nil:NilClass (NoMethodError)
     from name_bad.rb:15:in `<main>'

次のようにレシーバselfを指定すると、正しく「Taro Yamada」と出力される。

    def initialize(first, last)
        self.first = first
        self.last = last
    end
おまけ

自分は@をよく使う。もちろんこちらでも動く。

    def initialize(first, last)
        @first = first
        @last = last
    end

で、@は糖衣構文かと思っていたのだけど、違うようだ。知らなかった。

いわく、@fooはインスタンス変数fooを参照し、self.foo=はインスタンスメソッドfoo=を呼び出す。@foo=としても、インスタンスメソッドfoo=は呼ばれないらしい。

今日はここまで。