Vol. 2 · № 01 · Un atelier RubyA Ruby workshop

Blocks & yield, ou le contrôle qu'on délègue Blocks & yield, or the control you delegate

On lit each { |n| … } comme « la boucle de Ruby ». C'est tout rater. each est une méthode ; le bloc est un morceau de code qu'on lui confie ; et c'est elle qui le rappelle, via yield, quand et autant de fois qu'elle veut. Tu fournis le quoi, la méthode garde le comment. We read each { |n| … } as 'Ruby's loop'. That misses everything. each is a method; the block is a piece of code you hand it; and it calls back, via yield, when and as often as it likes. You supply the what, the method keeps the how.

AudienceAudience
Dev habitué aux boucles for, prêt à déléguer le contrôle Dev used to for loops, ready to delegate control
Format
Self-paced
ChapitresChapters
5
Date
Jan 2026 Jan 2026
≈ 18 min ●●○○ BlocyieldContrôle
SommaireContents ·
№ 01CadrageFraming3 min

Le { … } n'est pas la boucle. C'est du code que tu confies.The { … } isn't the loop. It's code you hand over.

On lit each { |n| … } comme « la façon de boucler en Ruby ». C'est passer à côté de tout. each est une méthode ordinaire ; le bloc entre accolades est un morceau de code qu'on lui confie ; et c'est each qui décide de le rappeler — une fois par élément, via yield. La boucle n'est pas une construction du langage : c'est un objet à qui on a délégué le contrôle.We read each { |n| … } as 'how you loop in Ruby'. That misses everything. each is an ordinary method; the block in braces is a piece of code you hand it; and it's each that decides to call it back — once per element, via yield. The loop isn't a language construct: it's an object you've delegated control to.

Un bloc confié, rappelé par yieldA block handed over, called back by yield
[1, 2, 3].each { |n| puts n }
# le { |n| puts n } n'est pas "la boucle" :
# c'est un BLOC — un morceau de code — qu'on CONFIE à each.
# each le rappelle une fois par élément, avec yield.

def twice
  yield        # exécute le bloc qu'on m'a passé
  yield        # … et je décide de le rappeler une seconde fois
end

twice { puts "go" }   # affiche "go" deux fois
Qui tient le contrôle ?Who holds control?
twice { … }l'appelant confie un bloccaller hands over a block
def twicela méthode reçoit le contrôlethe method takes control
yieldelle rappelle le bloc, quand elle veutit calls the block back, when it wants
↩le bloc rend la main à la méthodethe block returns control to the method

Tu fournis le quoi (le bloc) ; la méthode garde le quand et le combien de fois. C'est l'inversion de contrôle.You supply the what (the block); the method keeps the when and the how many times. That's inversion of control.

Le réflexe du volumeThe volume's reflex

« Ce comportement, je le passe en bloc — et c'est l'objet receveur qui décide quand l'exécuter. » Au lieu d'écrire une boucle qui fait les choses, on confie le corps à une méthode qui sait comment itérer, ouvrir, réessayer, nettoyer. Le bloc dit le quoi ; la méthode orchestre le comment."This behavior, I pass it as a block — and the receiving object decides when to run it." Instead of writing a loop that does things, you hand the body to a method that knows how to iterate, open, retry, clean up. The block says the what; the method orchestrates the how.

Pourquoi ça suit directement le Vol 1.Why this follows straight from Vol 1.

Le Vol 1 a posé : tout est un message à un objet. Le bloc est la suite naturelle — on envoie un message à un objet en lui joignant un morceau de code, et on le laisse décider quoi en faire. each, map, open, transaction : toujours le même geste. Comprends yield, et la moitié de la bibliothèque standard cesse d'être magique.Vol 1 established: everything is a message to an object. The block is the natural sequel — you send a message to an object with a piece of code attached, and let it decide what to do with it. each, map, open, transaction: always the same gesture. Understand yield, and half the standard library stops being magic.

№ 02La surpriseThe surprise4 min

Ailleurs, tu boucles sur la collection. Ici, elle te rappelle.Elsewhere, you loop over the collection. Here, it calls you back.

Dans un langage statique, for et while sont des mots-clés : la boucle est dans le langage, et la collection se fait lire passivement de l'extérieur — c'est toi qui tiens l'index. Ruby renverse le sens. La collection sait itérer sur elle-même ; tu lui confies un bloc, et elle te rappelle. L'index, la condition, l'ordre : ce sont ses affaires, plus les tiennes.In a static language, for and while are keywords: the loop lives in the language, and the collection is read passively from the outside — you hold the index. Ruby reverses the direction. The collection knows how to iterate over itself; you hand it a block, and it calls you back. The index, the condition, the order: its business, no longer yours.

Java — la boucle est dehors, la collection est passiveJava — the loop is outside, the collection is passive
// Java : for est une CONSTRUCTION du langage
for (int i = 0; i < list.size(); i++) {
  System.out.println(list.get(i));
}
// c'est TOI qui tiens l'index, la condition, l'incrément.
// la collection est passive — elle se fait lire de l'extérieur.

