Ruta graveolens  ·  notes from a language experiment  ·  cultivated since 2025

Assignment Statements

An assignment statement evaluates its right-hand side to a value and stores that value into the place named by its target (the target is a place, not merely a variable; see 5.2:2). If the target place currently holds a live (owned) value, that value is dropped before the new value is stored (overwrite-drop); storing into a moved-out place reinitializes it. The assignment itself evaluates to unit. These ownership effects are those of the core calculus's assign p = e form (docs/formal/01-core-calculus.md §5.2, §6.8) and of 3.8:55–56.

assign_stmt   = assign_target "=" expression ";" ;
assign_target = IDENT { "." IDENT | "[" expression "]" }
              | "self" ( "." IDENT | "[" expression "]" ) { "." IDENT | "[" expression "]" } ;

The assignment target is a place: a variable (or, inside a method, self) followed by any number of field (.f) and index ([e]) projections, freely mixed. All of x, p.f, arr[i], arr[i].f, p.arr[i], a.b.c, and o.items[i].arr[j] are valid targets. (Appendix A's place_expr is the normative statement of this production; see that appendix.)

Evaluation Order

In an assignment target = expression, the right-hand side expression is evaluated first to produce the value to be stored. The assignment target names a place; any index subexpressions appearing in the target (the [e] in an arr[e] target) are evaluated after the right-hand side, in source order (left-to-right). Once the place has been resolved, the produced value is written into it. This matches the core calculus's evaluation order: the assignment context assign p = E reduces the right-hand side E first, and the target place's index subexpressions are reduced as part of resolving the place for the store (docs/formal/01-core-calculus.md §6.2, §6.8).

fn tap(n: i32) -> i32 { @dbg(n); n }

fn main() -> i32 {
    let mut arr: [i32; 4] = [0, 0, 0, 0];
    // Prints 2 (the right-hand side) before 1 (the index of the place).
    arr[tap(1)] = tap(2);
    arr[1]  // 2
}

Variable Assignment

The variable MUST have been declared with let mut.

The expression type MUST be compatible with the variable's type.

fn main() -> i32 {
    let mut x = 0;
    x = 42;
    x
}

Array Element Assignment

Array element assignment requires a mutable array.

fn main() -> i32 {
    let mut arr: [i32; 2] = [0, 0];
    arr[0] = 20;
    arr[1] = 22;
    arr[0] + arr[1]
}

Struct Field Assignment

Struct field assignment requires a mutable struct value.

struct Point { x: i32, y: i32 }

fn main() -> i32 {
    let mut p = Point { x: 0, y: 0 };
    p.x = 42;
    p.x
}

Nested Field Assignment

Fields of nested structs can be assigned through chained field access.

All struct values in the chain MUST be part of a mutable binding.

struct Inner { value: i32 }
struct Outer { inner: Inner }

fn main() -> i32 {
    let mut o = Outer { inner: Inner { value: 0 } };
    o.inner.value = 42;
    o.inner.value
}

Assignment is Not an Expression

Assignment is a statement, not an expression. It MUST NOT be used in expression position.