スポンサーリンク

Rust ライフタイムアノテーションについて

Rustはライフタイムという概念で参照切れを起こすコードをコンパイルエラーにし、実行時エラーの可能性を最小にしようと努力する(借用チェッカー)。問題は、参照切れを起こす「かもしれない」コードを判定できないので、結果「参照切れを起こすからビルドできない」場合の他に「参照切れを起こすかどうかわからないからビルドできない」という場合も出てくる。

そんな時に、コンパイラにライフタイムの計算方法を教えてやることで、「わからないからエラー」ではなく「参照切れになるからエラー」と言わせるのがライフタイムアノテーション(らしい)。

例1 参照切れになるかどうかわからないからエラー

以下の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;

}

例2 参照切れになる可能性があるからエラー

上記コードにライフタイムアノテーションを付ける。

これにより、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;

}

例3 参照切れにならないコードに変更

原因が _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に似た性質を持つ。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: