Linguagem de Programação Crystal

Capturing blocks

A block can be captured and turned into a Proc, which represents a block of code with an associated context: the closured data.

To capture a block you must specify it as a method's block argument, give it a name and specify the input and output types. For example:

def int_to_int(&block : Int32 -> Int32)
  block
end

proc = int_to_int { |x| x + 1 }
proc.call(1) #=> 2

The above code captures the block of code passed to int_to_int in the block variable, and returns it from the method. The type of proc is Proc(Int32, Int32), a function that accepts a single Int32 argument and returns an Int32.

In this way a block can be saved as a callback:

class Model
  def on_save(&block)
    @on_save_callback = block
  end

  def save
    if callback = @on_save_callback
      callback.call
    end
  end
end

model = Model.new
model.on_save { puts "Saved!" }
model.save # prints "Saved!"

In the above example the type of &block wasn't specified: this just means that the captured block doesn't have arguments and doesn't return anything.

Note that if the return type is not specified, nothing gets returned from the proc call:

def some_proc(&block : Int32 ->)
  block
end

proc = some_proc { |x| x + 1 }
proc.call(1) # void

To have something returned, either specify the return type or use an underscore to allow any return type:

def some_proc(&block : Int32 -> _)
  block
end

proc = some_proc { |x| x + 1 }
proc.call(1) # 2

proc = some_proc { |x| x.to_s }
proc.call(1) # "1"

break and next

return and break can't be used inside a captured block. next can be used and will exit and give the value of the captured block.

with ... yield

The default receiver within a captured block can't be changed by using with ... yield.