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"
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.
The default receiver within a captured block can't be changed by using with ... yield
.