所有権の四つの種
どの値を、何回使ってよいのかを学びます。
7.1 まず move を理解する
所有権とは、ある値を「どの名前が使う権利を持っているか」を管理する仕組みです。複製できない値を新しい変数に渡すと、古い名前からはその値を使えなくなります。これを move と呼びます。
Tauto は、値の使い方の規則を、四つの種(kind) で整理します。
| 種 | 複製 | 捨てる | 典型的な意味 |
|---|---|---|---|
copy | できる | できる | 小さな値をビットコピー |
shared | できる | できる | 共有して、複数の場所から読む |
affine | できない | できる | 一度しか使えない資源 |
linear | できない | できない | 必ずちょうど一度使う資源 |
関係は copy ⊑ shared ⊑ affine ⊑ linear です。copy と shared は、使える回数では同じですが、実行時の複製のコストが違います(shared は参照カウントを使います)。
i64 や Bool は copy、Text は shared、Vec(A) は affine です。自分の型には affine struct File {} や linear enum Handshake { ... } のように、種を宣言できます。構造から決まる種より緩い宣言は、型エラーになります。
7.2 Vec は 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)
size = 2
size = 2
Vec(A) は、変更できる要素の並びです。Vec::push(&mut v, ...) は、所有権を奪うのではなく、v の中身を更新するために一時的に借ります(借用は次の章です)。
関数に Vec をそのまま渡すと、所有権ごと渡ります。
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)
・ consume error 10:11: v is borrowed after move
consume(v) のあとの v は、所有していません。そこで使おうとすると、実行前の検査で止まります。
ch07-move.taut:10:10: use error: v is borrowed after move
資源の二重使用を防ぐ基本の考え方は、これです。最後の 1 行を消せば、このプログラムは通ります。
7.3 共有は、安全性を全部は保証しない
shared は自由に複製できるので扱いやすい一方、実行時には参照カウントなどのコストがかかります。共有できることと、参照の循環によるメモリリークが存在しないことは、別の話です。所有権の検査だけで、すべてのリークが静的に否定されるわけではありません。
演習 7.1:Vec を読みたいだけの関数には、所有権を渡すのと借用を渡すのでは、どちらが自然でしょうか。理由も考えてください。