

# `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:

```silk
for <pattern> in <iterable> {
  ...
}
```

```silk
for let <pattern> in <iterable> {
  ...
}
```

```silk
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 as
 `if 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](/silk/docs/language/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.
- `break` exits the loop; `continue` advances 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..end` iterates `start, start+1, ..., end-1` (end-exclusive).
- `start..=end` iterates `start, start+1, ..., end` (end-inclusive).

Notes:

- The range bounds are evaluated once, left-to-right (`start` then `end`).
- 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.
- `continue` advances 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 `let`
 binding 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:

```silk
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.
- `break` exits the loop; `continue` advances to the next element.

### Pattern-filtered iteration with `for let`

Silk also supports a filtered iteration form:

```silk
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.
- `break` and `continue` keep their ordinary `for` meaning.

supported patterns are the same refutable subset already implemented by
`if let` / `while let`, including:

- `Some(x)` / `None`
- `Ok(x)` / `Err(x)`
- typed binders such as `v: T` where that pattern form is already supported
- enum variant payload patterns such as `Enum::Variant(x, _)`

Example:

```silk
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:

```silk
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)`](/silk/docs/std/interfaces/)).

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 to
 `value` (a copy) for that iteration and the body executes.
- `continue` advances by calling `next()` again; `break` exits the loop.

## C-style `for` loops

Silk also supports the traditional “C-style” `for` loop:

```silk
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 is `false`, the loop
 exits.
- The loop body executes once per iteration when `<condition>` is `true`.
- 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 to `var`) within the loop.
 - `const` init bindings remain immutable.

## Guidance

In Silk currently, `for` supports integer ranges and builtin
array/slice iteration. To write other loops today, use [`while`](/silk/wiki/language/flow-while/):

```silk
fn main () -> int {
  let mut i: int = 0;
  while i < 3 {
    std::io::println("i = {}", i);
    i += 1;
  }
  return 0;
}
```

## Compiler Requirements

- Recognize `for` loop syntax.
- Resolve iteration targets (ranges, collections) according to the language’s
 iteration model.
- Lower `for` into explicit control flow, with correct semantics for `break`
 and `continue`.

Compiler requirements:

- Recognize `for` loop syntax.
- Resolve iteration targets (ranges, collections) according to the language’s iteration model.
