コンテンツへスキップ

ブロック転送

キャプチャされたブロックを転送するには、式に&をプレフィックスとして付けたブロック引数を使用します。

def capture(&block)
  block
end

def invoke(&block)
  block.call
end

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

上記の例では、invokeはブロックを受け取ります。invokeは通常の引数ではなく、ブロック引数のみを受け取るため、procを直接渡すことはできません。&を使用して、procをブロック引数として渡すことを明示的に指定します。それ以外の場合は

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

実際には、yieldするメソッドにprocを渡すことができます。

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転送を避けてください。