webエンジニアの日常

RubyやPython, JSなど、IT関連の記事を書いています

Rubyをもう一歩進んで勉強する4章(Rubyの定数について)

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"

上記の例では最終行で例外が投げられます。

定数を直接編集したり再定義することは少ないと思いますが、上記の例のようにローカル変数が定数を参照している場合を考えると、間違えて編集してしまうかもしれません。これを防ぐには定数を(深く)ミュータブルにしておくことが大事ですね。

Effective Ruby

Effective Ruby

たのしいRuby 第5版

たのしいRuby 第5版

Ruby on Rails 5アプリケーションプログラミング

Ruby on Rails 5アプリケーションプログラミング