跳至內容

區塊轉發

要轉發捕獲的區塊,您可以使用區塊引數,在表達式前加上 &

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)

您實際上可以將 proc 傳遞給一個會 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 轉發。