Skip to content

Ownership ​

Talor keeps Rust's guarantee and drops its ceremony. Every type that owns heap data is released by the compiler, every copy is either free or written, and nothing is counted behind your back. There is no mut x: T, no own x, and no marker at the point where a value moves.

Moves ​

A value that is not plain moves when it is handed over: stored into a struct, a variant, an array or a tuple, returned, passed to a parameter that takes it, or captured by a closure. Using it afterwards is a compile error:

talor
let a = [1, 2, 3];
let all: Array<Array<i64>> = [];
let b = a;
all.push(b);                // b is handed over, so the `let` moved a
a.len()                     // error: 'a' was moved on line 3; write 'a.clone()' at the move if both uses are needed

A let whose new name is only read does not move: let b = a; followed by reads of b and of a is accepted, because both names read the same array. The let moves the value when the new name is handed over or written through.

The compiler treats the last use of a variable as a move automatically, so xs.push(item) moves item when nothing reads it afterwards, and no clone is needed.

A field that moved out ​

A field can move out of a struct on its own. The struct may not be used as a whole while the field is empty, but its other fields may, and a write to the field fills it again:

talor
struct Bag { items: Array<i64>, n: i64 }

fn eat(xs: Array<i64>): i64 takes xs { xs.len() }

fn main(): i32 {
    let b = Bag { items: [1, 2, 3], n: 0 };
    let k = eat(b.items);       // `items` moves out of `b`
    b.n = 5;                    // a field beside it: accepted
    b.items = [5, 6];           // the field is filled again, and `b` is whole
    println(`${k} ${b.items.len()} ${b.n}`);
    0
}

Reading the field between the move and the write is refused.

The clone rule ​

The compiler copies implicitly only plain types. For every other type, when a value is needed twice the program must say which it wants, and the error says where:

error: 'tokens' was moved on line 4; write 'tokens.clone()' at the move if both uses are needed

Silent deep copies and silent reference counting never happen. Sharing is a type: shared T.

.clone() is a deep copy: an array copies its elements, a Box allocates a new box, and a shared adds one to its count.

talor
fn main(): i32 {
    let b = Box.new([1, 2]);
    let c = b.clone();          // Box<Array<i64>>: a second box, a second array
    let xs = (*b).clone();      // Array<i64>: a copy of the array inside
    xs.push(3);
    println(`${(*c).len()} ${xs.len()} ${(*b).len()}`);
    0
}

Two kinds of value cannot be cloned, and neither can anything that holds one:

  • A closure, because its captured values have a single owner.
  • A value holding a resource such as a file, a socket or a thread handle, because the runtime releases a resource once, and two copies would release it twice.

When two holders need one of these, put it in a shared and clone that: the clone is a count, and both holders reach the same value.

Drop ​

Owned values are released at the end of the scope that owns them, on every exit path (return, break, continue, ?) and on the panic path, where the unwind runs the same releases. A struct releases its fields in declaration order; a variant releases the payload of the case it holds. A moved value is not released by its former owner.

A temporary, a value with no name, is released at the end of the statement that created it, or at the end of the branch it was created in. The one exception is the receiver of a method that takes it: the method owns it from the call on.

Reading is not taking ​

What a function does beyond reading, the compiler reads out of its body, and a call site writes nothing: borrowing covers parameters, the edits and takes clause, and when a call makes a copy of an argument for you.

A read of a place the function does not own - an element, a field of a borrowed value, a loop variable - keeps the borrow instead of copying the value, and the region that borrow lives in is inferred from the control flow: lifetimes.

Talor v0.1.0 - Released under the MIT OR Apache-2.0 license.