借用と参照
資源を move せずに読み、必要なら書き込みます。
8.1 読み取りと書き込み
&x は読み取りの借用、&mut x は変更のための借用です。関数の引数にも同じ記号を付けます。
import std::prelude::Affine
fn length(&v: Vec(i64)): u64 {
Vec::len(&v)
}
fn append_one(&mut v: Vec(i64)): () {
Vec::push(&mut v, 1)
}
var v := Vec::new()
append_one(&mut v)
let n := length(&v)
perform show("len", n)
・ length ・ append_one len = 1
len = 1
length は Vec を消費しません。append_one は既存の Vec を変更します。&mut を取れるのは var だけです。
8.2 借用を返す「view」
借用を返す関数を使うと、大きな値をコピーせずに、その一部を参照できます。
import std::prelude::Affine
fn first(&v: Vec(i64)): &i64 ! {out_of_bounds} {
Vec::index(&v, 0)
}
var values := Vec::new()
Vec::push(&mut values, 7)
let head := handle {
let &r := first(&values)
r
} with {
out_of_bounds() => 0
}
perform show("head", head)
・ first head = 7
head = 7
first は、先頭の要素への借用を返します。要素がない可能性は、out_of_bounds という effect で表されています(! {out_of_bounds} が effect の行です)。この effect は handle で処理しないと、実行時に「unhandled effect」で止まります。この例ではハンドラが 0 を返すので、空の場合の代わりの値も用意できます。
8.3 借用の範囲を意識する
Tauto の借用には、強い制限があります。借用を任意の構造体に入れたり、外へ持ち出す閉包に捕まえさせたりすることは許されません。let &r := ... で束縛した借用では、現在のブロックの終わりまで、持ち主が凍結されます。凍結は字句的で、最後の使用で解けるわけではありません。
ただし、結果が複製できる型への借用(&i64 など)は、i64 そのものと同じに扱われ、凍結は起こりません。凍結を見るには、複製できない要素への借用を使います。
import std::prelude::Affine
fn first_row(&m: Vec(Vec(i64))): &Vec(i64) ! {out_of_bounds} {
Vec::index(&m, 0)
}
var m := Vec::new()
var row := Vec::new()
Vec::push(&mut row, 1)
Vec::push(&mut m, row)
let n := handle {
let &r := first_row(&m)
Vec::push(&mut m, Vec::new())
Vec::len(&r)
} with {
out_of_bounds() => 0
}
perform show("n", n)
・ first_row error 14:13: m is frozen by r
r が生きているブロックの中で m を変更しようとしているので、検査で止まります。
ch08-freeze.taut:14:12: use error: m is frozen by r
Vec::push(&mut m, Vec::new()) を handle の外(借用のブロックのあと)に移せば、通ります。
この方式は、「借用が使われなくなった最後の行で凍結を解く」方式より保守的です。その代わり、借用の境界がソースから読み取れます。
演習 8.1:fn first(&v: Vec(i64)): &i64 ... に out_of_bounds が必要な理由を説明してください。