章一覧

所有権の四つの種

どの値を、何回使ってよいのかを学びます。

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 な値

ch07-vec.taut
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 をそのまま渡すと、所有権ごと渡ります。

ch07-move.taut
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 を読みたいだけの関数には、所有権を渡すのと借用を渡すのでは、どちらが自然でしょうか。理由も考えてください。