良書やなEffective Ruby。
項目4 定数がミュータブルなことに注意しよう
定数のfreeze
module Defaults NETWORKS = ["192.168.1", "192.168.2"].freeze end def host_addresses(host, networks=Defaults::NETWORKS) networks.map { |net| net << ".#{host}" } end >> host_addresses(1) => ["192.168.1.5.1", "192.168.2.5.1"] >> Defaults::NETWORKS => ["192.168.1.1", "192.168.2.1"] # 書き換えられた!
Arrayのfreezeはハマるやつ。
こう書かないとダメ。
module Defaults NETWORKS = ["192.168.1", "192.168.2"].map!(&:freeze).freeze end >> host_addresses(2) Traceback (most recent call last): 5: from /Users/rochefort/.anyenv/envs/rbenv/versions/2.5.0/bin/irb:11:in `<main>' 4: from (irb):12 3: from (irb):9:in `host_addresses' 2: from (irb):9:in `map' 1: from (irb):9:in `block in host_addresses' FrozenError (can't modify frozen String)
再代入禁止
以下は、書籍とは異なりますが、こっちの方がわかりやすいと思ったので、Defaults::NETWORKS を例にして見ました。
>> Defaults::NETWORKS => ["192.168.1", "192.168.2"] >> Defaults::NETWORKS = "a" (irb):32: warning: already initialized constant Defaults::NETWORKS (irb):3: warning: previous definition of NETWORKS was here => "a" # 再代入できた!
というようにwarningは出るが書き換わります。
これを防ぎたければ、module/class自体をfreezeすれば良い。
>> Defaults.freeze => Defaults >> Defaults::NETWORKS => ["192.168.1", "192.168.2"] >> Defaults::NETWORKS = "a" Traceback (most recent call last): 2: from /Users/trsw/.anyenv/envs/rbenv/versions/2.5.0/bin/irb:11:in `<main>' 1: from (irb):39 FrozenError (can't modify frozen Module)
余談
このmodule/classでfreezeさせるのは使ったことなかったけど、これを利用しちゃえば Array#map!(&:freeze).freeze
という書き方なんてしなくても済むのか。
module Defaults NETWORKS = ["192.168.1", "192.168.2"] end Defaults.freeze def host_addresses(host, networks=Defaults::NETWORKS) networks.map { |net| net << ".#{host}" } end >> host_addresses(3) Traceback (most recent call last): 5: from /Users/rochefort/.anyenv/envs/rbenv/versions/2.5.0/bin/irb:11:in `<main>' 4: from (irb):58 3: from (irb):55:in `host_addresses' 2: from (irb):55:in `map' 1: from (irb):55:in `block in host_addresses' FrozenError (can't modify frozen String)
なるほど、これなら定数用のモジュールを用意して、そこでfreezeしちゃえばいいのね。
まぁでも定数って割と限定的なclass内で利用したいケースの方が多い気がするからケースバイケースかなぁ。
覚えておく事項
- 定数は書き換えられないようにするためにかならずフリーズしよう
- 定数が配列やハッシュなどのコレクションオブジェクトを参照する場合、コレクションとその要素をフリーズしよう。
- 既存の定数に新しい値が代入されるのを防ぐためには、定数が定義されているモジュールをフリーズしよう
- 作者: Peter J. Jones,arton,長尾高弘
- 出版社/メーカー: 翔泳社
- 発売日: 2015/01/09
- メディア: 大型本
- この商品を含むブログ (13件) を見る