Rustはライフタイムという概念で参照切れを起こすコードをコンパイルエラーにし、実行時エラーの可能性を最小にしようと努力する(借用チェッカー)。問題は、参照切れを起こす「かもしれない」コードを判定できないので、結果「参照切れを起こすからビルドできない」場合の他に「参照切れを起こすかどうかわからないからビルドできない」という場合も出てくる。
そんな時に、コンパイラにライフタイムの計算方法を教えてやることで、「わからないからエラー」ではなく「参照切れになるからエラー」と言わせるのがライフタイムアノテーション(らしい)。
以下のtest関数の戻り値はgetinput()によって決まる。つまり実行時のユーザの入力次第。
testは参照を返すので、_r がmain()内の oo を指すのか、 vv を指すのかわからない。
これを借用チェッカーが評価できないらしく、ライフタイムが計算できないとエラーを出す。
struct structAB { v:i32, }
fn test(jj:&structAB,kk:&structAB )->&structAB{ // error[E0106]: missing lifetime specifier let val = getinput();// 入力によってどちらが帰るかが決まる if val%2==0{ return jj; } else{ return kk; } }
fn main() { let oo=structAB{v:5}; let _r; { let vv=structAB{v:4}; _r = test(&oo,&vv);// _r のライフタイムが oo か vv かわからない } println!("result: {}",_r.v); }
fn getinput()->i32{ let mut buffer = String::new(); let ret = std::io::stdin().read_line(&mut buffer); let val:i32=buffer.trim().parse().unwrap(); return val; }
上記コードにライフタイムアノテーションを付ける。
これにより、test関数の各引数は、jj または kk どちらか一方の、より短い方のライフタイムを持つもの、「として借用チェックを行え」という指示となる。
下の例では、test関数内で入力が偶数であれば呼び出し元の「oo」への参照が帰るので、このプログラムは正常動作するが、ライフタイムが両方ともvvの長さとして計算されるので、_rの参照切れの可能性を検知でき、エラーとできる。
struct structAB { v:i32, }
// ライフタイムアノテーション('a)付き fn test<'a>(jj:&'a structAB,kk:&'a structAB )->&'a structAB{ let val = getinput(); if val%2==0{ return jj; } else{ return kk; } }
fn main() { let oo=structAB{v:5}; let _r; { let vv=structAB{v:4}; _r = test(&oo,&vv);// 「_rのライフタイムは oo と vv のどちらか短いほう」とみなして検証される } println!("result: {}",_r.v); }
fn getinput()->i32{ let mut buffer = String::new(); let ret = std::io::stdin().read_line(&mut buffer); let val:i32=buffer.trim().parse().unwrap(); return val; }
原因が _r の参照切れの可能性なので、参照でなくコピーを保存するようなコードであればエラーでなくなる、ということがわかるので、そのように変更する。
#[derive(Copy, Clone)] struct structAB { v:i32, }
// ライフタイムアノテーション('a)付き
fn test<'a>(jj:&'a structAB,kk:&'a structAB )->&'a structAB{ let val = getinput(); if val%2==0{ return jj; } else{ return kk; } }
fn main() { let oo=structAB{v:5}; let _r:structAB; { let vv=structAB{v:4}; _r = test(&oo,&vv).clone();// 参照をしようと思うから破綻するので、コピーすれば問題ない } println!("result: {}",_r.v); }
fn getinput()->i32{ let mut buffer = String::new(); let ret = std::io::stdin().read_line(&mut buffer); let val:i32=buffer.trim().parse().unwrap(); return val; }
なお、Rustでは参照を「借用」(borrow)と言い、所有権すなわち破棄する権利は元の変数にある。
つまりC++でいうところのweak_ptrに似た性質を持つ。