🐈
Ruby で Block を使うときに変数がキャプチャーされていないときは、instance_evalを疑おう
tl;dr
Ruby で Block を使うときに変数がキャプチャーされていないときは、instance_eval
を疑おう。というか実装を読もう。
くわしく
今日Rack::Builderを触っていて、下記のコードが予想と違う挙動をしたことが気になった。
require 'rack'
@something = 'something'
Rack::Builder.new do
puts @something
end
これはnil
となり、空行が出力される。
「はて、block(clousure)なのに何で変数キャプチャーされてないの?」と思ったら、
実装が下記だった。
def initialize(default_app = nil, &block)
@use, @map, @run, @warmup, @freeze_app = [], nil, default_app, nil, false
instance_eval(&block) if block_given?
end
instance_eval
がつくられたばかりのオブジェクトに対して呼ばれるので、block宣言時の変数はキャプチャーされていないのであった。
(補足)
自分で上記の説明読み返してみても不明だったので。
上記コードで、instance_eval
のレシーバーはRack::Builder
のインスタンス。単純に block.call
をした場合と違って、この時点で .new
を呼ばれたスコープへのアクセスは消えている状態。
参考
Discussion