The Four Ownership Kinds
Learn which values can be used, and how many times.
7.1 Understand moves first
Ownership tracks which name has the right to use a value. When a nonduplicable value is passed to a new variable, the old name can no longer use it. This is a move.
Tauto organizes usage rules into four kinds.
| Kind | Duplicate | Discard | Typical meaning |
|---|---|---|---|
copy | Yes | Yes | Bitwise copies of small values |
shared | Yes | Yes | Shared data read from several places |
affine | No | Yes | A resource used at most once |
linear | No | No | A resource used exactly once |
The relation is copy ⊑ shared ⊑ affine ⊑ linear. copy and shared permit the same number of uses but have different runtime duplication costs: shared uses reference counting.
i64 and Bool are copy, Text is shared, and Vec(A) is affine. Declare a kind for your own type with forms such as affine struct File {} or linear enum Handshake { ... }. A declaration that permits more uses than the structure allows is a type error.
7.2 Vec is affine
import std::prelude::Affine
var v := Vec::new()
Vec::push(&mut v, 3)
Vec::push(&mut v, 4)
let size := Vec::len(&v)
perform show("size", size)
// Borrow the vector to append elements and inspect its size.
size = 2
size = 2
Vec(A) is a mutable sequence of elements. Vec::push(&mut v, ...) temporarily borrows v to update its contents rather than taking ownership (borrowing is the next chapter's subject).
Passing a Vec directly to a function transfers ownership.
import std::prelude::Affine
fn consume(v: Vec(i64)): u64 {
Vec::len(&v)
}
var v := Vec::new()
Vec::push(&mut v, 1)
let n := consume(v)
Vec::push(&mut v, 2)
// This deliberately uses a vector after transferring ownership.
・ consume error 10:11: v is borrowed after move
After consume(v), the caller no longer owns v. Attempting to use it is rejected before execution.
ch07-move.taut:10:10: use error: v is borrowed after move
This is the basic idea that prevents a resource from being used twice. Remove the final line and the program is accepted.
7.3 Sharing does not guarantee every safety property
shared values are easy to use because they can be duplicated freely, but duplication carries costs such as reference counting. Being shareable is different from being free of reference cycles and memory leaks. Ownership checking alone does not statically rule out every leak.
Exercise 7.1: Should a function that only reads a Vec take ownership or a borrow? Explain your choice.