単一 / 複数ライフタイム注釈を持つ構造体の違い
項目14:生存期間を理解しよう を読んでいて混乱し始めたのでまとめる
単一の ライフタイム注釈 を持つ 構造体
code:rs
struct Single<'a> {
x: &'a str,
y: &'a str,
}
複数のライフタイム注釈を持つ構造体
code:rs
struct Multi<'a, 'b> {
x: &'a str,
y: &'b str,
}
同じ点
構造体が持つ全ての参照が有効(ドロップ されていない)場合のみ構造体は有効
どれか 1 つでも無効になった場合、構造体全体が使えなくなる ように コンパイラ がチェックする
異なる点
複数ライフタイム注釈の方が、「関数の戻り値だけ長く生かす」などの柔軟な設計が可能
単一ライフタイム注釈
2 つの参照がどちらも 'a として束縛されるため、片方のスコープが短ければ、もう片方のスコープも同じだけ短いと認識される
つまり、最も短い参照に合わせてしまう ため、意図せずに 生存期間 が縮まるケース が起こりやすい
複数ライフタイム注釈
フィールドごとに異なる生存期間を独立させることができる
つまり、長い方は長く、短い方は短く と表現できる
そのため、片方だけ長生きさせたい、返すのはこっちだけといったユースケースを実現するのに都合が良い
単一ライフタイム注釈では失敗するが、複数ライフタイム注釈では成功するケースを考え得る
code:rs
#derive(Debug)
struct Single<'a> {
x: &'a str,
y: &'a str,
}
fn single_return<'a>(s1: &'a str, s2: &'a str) -> &'a str {
let st = Single { x: s1, y: s2 };
st.x
}
fn main() {
let outer = String::from("outer");
let ret;
{
let inner = String::from("inner");
ret = single_return(&outer, &inner);
// ここで 'a は outer と inner 両方を含むライフタイムとなるが、
// inner が最も短いスコープでドロップするため、'a もその短さに合わせられる。
}
// ret はダングリング参照扱いになるためエラー
println!("{}", ret);
}
code:rs
#derive(Debug)
struct Multi<'a, 'b> {
x: &'a str,
y: &'b str,
}
fn multi_return<'a, 'b>(s1: &'a str, s2: &'b str) -> &'a str {
let st = Multi { x: s1, y: s2 };
st.x
}
fn main() {
let outer = String::from("outer");
let ret;
{
let inner = String::from("inner");
ret = multi_return(&outer, &inner);
// 'a と 'b は別の生存期間とみなせるので、
// "返り値は 'a に対応する outer" だけとコンパイラは判断。
}
// 返り値は 'a(outer) の参照なので OK
println!("{}", ret);
}
使い分け
単一ライフタイム注釈を選択すべき
全ての参照が同じスコープで問題ない場合や分ける必要がない場合
シンプルに保ちたい(複雑さ を増やしたくない)場合
複数ライフタイム注釈を選択すべき
各参照のスコープが明確に異なる場面で、それを正確に表現しないとエラーになる場合やあるいは柔軟に扱いたい場合
e.g. 一部の参照だけ関数の戻り値としてを返したいなど
#Rust