Move Expressions¶
Accepted
Accepted for V1 source semantics; ownership analysis verification is tracked in Scope Backlog.
move is an explicit ownership-transfer expression:
var b = move a
consume(move b)
return move c
move applies only to movable places in V1:
- local
varbindings - local
constbindings - by-value parameters, including by-value
self - loop bindings
It does not apply to fresh rvalues, fields, array or slice elements, optional payloads, dereferenced pointers, globals, module bindings, or captured outer variables in V1:
consume(make_file()) // ok: fresh rvalue
move requires a movable local place:
consume(move make_file()) // error: move requires a movable place
move owner.field // error in V1
move items[i] // error in V1
move ptr.* // error in V1
move place produces the moved value and marks the source binding uninitialized on that control-flow path. Use after a moved or maybe-moved binding is a hard error. A moved var binding may be reassigned; a moved const binding cannot be reassigned and remains unusable.
move has low precedence and applies to the following movable place expression. When a moved value is used as a method receiver, write the moved receiver explicitly with parentheses:
var erased = (move concrete_box).into_dyn(Iterator(i32))
Ambiguous forms such as moving the result of a method call should be rejected or linted until the expression grammar is finalized:
move concrete_box.into_dyn(Iterator(i32)) // unclear; do not use in V1
Plain assignment, function argument passing, aggregate literal fields, and return value are copy-like in the semantic model. If the source binding's type has copying forbidden, these forms are compile errors unless the source expression is a fresh rvalue or uses explicit move:
consume(file) // copy; error if copy is forbidden
consume(move file) // transfer
return file // copy; error if copy is forbidden
return move file // transfer
return File.open() // ok: fresh rvalue
move is valid for copyable and non-copyable types. It always moves and invalidates the source binding; it never secretly means copy. Moving a trivially copyable value may be linted as unnecessary unless it is useful for generic code or intentionally ending a binding's lifetime.
Move analysis is definite-assignment style, not borrow checking. The compiler tracks local binding states as initialized, moved/uninitialized, or maybe moved:
var file = make_file()
if cond {
consume(move file)
}
use(file) // error: maybe moved
Expression evaluation order is deterministic so move effects are predictable:
- function arguments evaluate left-to-right
- aggregate literal fields evaluate in source order
- assignment evaluates the right-hand side before storing
- exact complex place evaluation order is finalized with full expression semantics
move may appear in conditionals, short-circuit expressions, and loops. Normal move analysis applies. Moves in complex boolean conditions may be linted when they make lifetime state hard to read.