Ruby中使用instance_exec传递块参数遇nil问题求助
解决Ruby动态方法中块参数为nil的问题
你的问题核心是误解了块参数传递的两种方式:把Proc作为普通参数传递,和把Proc转换成块传递。我们来一步步拆解问题,给出解决方案。
为什么原来的代码会报错?
你在调用b时写了b do |&block| ... end,这里的|&block|是在声明:这个块期望接收一个块参数(也就是调用b时要传入一个块,比如b { ... },然后把这个块转换成Proc赋值给block)。但实际上,你是通过instance_exec block_a, &block_b把block_a作为普通参数传给block_b的,并没有给b本身传递块,所以block自然是nil,调用instance_eval &block就会报错。
而你用字符串的例子能正常工作,是因为字符串是作为普通参数传递的,block_b的|s|正好接收这个普通参数,和块参数无关。
解决方案一:把block_a作为普通参数传递
修正b的块参数写法,直接接收block_a这个Proc对象,不需要用&:
def a &block_a define_method :b do |&block_b| puts "Running in define_method" # 把block_a作为普通参数传给block_b instance_exec(block_a, &block_b) end end a do puts "In block A" end # 直接接收Proc对象,不用& b do |block_a_proc| puts "In block B" # 执行这个Proc instance_eval(&block_a_proc) end
运行这段代码会得到正确输出:
Running in define_method In block B In block A
解决方案二:把block_a作为块传递给block_b
如果你坚持要在block_b里用|&block|的写法,那需要把block_a转换成块传递给block_b,而不是作为普通参数。这时候可以用block_b.call(&block_a)来实现:
def a &block_a define_method :b do |&block_b| puts "Running in define_method" # 把block_a转换成块,传给block_b block_b.call(&block_a) end end a do puts "In block A" end # 用&block接收传入的块(即block_a) b do |&block| puts "In block B" instance_eval(&block) end
这段代码也会得到正确输出,因为block_b.call(&block_a)把block_a这个Proc转换成了块,|&block|就会捕获这个块并转换成Proc赋值给block,不再是nil。
关键区别总结
instance_exec(arg, &block):把arg作为普通参数传给block,block的参数要写普通变量(比如|arg|)。block.call(&proc):把proc转换成块传给block,block的参数要写|&block|来捕获这个块。
内容的提问来源于stack exchange,提问作者SIL_BFT
相关产品推荐
相关产品推荐

