Passage de bloc
Pour passer des blocs capturés, vous utilisez un argument de bloc, en préfixant une expression avec &
:
def capture(&block)
block
end
def invoke(&block)
block.call
end
proc = capture { puts "Hello" }
invoke(&proc) # prints "Hello"
Dans l'exemple précédent, invoke
reçoit un bloc. On ne peut lui passer proc
directement car invoke
ne reçoit pas les arguments classiques, seulement un argument bloc. Nous utilisons &
pour spécifier que nous voulons passer proc
comme argument de bloc. Sinon:
invoke(proc) # Error: wrong number of arguments for 'invoke' (1 for 0)
Vous pouvez en fait passer un proc à une méthode qui appelle yield:
def capture(&block)
block
end
def twice
yield
yield
end
proc = capture { puts "Hello" }
twice &proc
Ce qui précède est simplement réécrit en:
proc = capture { puts "Hello" }
twice do
proc.call
end
Ou, en combinant les syntaxes &
et ->
:
twice &->{ puts "Hello" }
Ou:
def say_hello
puts "Hello"
end
twice &->say_hello
Passer des blocs non capturés
Pour passer des blocs non capturés, vous devez utiliser yield
:
def foo
yield 1
end
def wrap_foo
puts "Avant foo"
foo do |x|
yield x
end
puts "Après foo"
end
wrap_foo do |i|
puts i
end
# Sortie:
# Avant foo
# 1
# Après foo
Vous pouvez aussi utiliser la syntaxe &block
pour passer les blocs,
mais dans ce cas vous devez au moins spécifier les types en entrée,
et le code généré incluera des closures et sera plus lent:
def foo
yield 1
end
def wrap_foo(&block : Int32 -> _)
puts "Avant foo"
foo(&block)
puts "Après foo"
end
wrap_foo do |i|
puts i
end
# Sortie:
# Avant foo
# 1
# Après foo
Essayez d'éviter de passer des blocs de cette manière si utiliser yield
suffit.
Il y a aussi le problème que break
et next
ne sont pas autorisés dans un bloc capturé,
ainsi ce qui suit ne fonctionnera pas en passant avec &block
:
foo_forward do |i|
break # error
end
Pour faire court, éviter de passer avec &block
quand yield
est utilisé.