Tu gères tout : l'index i, la condition d'arrêt, l'incrément, l'accès get(i). Une erreur sur l'un des quatre, et c'est un dépassement de borne ou une boucle infinie. La collection, elle, ne fait que se laisser lire.You manage everything: the index i, the stop condition, the increment, the get(i) access. A mistake on any of the four, and it's an out-of-bounds or an infinite loop. The collection merely lets itself be read.

Une boucle for lit une collection. Un each est la collection qui te rappelle. Ce n'est pas la même direction.A for loop reads a collection. An each is the collection calling you back. Not the same direction.
№ 03La mécaniqueThe mechanics5 min

yield rappelle ton bloc. ensure encadre ce qu'il y a autour.yield calls your block back. ensure brackets what's around it.

yield est le mot qui exécute le bloc reçu, autant de fois que la méthode le décide. block_given? lui demande s'il y a un bloc du tout. Avec ces deux-là, tu écris ton propre each — et tu découvres le vrai gain de l'inversion : la méthode peut faire quelque chose avant et après ton bloc. Ouvrir un fichier, le yield, puis le refermer quoi qu'il arrive. Ton bloc n'a plus à s'en soucier.yield is the word that runs the received block, as many times as the method decides. block_given? asks whether there's a block at all. With those two you write your own each — and you discover the real payoff of the inversion: the method can do something before and after your block. Open a file, yield it, then close it no matter what. Your block no longer has to care.

Écrire son propre itérateurWriting your own iterator
class Playlist
  def initialize(*songs) = @songs = songs

  def each
    return enum_for(:each) unless block_given?  # sans bloc → un Enumerator
    i = 0
    while i < @songs.size
      yield @songs[i]      # je rappelle ton bloc avec chaque chanson
      i += 1
    end
  end
end

Playlist.new("a", "b").each { |s| puts s }   # a, puis b
Un seul each débloque tout EnumerableOne each unlocks all of Enumerable
class Playlist   # on rouvre la classe ci-dessus, each compris
  include Enumerable      # le seul contrat exigé : un each qui yield
end

# … et tu obtiens map, select, sort, to_a, first… GRATUITEMENT.
Playlist.new("c", "a", "b").sort   # => ["a", "b", "c"]
# (on y revient au Vol 8 — ici, retiens : tout part de yield)
block_given? : le bloc est optionnel.block_given?: the block is optional.

Sans bloc, l'idiome return enum_for(:each) renvoie un Enumerator — un objet qu'on chaînera plus tard (playlist.each.with_index). La méthode s'adapte selon qu'on lui confie, ou non, un morceau de code à rappeler.With no block, the idiom return enum_for(:each) returns an Enumerator — an object you can chain later (playlist.each.with_index). The method adapts to whether you hand it a piece of code to call back, or not.

Le pattern « ressource » : ouvrir, yield, refermerThe 'resource' pattern: open, yield, close
def with_logfile(path)
  f = File.open(path, "a")
  yield f                 # tu fais ce que tu veux du fichier…
ensure
  f&.close                # … et JE garantis la fermeture, même si ça lève
end

with_logfile("app.log") { |log| log.puts "démarré" }
# le fichier est refermé quoi qu'il arrive — c'est ça, encadrer un bloc

Voilà ce qu'une boucle ne saura jamais faire : la méthode exécute du code avant ton bloc (ouvrir), te le yield, puis exécute du code après (refermer) — et grâce à ensure, le « après » a lieu même si ton bloc lève une exception. Tu ne peux pas oublier de refermer : ce n'est plus ton travail.Here's what a loop can never do: the method runs code before your block (open), yields it to you, then runs code after (close) — and thanks to ensure, the "after" happens even if your block raises. You cannot forget to close: it's no longer your job.

№ 04Le piègeThe trap4 min

Gérer la ressource « à la main », c'est reprendre le contrôle qu'on t'offrait.Managing the resource "by hand" is taking back the control you were offered.

Venant d'un langage où l'on ouvre et ferme soi-même, le réflexe est d'écrire f = File.open … f.close. Ça marche — tant que rien ne lève entre les deux. Le jour où une exception traverse, le close est sauté et le descripteur fuit. La forme à bloc existe précisément pour ça : elle encadre ton code d'un ensure que tu ne peux pas oublier. Ignorer le bloc, c'est refuser la garantie qu'il t'apportait.Coming from a language where you open and close yourself, the reflex is to write f = File.open … f.close. It works — as long as nothing raises in between. The day an exception passes through, the close is skipped and the descriptor leaks. The block form exists precisely for this: it brackets your code with an ensure you cannot forget. Ignoring the block is refusing the guarantee it gave you.

L'ouverture gérée à la main qui fuitHand-managed open that leaks
# Le réflexe « je gère moi-même » : ouvrir, faire, refermer
f = File.open("report.csv", "w")
f.puts header
write_rows(f, data)        # ← si CECI lève une exception…
f.close                    # … cette ligne n'est JAMAIS atteinte.

