Move Semantics
This section describes how values are moved and copied in Rue.
Value Categories
Types in Rue are categorized by how they behave when used:
- Copy types can be implicitly duplicated when used. Using a Copy type does not consume the original value.
- Move types (also called affine types) are consumed when used. After using a move type value, the original binding becomes invalid.
The following types are Copy types:
- All integer types (
i8,i16,i32,i64,u8,u16,u32,u64) - The boolean type (
bool) - The unit type (
()) - Enum types (all variants of an enum)
User-defined struct types are move types by default. Using a struct value consumes it.
struct Point { x: i32, y: i32 }
fn main() -> i32 {
let p = Point { x: 1, y: 2 };
let q = p; // p is moved to q
// p is no longer valid here
q.x + q.y
}
The @copy Directive
A struct type may be declared as a Copy type using the @copy directive before the struct definition.
copy_struct = "@copy" struct_def ;
A struct marked with @copy is a Copy type. Using a @copy struct value does not consume it; the value is implicitly duplicated.
@copy
struct Point { x: i32, y: i32 }
fn main() -> i32 {
let p = Point { x: 1, y: 2 };
let q = p; // p is copied, not moved
let r = p; // p can be used again
p.x + q.x + r.x // all three are valid
}
A @copy struct must contain only fields that are themselves Copy types. It is a compile-time error to mark a struct as @copy if any of its fields are move types.
struct Inner { value: i32 } // move type (no @copy)
@copy
struct Outer { inner: Inner } // ERROR: field 'inner' has non-Copy type 'Inner'
A @copy struct may contain fields of primitive Copy types (integers, booleans, unit), enum types, or other @copy struct types.
@copy
struct Point { x: i32, y: i32 }
@copy
struct Rect { top_left: Point, bottom_right: Point } // OK: Point is @copy
fn main() -> i32 {
let r = Rect {
top_left: Point { x: 0, y: 0 },
bottom_right: Point { x: 10, y: 10 }
};
let r2 = r; // r is copied
r.top_left.x // r is still valid
}
Use After Move
It is a compile-time error to use a value that has been moved.
struct Point { x: i32, y: i32 }
fn main() -> i32 {
let p = Point { x: 1, y: 2 };
let q = p; // p is moved
let r = p; // ERROR: use of moved value 'p'
0
}
A value is considered moved when it is:
- Assigned to another variable
- Passed as an argument to a function
- Returned from a function
struct Data { value: i32 }
fn consume(d: Data) -> i32 { d.value }
fn main() -> i32 {
let d = Data { value: 42 };
let result = consume(d); // d is moved into the function
// d is no longer valid here
result
}
Copy Types and Multiple Uses
Copy types can be used multiple times without being consumed.
fn main() -> i32 {
let x = 42;
let a = x; // x is copied
let b = x; // x is copied again
a + b // 84
}
Function parameters of Copy types receive a copy of the argument. Function parameters of move types receive ownership of the argument.
Shadowing and Moves
Shadowing a variable does not prevent it from being moved. A moved variable remains invalid even if a new variable with the same name is introduced in an inner scope.
struct Data { value: i32 }
fn main() -> i32 {
let d = Data { value: 1 };
let x = d; // d is moved
{
let d = Data { value: 2 }; // New 'd' shadows, but doesn't restore old 'd'
d.value
}
// Original 'd' is still invalid here
}