self
を返す破壊的メソッドの中には nil
をたまに返すものがあるので、新しいオブジェクトを返すメソッドの単純な置き換えとして使う際は注意がいる。
# 置き換えても一見うまく動いているが…
str = "ABC\n"
str.downcase.chomp #=> "abc"
str.downcase!.chomp! #=> "abc"
# たまにおかしくなる場合がある
str = 'abc'
str.downcase.chomp #=> "abc"
str.downcase!.chomp! #=> NoMethodError: undefined method `chomp!' for nil:NilClass
では nil
を返しうるメソッドと self
を必ず返すメソッドの差は何か、ということが気になったので、よく使う String
, Array
, Hash
クラスについてメソッドを調べてみた。結果として単純明快な規則までは分からなかったものの、種類毎にどちらのパターンか決まっている印象を受けた。
(ActiveSupportについては同じ規則でなく、実装の都合で決まっているように見える)
Ruby
Ruby 2.5のリファレンスマニュアルから抜粋し、覚えやすいよう大まかな役割毎に分類した。(正確な動作説明はマニュアルを参照)
String
※ まともに書くと長いので、複数のメソッド名を正規表現でまとめている
-
self
またはnil
を返す破壊的メソッド- 特定文字の除去 :
delete(|_prefix|_suffix)!
,chomp!
,chop!
,[lr]?strip!
,squeeze!
- 大文字小文字の変換 :
upcase!
,downcase!
,swapcase!
,capitalize!
- 置換 :
sub!
,gsub!
,tr!
,tr_s!
- 特定文字の除去 :
- 必ず
self
を返す破壊的メソッド- 初期化 :
clear
,replace
- 文字列の追加 :
<<
,concat
,insert
- 並べ替え :
reverse!
- 次の文字列 :
succ!
,next!
- エンコーディング :
encode!
,force_encoding
,scrub!
,unicode_normalize!
- 初期化 :
Array
-
self
またはnil
を返す破壊的メソッド- 抽出 :
select!
,reject!
,compact!
,uniq!
- 変形 :
flatten!
- 抽出 :
- 必ず
self
を返す破壊的メソッド- 初期化 :
clear
,fill
,replace
- 要素の追加 :
<<
,concat
,insert
,push
,append
,unshift
,prepend
- 並べ替え :
sort!
,sort_by!
,shuffle!
,reverse!
,rotate!
- 抽出 :
keep_if
,delete_if
- 要素の変換 :
collect!
,map!
- 初期化 :
Hash
-
self
またはnil
を返す破壊的メソッド- 抽出 :
select!
,reject!
,compact!
- 抽出 :
- 必ず
self
を返す破壊的メソッド- 初期化 :
clear
,replace
- 追加・更新 :
update
,merge!
- 抽出 :
keep_if
,delete_if
- 要素の変換 :
transform_keys!
,transform_values!
- 再計算 :
rehash
- 設定変更 :
compare_by_identity
- 初期化 :
ActiveSupport
ActiveSupport 5.2.0のソースコードから抜粋した。新しいRubyに取り込まれたものについても記載する。
String
-
self
またはnil
を返す破壊的メソッド- 置換 :
indent!
- 置換 :
- 必ず
self
を返す破壊的メソッド- 特定文字の除去 :
remove!
- 置換 :
squish!
- 特定文字の除去 :
Array
-
self
またはnil
を返す破壊的メソッド- (無し)
- 必ず
self
を返す破壊的メソッド- 要素の追加 :
append
,prepend
- 要素の追加 :
Hash
※ まともに書くと長いので、複数のメソッド名を正規表現でまとめている
-
self
またはnil
を返す破壊的メソッド- 抽出 :
compact!
- 抽出 :
- 必ず
self
を返す破壊的メソッド- 追加・更新 :
deep_merge!
,reverse_(merge!|update)
,with_defaults!
- 抽出 :
except!
- 要素の変換 :
transform_values!
,(deep_)?(transform|stringify|symbolize)_keys!
,to_options!
- 追加・更新 :
補足
上に挙げた「self
または nil
を返す破壊的メソッド」は全て、実際に nil
を返すのはオブジェクトに手を加えなかったとき。例えば置換の場合、全く同じ文字列に置換すれば nil
とはならない。
str = 'abc'
str.tr!('A', 'A') #=> nil
str.tr!('a', 'a') #=> "abc"
必ず self
を返してほしいなら、Object#tap
を使えば安全。
str = 'abc'
str.tap { |obj| obj.tr!('A', 'A') } #=> "abc"
str.tap(&:downcase!).tap(&:chomp!) #=> "abc"