# Résultat : le descripteur de fichier fuit. Sous charge, à la longue,
# "Too many open files" — un bug qui n'apparaît qu'en production.
La forme à bloc, qui garantit la fermetureThe block form, which guarantees the close
# Le correctif : confie le bloc, laisse la méthode encadrer
File.open("report.csv", "w") do |f|
  f.puts header
  write_rows(f, data)      # même si ça lève…
end                        # … File.open a un ensure interne : f est refermé.

# Tu ne peux PAS oublier de refermer : ce n'est plus ton travail.
Le contre-exemple obligatoire.The mandatory counter-example.

Réécrire open … close à la main là où File.open { … } disait la même chose, c'est revenir à la gestion manuelle d'un autre langage — et réintroduire la fuite que le bloc éliminait. Même réflexe perdant qu'une boucle for à la place d'un each : on reprend un contrôle qu'on ferait mieux de déléguer. La question à se poser : existe-t-il déjà une méthode qui encadre ce que je m'apprête à faire à la main ?Rewriting open … close by hand where File.open { … } said the same thing is reverting to another language's manual management — and reintroducing the leak the block removed. Same losing reflex as a for loop instead of an each: taking back control you'd be better off delegating. The question to ask: is there already a method that brackets what I'm about to do by hand?

Si une méthode t'offre un bloc, c'est qu'elle veut faire quelque chose autour de ton code. Le contourner, c'est jeter cette garantie.If a method offers you a block, it wants to do something around your code. Bypassing it throws that guarantee away.
№ 05Bilan & éditoWrap-up & editorial2 min

Tu fournis le quoi. La méthode garde le comment.You supply the what. The method keeps the how.

Le bloc n'est pas « la boucle de Ruby » : c'est un morceau de code qu'on confie à un objet, et que cet objet rappelle quand, et autant de fois, qu'il le décide. Une fois ce renversement intégré, each, map, open, transaction se lisent tous pareil — et tu peux écrire les tiens.The block isn't 'Ruby's loop': it's a piece of code you hand to an object, which that object calls back when, and as often as, it decides. Once that reversal sinks in, each, map, open, transaction all read the same — and you can write your own.

L'outilThe toolCe qu'il faitWhat it doesLe réflexeThe reflex
Le bloc { } / do…endThe block { } / do…endUn morceau de code confié à une méthode — le quoi, pas le comment.A piece of code handed to a method — the what, not the how.Décris le corps ; laisse la méthode orchestrer l'itération ou la ressource.Describe the body; let the method orchestrate the iteration or the resource.
yieldyieldExécute le bloc reçu — zéro, une, ou plusieurs fois, au choix de la méthode.Runs the received block — zero, one, or many times, at the method's choice.C'est la méthode qui tient le quand et le combien. Pas l'appelant.The method holds the when and the how-many. Not the caller.
block_given?block_given?Dit s'il y a un bloc. Sinon, renvoie souvent un Enumerator chaînable.Says whether there's a block. If not, often returns a chainable Enumerator.Rends le bloc optionnel quand un appel sans bloc a du sens.Make the block optional when a block-less call makes sense.
yield + ensureyield + ensureExécuter du code avant ET après ton bloc — fermeture garantie, même sur exception.Run code before AND after your block — guaranteed close, even on exception.Cherche la forme à bloc avant de gérer une ressource à la main.Look for the block form before managing a resource by hand.
Écrire un yield, c'est dire : je sais quand et comment ; dis-moi seulement quoi. C'est la moitié du style de Ruby.Writing a yield says: I know when and how; just tell me what. That's half of Ruby's style.
Mot de l'éditeurFrom the editor

Déléguer le contrôle n'est pas le perdre.Delegating control isn't losing it.

Le bloc demande un petit acte de confiance : tu cesses de piloter la boucle toi-même et tu laisses une méthode décider quand exécuter ton code. En échange, tu n'as plus à tenir l'index, ni à te souvenir de refermer le fichier, ni à entourer chaque appel d'un begin/ensure. Ce que tu abandonnes — le contrôle du comment — était précisément la source de la moitié de tes bugs. Ce que tu gardes — le quoi — est la seule chose qui était vraiment à toi.The block asks a small act of trust: you stop driving the loop yourself and let a method decide when to run your code. In return, you no longer hold the index, nor remember to close the file, nor wrap every call in begin/ensure. What you give up — control of the how — was exactly the source of half your bugs. What you keep — the what — was the only thing that was ever really yours.

Prochain numéro : Proc vs Lambda. On a confié des blocs ; on va maintenant les capturer dans des objets — et découvrir que deux objets qui se ressemblent (proc et lambda) ne traitent pas du tout pareil l'arité et le return.Next issue: Proc vs Lambda. We've handed over blocks; now we'll capture them in objects — and discover that two look-alike objects (proc and lambda) treat arity and return in completely different ways.