Rubyでは定数を先頭を大文字の変数として定義することができますが、ミュータブル(変更不可)ではありません。
システム全体で定数は変更されないことが期待されますが、イミュータブル(変更可能)となっているので、予期せぬ不具合や見つけにくいバグが生じる恐れがあります。
そこで、4章では定数をミュータブルにする方法を見ていきます。
スポンサーリンク
定数をミュータブルにするには、freezeメソッドを使います
Str = "hello" Str.freeze Str.upcase!
freezeでミュータブルになった変数Strをupcase!(大文字に破壊的に変更)しようとすると、「RuntimeError: can't modify frozen String」という例外が投げられます。
配列についても、
Arr = [1, 2, 3] Arr.freeze Arr.push(4)
とすると、例外が投げられます。
しかし、次の2つの例では警告は出ますが例外は投げられません。
- 配列定数の中身を変える
NETWORKS = ["192.168.1", "192.168.2"].freeze def host_addresses(host, networks= NETWORKS) networks.map{|net| net << ".#{host}"} end
上記の例だと、host_addressesの第2引数がない状態で実行してしまう場合、NETWORKSはfreezeしているにもかかわらず変更されてしまいます。
つまり、配列自体がミュータブルになっても、要素はミュータブルになっていなのです。
そこで、次のように要素までfreezeする必要があります。
NETWORKS = ["192.168.1", "192.168.2"].map!(&:freeze).freeze def host_addresses(host, networks= NETWORKS) networks.map{|net| net << ".#{host}"} end
- 再定義できてしまう
Str = "hello".freeze
このように書くと、upcase!メソッドでは変更不可となります。
しかし、ミュータブルになっても再定義が可能で、
Str = "hello",freeze Str = "goodby"
は警告はでますが例外をなげることはありません。
そこで、ミュータブルなモジュールの中に入れてしまうと再代入に関しても不可能になります。
module Defaults Str = "hello".freeze end Defaults.freeze Defaults::Str = "goodby"
上記の例では最終行で例外が投げられます。
定数を直接編集したり再定義することは少ないと思いますが、上記の例のようにローカル変数が定数を参照している場合を考えると、間違えて編集してしまうかもしれません。これを防ぐには定数を(深く)ミュータブルにしておくことが大事ですね。
- 作者: Peter J. Jones,arton,長尾高弘
- 出版社/メーカー: 翔泳社
- 発売日: 2015/01/09
- メディア: 大型本
- この商品を含むブログ (13件) を見る
- 作者: 高橋征義,後藤裕蔵,まつもとゆきひろ
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2016/02/26
- メディア: 単行本
- この商品を含むブログ (2件) を見る
Ruby on Rails 5アプリケーションプログラミング
- 作者: 山田祥寛
- 出版社/メーカー: 技術評論社
- 発売日: 2017/04/14
- メディア: 大型本
- この商品を含むブログを見る