跳到内容

块转发

要转发捕获的块,您需要使用块参数,并在表达式前加上 &

def capture(&block)
  block
end

def invoke(&block)
  block.call
end

proc = capture { puts "Hello" }
invoke(&proc) # prints "Hello"

在上面的例子中,invoke 接收一个块。我们不能直接将 proc 传递给它,因为 invoke 不会接收普通参数,只会接收块参数。我们使用 & 指定我们确实想要将 proc 作为块参数传递。否则

invoke(proc) # Error: wrong number of arguments for 'invoke' (1 for 0)

您实际上可以将一个过程传递给一个使用 yield 的方法

def capture(&block)
  block
end

def twice(&)
  yield
  yield
end

proc = capture { puts "Hello" }
twice &proc

上面的代码只是简单地重写为

proc = capture { puts "Hello" }
twice do
  proc.call
end

或者,结合 &-> 语法

twice &->{ puts "Hello" }

或者

def say_hello
  puts "Hello"
end

twice &->say_hello

转发未捕获的块

要转发未捕获的块,您必须使用 yield

def foo(&)
  yield 1
end

def wrap_foo(&)
  puts "Before foo"
  foo do |x|
    yield x
  end
  puts "After foo"
end

wrap_foo do |i|
  puts i
end

# Output:
# Before foo
# 1
# After foo

您也可以使用 &block 语法转发块,但这需要您至少指定输入类型,并且生成的代码会涉及闭包,速度会更慢

def foo(&)
  yield 1
end

def wrap_foo(&block : Int32 -> _)
  puts "Before foo"
  foo(&block)
  puts "After foo"
end

wrap_foo do |i|
  puts i
end

# Output:
# Before foo
# 1
# After foo

如果可以使用 yield,请尽量避免像这样转发块。还有一个问题是,breaknext 不允许在捕获的块内使用,因此在使用 &block 转发时,以下代码将无法工作

foo_forward do |i|
  break # error
end

简而言之,当涉及到 yield 时,请避免使用 &block 转发。