E0904: Move out of index
| Code | Category | Stability |
|---|---|---|
E0904 | Array | Permanent |
Explanation
Moving a non-Copy element out of an array is tracked one element at a time, and that tracking has to name the element while the program is compiled. A read moves an element out only when the index is a compile-time constant and the indexing applies directly to an array variable or by-value array parameter (3.8:68); every other consuming read is E0904 (7.1:28) — a runtime index, or an array reached through another projection or produced by an expression. The restriction is a soundness requirement rather than a convenience: with a runtime index the compiler cannot know which element moved, so neither use-after-move checking nor drop elaboration could stay correct. The same diagnostic covers a value-context projection with a declared-linear prefix even when the selected element is Copy, because such a read destructures the smallest enclosing declared-linear place (3.8:33) and a runtime index cannot identify the place whose residue would have to be disposed of.
Likely cause
A non-Copy element was consumed through a computed index, as in take(xs[i]) for a runtime i. Index with a literal or a named compile-time constant when the element is known; otherwise borrow the element instead of moving it, so it stays in the array, or move the whole array by value and consume its elements at constant indices. Iteration is not affected by this rule: a for loop binds each element by shared borrow rather than moving it out.
Examples
Move an element out at a runtime index
struct Data { value: i32 }
fn take(d: Data) -> i32 { d.value }
fn pick() -> u64 { 0 }
fn main() -> i32 {
let xs: [Data; 2] = [Data { value: 1 }, Data { value: 2 }];
let index = pick();
take(xs[index])
}
Move an element out at a constant index
struct Data { value: i32 }
fn take(d: Data) -> i32 { d.value }
fn main() -> i32 {
let xs: [Data; 2] = [Data { value: 1 }, Data { value: 2 }];
take(xs[1])
}