for Loop
The for loop iterates over a range or iterable and executes a block once per
element.
Supported forms include integer range iteration (start..end and
start..=end), array/slice iteration (for x in xs { ... } for T[N] and
T[]), iterator iteration (for x in it { ... } when it.next() -> T?), and
C-style for (init; condition; step) { ... } loops.
Surface Syntax#
Supported surface forms:
for <pattern> in <iterable> {
...
}
for let <pattern> in <iterable> {
...
}
for (<init>; <condition>; <step>) {
...
}
Notes:
for <pattern> in <iterable> { ... }currently accepts a single identifier binder (and_) as the ordinary element-binding form.for let <pattern> in <iterable> { ... }is the refutable pattern form.for let mut <pattern> in <iterable> { ... }marks pattern binders mutable for the current loop iteration. It uses the same currently supported refutable match-pattern subset asif let/while let.<iterable>is an expression.<init>is a local binding (let/var/const) with an initializer.<condition>is a boolean expression.<step>is a statement-like expression (the same restricted subset as expression statements; see flow expression statements).
Semantics#
General rules:
- The iterable expression is evaluated once to produce an iteration source.
- The loop body executes once per produced element.
breakexits the loop;continueadvances to the next element.
Range iteration#
When the iterable is a range expression (for example start..end or
start..=end), the loop iterates over integer values.
Design intent:
start..enditeratesstart, start+1, ..., end-1(end-exclusive).start..=enditeratesstart, start+1, ..., end(end-inclusive).
Notes:
- The range bounds are evaluated once, left-to-right (
startthenend). - If the start bound is greater than or equal to the end bound (
start >= end) for an end-exclusive range, the loop executes zero times. - If the start bound is greater than the end bound (
start > end) for an end-inclusive range, the loop executes zero times. continueadvances to the next element (it performs the increment step, then re-checks the range condition).- The loop binder is in scope only inside the loop body block.
- The binder is immutable in the Supported forms (it behaves like a
letbinding that is updated by the loop machinery; user code cannot assign to it).
Type checking (Supported forms):
- Both range bounds must have integer type (
int,i8/u8,i16/u16,i32/u32,i64/u64). - The two bound types must match, except that an integer literal bound may be
coerced to the other bound’s integer type (for example
for i in 0..n_u32). - The loop binder (when not
_) has the bound’s integer type.
Example:
fn main () -> int {
let mut sum: int = 0;
for i in 0..3 {
// i takes values 0, 1, 2
sum += i;
}
// 0 + 1 + 2 = 3
return sum;
}
Array and slice iteration#
In Silk currently, for also supports iterating over builtin
array and slice types:
- fixed arrays
T[N], - slices
T[].
Semantics (Supported forms):
- The iterable expression is evaluated once.
- The loop executes in increasing index order, starting at index
0. - The loop binder (when not
_) is bound to the element value (a copy) for the current iteration. - The binder is in scope only inside the loop body block.
breakexits the loop;continueadvances to the next element.
Pattern-filtered iteration with for let#
Silk also supports a filtered iteration form:
for let <pattern> in <iterable> {
...
}
Semantics (Supported forms):
- The iterable expression is evaluated once.
- The loop still advances over every produced element in order.
- Each produced element is matched against
<pattern>. - When the pattern matches, any binders introduced by the pattern are in scope only for that iteration’s loop body, and the body executes once.
- When the pattern form uses
let mut, those binders may be reassigned inside that iteration's body. - When the pattern does not match, the current element is skipped and iteration continues with the next element.
breakandcontinuekeep their ordinaryformeaning.
supported patterns are the same refutable subset already implemented by
if let / while let, including:
Some(x)/NoneOk(x)/Err(x)- typed binders such as
v: Twhere that pattern form is already supported - enum variant payload patterns such as
Enum::Variant(x, _)
Example:
import std::result;
type R = std::result::Result(int, int);
fn main () -> int {
let xs: R[4] = [R.ok(2), R.err(7), R.ok(3), R.err(9)];
let mut sum: int = 0;
for let Ok(v) in xs {
sum += v;
}
return if sum == 5 { 0 } else { 1 };
}
Limitations:
- Element types are limited to the currently-supported array/slice element
subset (types that lower to a fixed scalar slot sequence in the current
back-end, such as primitive scalars,
string, and supported non-opaque structs). - Iteration is by value; to mutate an element, use indexing (
xs[i] = ...).
Example:
fn main () -> int {
let xs: int[3] = [1, 2, 3];
let mut sum: int = 0;
for x in xs {
sum += x;
}
return sum;
}
Iterator protocol#
In addition to builtin arrays and slices, for supports iterating over a
stateful iterator value.
An expression it is treated as an iterator when it has a next() -> T?
instance method (typically by implementing std::interfaces::Iterator(T)).
Semantics (Supported forms):
- The iterable expression is evaluated once to produce the iterator value.
- The loop repeatedly calls
it.next(). - When the result is
None, the loop exits. - When the result is
Some(value), the binder (when not_) is bound tovalue(a copy) for that iteration and the body executes. continueadvances by callingnext()again;breakexits the loop.
C-style for loops#
Silk also supports the traditional “C-style” for loop:
fn main () -> int {
let len: int = 10;
let mut sum: int = 0;
for (let i = 0; i < len; ++i) {
sum += i;
}
return sum;
}
Semantics (Supported forms):
<init>executes exactly once before the first condition check.<condition>is checked before each iteration; if it isfalse, the loop exits.- The loop body executes once per iteration when
<condition>istrue. - After the body executes normally,
<step>executes, then the loop re-checks<condition>. continue;skips the remainder of the loop body and jumps to<step>(then re-checks<condition>).break;exits the loop immediately without executing<step>for that iteration.- The init binding’s name is in scope within the entire loop (condition, step, and body) but is not visible after the loop.
Init binding mutability (Supported forms):
- For ergonomics,
for (let i = 0; ...; ++i)is accepted and the init binding is treated as mutable (equivalent tovar) within the loop. constinit bindings remain immutable.
Guidance#
In Silk currently, for supports integer ranges and builtin
array/slice iteration. To write other loops today, use while:
fn main () -> int {
let mut i: int = 0;
while i < 3 {
std::io::println("i = {}", i);
i += 1;
}
return 0;
}
Compiler Requirements#
- Recognize
forloop syntax. - Resolve iteration targets (ranges, collections) according to the language’s iteration model.
- Lower
forinto explicit control flow, with correct semantics forbreakandcontinue.
Compiler requirements:
- Recognize
forloop syntax. - Resolve iteration targets (ranges, collections) according to the language’s iteration model.
Source repository · Edit this page · View Markdown