Local & Foreign Labels
Labels are introduced with a leading , which is symmetric to how they are later referred to. Here is how to : or continue from a nested loop:
break
:OUTER_LOOP
recipes.each: |recipe| {
recipe.ingredients.each: |ingredient|
if !(fridge.has(ingredient))
continue :OUTER_LOOP;
return recipe
}
We'll discuss iteration in the following chapter, in any case calls to .each amount to a regular for (;;) loop, and we're using them here for brevity.
Of note is that parent function labels are available to child functions:
:OUTER_LOOP
recipes.each: |recipe| {
fn fail(reason: string) {
println("Cannot prepare " recipe.name ": " reason)
continue :OUTER_LOOP;
}
recipe.ingredients.each: |ingredient|
if !(fridge.has(ingredient))
fail("We don't have " ~ ingredient)
return recipe
}
Because is now defined in the outer loop, the label has become redundant:
fn fail
recipes.each: |recipe| {
fn fail(reason: string)
continue println("Cannot prepare " recipe.name ": " reason)
recipe.ingredients.each: |ingredient|
if !(fridge.has(ingredient))
fail("We don't have " ~ ingredient)
return recipe
}
Labelled Blocks and Block Expressions
You can from labelled blocks, just like in JavaScript, Rust and Zig:
break
:LABEL {
for (mut i = 0; i < 10; i++)
for (mut j = 0; j < 10; j++)
if (i == j)
break :LABEL;
println("This is never executed.")
}
Blocks are expressions, which allows you to wrap any statement in a block to place it in expression position:
let zero = {
println("Effect!")
0
}
The comma operator from C-style languages is not available, because blocks provide identical functionality.
Thus, the C-style expression a = (b, c) is written as a = { b; c }.
You can with a value from labelled block expressions:
break
let zero = {
:LABEL {
for (mut i = 0; i < 10; i++)
for (mut j = 0; j < 10; j++)
if (i == j)
break :LABEL i;
-1
}
}
assert(zero == 0)