🐘
【Ruby】tap メソッドの使い方
概要
- tap メソッドはブロックにオブジェクト(レシーバ)自身を渡す
- 最終評価として、そのオブジェクト(レシーバ)を返す
環境
❯ ruby -v
ruby 3.0.6p216 (2023-03-30 revision 23a532679b) [arm64-darwin23]
解説
例えば以下のようなコードのとき、tap
はブロックの評価が終わった後に実行されたオブジェクト(レシーバ)自身を返すので、結果は"a"
になります
"a".tap do |x|
end
=> "a"
ブロック内でレシーバに対してメソッドを実行しても、返り値はレシーバになります。
"a".tap do |x|
x.upcase
end
=> "a"
ブロック内で破壊的変更をすると、変更後の値が返ります。
"a".tap do |x|
x.upcase!
end
=> "A"
注意
変数に格納したレシーバを、tap
のブロック内で破壊的に変更すると、変数自体も変更されてしまい、
予期せぬバグを引き起こすおそれがあります。
reciever = "a"
reciever.tap do |x|
x.upcase!
end
# レシーバが変更されている
reciever
=> "A"
ユースケース
自分の現場では、空の配列やハッシュに対して .tap
を実行し、レシーバに要素を追加していくという処理をよくやっています。
[].tap do |x|
x << 1 if true
x << 2 if true
x << 3 if false
end
=> [1, 2]
インスタンスの初期値を変更するということもできます。
class User
attr_accessor :name, :age
def initialize(name, age)
@name = name
@age = age
end
end
user = User.new("John", 20)
user.name
=> "John"
user.tap do |x|
x.name = "Doe"
end
user.name
# name が変更されている
=> "Doe"
コンストラクタにデフォルトで nil を指定してあげると、以下のようにインスタンス生成することもできます。
class User
attr_accessor :name, :age, :email
def initialize(name: nil, age: nil)
@name = name
@age = age
end
end
user = User.new.tap do |x|
x.name = "John"
x.age = 25
end
user.name
=> "John"
user.age
=> 25
まとめ
返り値がレシーバになるというところが、前回投稿した each_with_object
と似ている(each_with_object
は各ブロックの返り値が引数で渡したもの)ので、こちらの記事と一緒に見ると、差分で理解しやすいと思います。
Discussion