参照は存在しているだけで、それと競合する各種の操作をコンパイルエラーにする。そして、参照のライフタイムは、基本的にはその参照の最後の使用までだが、状況によってはさらに延長される。ここではその延長されるパターンについて紹介する。

関連エラー

以下は関連するエラー。

基本ロジック

一つのライフタイムが複数の参照で共有される場合、そのライフタイムの範囲と制限は、それぞれの参照が要求する和になる。これにより、参照の最後の使用後も、他の参照の影響でライフタイムが継続し、その制限が残る事がある。

ケーススタディ

参照からの参照

参照から同じライフタイムの新しい参照を取得するパターン。

新しい参照が残っている間は、元の参照による制限も残る。

これは、元の参照と新しい参照の参照先が、データ構造における全体と部分、またはその逆のような重複した関係となるため、必須の措置である。つまり、新しい参照から部分側の更新を制限をしても、元の参照への制限がなければ、全体側からの更新ができてしまう。

コンパイルエラーになる例

以下では、参照 &data から参照 sub_ref を得ている。つまり、sub_ref への最後のアクセスまでは、&data も維持され、それと衝突する操作は行えなくなる。


fn main() {
    let mut data = Data { sub: 0 };
    let sub_ref = ref_to_ref(&data);
	&mut data;
    println!("{}", sub_ref);
}

fn ref_to_ref(data: &Data) -> &i32 {
    &data.sub
}

struct Data {
    sub: i32,
}

error[E0502]: cannot borrow `data` as mutable because it is also borrowed as immutable
 --> src\main.rs:4:2
  |
3 |     let sub_ref = ref_to_ref(&data);
  |                              ----- immutable borrow occurs here
4 |     &mut data;
  |     ^^^^^^^^^ mutable borrow occurs here
5 |     println!("{}", sub_ref);
  |                    ------- immutable borrow later used here

可変参照からの参照

可変参照から同じライフタイムの新しい参照を取得するパターン。

基本は『参照からの参照』のとおりで、新しい参照が残っている間は、元の参照による制限も残る。さらに、元の参照からの操作は全て禁止される。そのため、新しい参照が存在している間は、全ての操作がそこからに一本化される。

コンパイルエラーになる例

以下では、参照 &mut data から sub_ref を得ている。そのため、sub_ref への最後のアクセスまでは、&mut data も維持され、それと衝突する操作は行えなくなる。


fn main() {
    let mut data = Data { sub: 0 };
    let sub_ref = mut_to_ref(&mut data);
	&data;
    println!("{}", sub_ref);
}

fn mut_to_ref(data: &mut Data) -> &i32 {
    &data.sub
}

struct Data {
    sub: i32,
}

error[E0502]: cannot borrow `data` as immutable because it is also borrowed as mutable
 --> src\main.rs:4:2
  |
3 |     let sub_ref = mut_to_ref(&mut data);
  |                              --------- mutable borrow occurs here
4 |     &data;
  |     ^^^^^ immutable borrow occurs here
5 |     println!("{}", sub_ref);
  |                    ------- mutable borrow later used here

可変参照からの不変参照

可変参照を経由して、同じライフタイムの新しい不変参照を取得した場合について。

これも『可変参照からの参照』で説明したとおりである。

ただ、このパターンについてのよくある疑問があるため取り上げておく。

厳しすぎるルール?

可変参照からの参照では、新しい参照が残っている間は、元の参照からの操作は全て禁止されてしまう。可変参照からの不変参照の場合、これは少し厳しすぎるように思える

代わりに、元の参照からは読取のみを許可するのはどうだろう。つまり、元の可変参照は不変参照へとダウングレードしたと見なす。そして、新しい参照も不変参照である。よって、書込はどこからも行えないため、読取は常に安全に行える…はずである。

しかし、この代案は以下のレアケースの考慮が漏れている。

内部可変性による裏事情

内部可変型には get_mut, from_mut 関数を持つものがある。これらは、内部可変型と可変参照型とを相互変換できる (参考:『内部可変性と get_mut, from_mut 系の関数』)。

この変換において、変換先を得る代わりに、変換元への操作は全て禁止しなければならない。そうしないと、変換元か変換先のどちらかは内部可変型への参照型のため、一方からの読取と他方からの書込がともに許される区間ができてしまう。

つまり、元の参照への操作を全て禁止にするルールがここで必要になる。

コンパイルエラーになる例

以下は RefCell による例。不変参照中の値の更新をコンパイルエラーで予防している。


use std::cell::RefCell;

fn main() {
    let mut target = RefCell::new(0);
    let immutable_ref = get_ref_cell_value(&mut target);

    assert_eq!(*immutable_ref, 0);
    set_ref_cell_value(&target, 1);
    assert_eq!(*immutable_ref, 0);
}

fn get_ref_cell_value(mutable_ref: &mut RefCell<i32>) -> &i32 {
    &*mutable_ref.get_mut()
}

fn set_ref_cell_value(immutable_ref: &RefCell<i32>, value: i32) {
    *immutable_ref.borrow_mut() = value;
}

error[E0502]: cannot borrow `target` as immutable because it is also borrowed as mutable
 --> src\main.rs:8:24
  |
5 |     let immutable_ref = get_ref_cell_value(&mut target);
  |                                            ----------- mutable borrow occurs here
...
8 |     set_ref_cell_value(&target, 1);
  |                        ^^^^^^^ immutable borrow occurs here
9 |     assert_eq!(*immutable_ref, 0);
  |     ----------------------------- mutable borrow later used here

特殊な入れ子の参照

&'a A<'a> のような指定について。つまり、構造体への参照と構造体からの参照とが、同じライフタイム注釈を持つ場合について。

このような借用を行った場合、その借用は、その構造体が破棄されるまで返却されなくなる。なぜなら、[構造体への参照のライフタイム] ⊆ [構造体の存在期間] ⊆ [構造体からの参照のライフタイム] の包含関係において、両サイドが同じになるため、全体での等号が成立する。これにより、参照のライフタイムが構造体の存在期間と一致する事になる。

コンパイルエラーになる例

以下では、参照 &mut data と構造体の変数 data で問題の処理が発生する。そのため、data がある間は、参照 &mut data も残り、それと衝突する操作は行えなくなる。


fn main() {
    let context = 0;
    let mut data = Data { context: &context };
    use_data_mut(&mut data);
    &data;
}

fn use_data_mut<'a>(_data: &'a mut Data<'a>) {
    /* ... */
}

struct Data<'a> {
    context: &'a i32,
}

error[E0502]: cannot borrow `data` as immutable because it is also borrowed as mutable
 --> src\main.rs:5:5
  |
4 |     use_data_mut(&mut data);
  |                  --------- mutable borrow occurs here
5 |     &data;
  |     ^^^^^
  |     |
  |     immutable borrow occurs here
  |     mutable borrow later used here

Drop トレイトの影響

詳しくは『Drop トレイトのライフタイムへの影響 - ライフタイムの延長』を参照。