再借用について。
再借用が行われると、既存の参照からその参照先のライフタイムや可変性のみを変え、新しい参照が再取得される。なお、再借用は型解釈の一環としてほぼ暗黙で処理されるため、普段はあまり意識する必要はない。しかし、知っておくと各所の理解の助けになる事がある。
不変参照の再借用では、得られる参照は元の参照の別名のようになる。
不変参照 &T
はトレイト Copy
を実装している。再借用時もこれが利用される。
impl<T> Copy for &T where T: ?Sized
以下では、参照 data_ref
の別名として、_data_ref2
を取得している。
fn main() {
let data = 0;
let data_ref = &data;
let _data_ref2 = data_ref as &i32;
}
可変参照の再借用では、得られる参照は元の参照の一時的な代理になる。
可変参照 &mut T
は不変参照 &T
と異なり、トレイト Copy
を実装しない。しかし、可変参照の使用のために毎回ムーブを行うと、その使用後に毎回ムーブの戻しが必要になる。ここで、再借用で作られた代理を使えば、そうしたムーブは必要なくなる。
以下では、関数 use_mut
の引数として、参照 val_mut
の再借用が渡されている。
fn main() {
let mut val = 42;
let val_mut = &mut val;
use_mut(val_mut);
assert_eq!(*val_mut, 43)
}
fn use_mut(arg: &mut i32) {
*arg += 1;
}
再借用を利用して、可変参照を不変参照化できる。
以下では、可変参照 data_mut
を不変参照化して、_data_ref
を取得している。
fn main() {
let mut data = 0;
let data_mut = &mut data;
let _data_ref = data_mut as &i32;
}
フィールドアクセス式経由での再借用について。
フィールドのライフタイムが短縮されるパターンがある。
この場合、フィールドのライフタイムはそのまま使われる。
以下では、self.parent
の式は、ベース側よりフィールド側のライフタイムが長い。
そして、フィールドは不変参照なので、そのライフタイムがそのまま使われる。
fn main() {
let parent = Parent();
let child = Child::new(&parent);
let parent_ref = child.parent_ref();
parent_ref.do_something();
}
struct Parent();
impl Parent {
fn do_something(&self) {}
}
struct Child<'a> {
parent: &'a Parent,
}
impl<'a> Child<'a> {
fn new(parent: &'a Parent) -> Self {
Self { parent }
}
fn parent_ref(&self) -> &'a Parent {
self.parent
}
}
この場合、結果のライフタイムはベース側のライフタイムを超えないよう短縮される。そうでなければ、ベース側から辿れる可変参照と、再借用による可変参照、両者が同時に存在する期間が生まれ、可変参照の排他ルールが破られてしまう。
詳しくは、『error - ライフタイムの不足 × 再借用』を参照。
以下では、self.parent
の式は、ベース側よりフィールド側のライフタイムが長い。
そして、フィールドは可変参照なので、そのライフタイムはベース側に制限される。
その結果、メソッドの戻り値の条件を満たせず、エラーが発生する。
fn main() {
let mut parent = Parent();
let mut child = Child::new(&mut parent);
let parent_mut = child.parent_mut();
parent_mut.do_something();
}
struct Parent();
impl Parent {
fn do_something(&self) {}
}
struct Child<'a> {
parent: &'a mut Parent,
}
impl<'a> Child<'a> {
fn new(parent: &'a mut Parent) -> Self {
Self { parent }
}
fn parent_mut(&mut self) -> &'a mut Parent {
self.parent
}
}
error: lifetime may not live long enough --> src\main.rs:24:9 | 18 | impl<'a> Child<'a> { | -- lifetime `'a` defined here ... 23 | fn parent_mut(&mut self) -> &'a mut Parent { | - let's call the lifetime of this reference `'1` 24 | self.parent | ^^^^^^^^^^^ method was supposed to return data with lifetime `'a` but it is returning data with lifetime `'1`
型推論を使った初期化や代入では、再借用は行われない。
なぜなら、その場合はライフタイムや可変性が右辺と同じだと想定される。
つまり、型を省略するか否かで再借用か、もしくは通常のコピーや移動かが変化する。
以下では、with_infer
と with_move
は _y
の初期化時の型推論の有無のみが異なる。
fn main() {
with_infer();
with_move();
}
fn use_val<T>(_: T) {}
fn with_infer() {
let x = &mut 0;
let _y = x as &mut _;
use_val(x);
}
fn with_move() {
let x = &mut 0;
let _y = x;
use_val(x);
}
error[E0382]: use of moved value: `x` --> src\main.rs:17:10 | 15 | let x = &mut 0; | - move occurs because `x` has type `&mut i32`, which does not implement the `Copy` trait 16 | let _y = x; | - value moved here 17 | use_val(x); | ^ value used here after move