0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Ruby初学者】Rubyの定数に `gsub!` しても警告が出ないのはなぜか

Posted at

【備忘録】Rubyの定数に gsub! しても警告が出ないのはなぜか

IRBでこんなコードを試していて、不思議に思ったことを記録しておきます。

irb(main):006> HOGE = "hoge"
=> "hoge"
irb(main):007> HOGE.gsub!("hoge", "piyo")
=> "piyo"
irb(main):008> print HOGE
piyo=> nil

定数を変更したのに警告が出ななかったので、その理由を調べてまとめました。

結論

Rubyの定数は「再代入」には警告を出しますが、「破壊的変更」には警告を出さない

詳しく見てみる

再代入の場合(警告が出る)

HOGE = "hoge"
HOGE = "piyo"
# => warning: already initialized constant HOGE

再代入(==で新しいオブジェクトを代入)すると、Rubyは「定数を再定義している」と判断して警告する

破壊的変更の場合(警告が出ない)

HOGE = "hoge"
HOGE.gsub!("hoge", "piyo")
# => "piyo"

これは「HOGE が指しているオブジェクト自体を変更している」だけ。再代入ではないため、Rubyは警告を出さないとのこと

「なぜ変更できるのか」 〜freezeとfrozen_string_literal〜

実は、Rubyの文字列リテラルはデフォルトでは凍結されていないらしい

HOGE = "hoge"
HOGE.frozen?  # => false

したがって、破壊的メソッド(gsub!, chop!, upcase! など)を自由に使えてしまう

凍結すればエラーになる

HOGE = "hoge".freeze
HOGE.gsub!("hoge", "piyo")
# => FrozenError: can't modify frozen String

またはファイル先頭に以下を記載すれば良い

# frozen_string_literal: true

これにより、そのファイル内のすべての文字列リテラルが凍結される

まとめ

状況 挙動
定数に再代入 警告が出る
定数オブジェクトを破壊的に変更 警告なし(Rubyの仕様)
.freeze# frozen_string_literal: true FrozenErrorが発生

補足:検出したい場合

プロジェクト内で定数の破壊的変更を防ぎたいなら、RuboCopの以下ルールを有効にするのがおすすめとのこと

Style/MutableConstant:
  Enabled: true

最後に

Rubyでは「定数」という名前でも中身のオブジェクトまで不変とは限らないことを学びました。

不変にしたいなら freeze する、または # frozen_string_literal: true を活用するのが良いみたいです。

引き続き、学習を進めていきます。


間違えていたら、すいません。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?