Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

2フェーズ借用

2フェーズ借用は、vec.push(vec.len()) のようなネストしたメソッド呼び出しを可能にする、可変借用のより寛容なバージョンです。このような借用は、最初は「予約」フェーズで共有借用として振る舞い、後で完全な可変借用へ「有効化」できます。

特定の暗黙的な可変借用だけが 2フェーズになり得ます。ソースコード内のどの &mutref mut も 2フェーズ借用になることはありません。2フェーズ借用を生成するケースは次のとおりです。

  1. 可変参照レシーバーを持つメソッドを呼び出す際の autoref 借用。
  2. 関数引数内の可変再借用。
  3. オーバーロードされた複合代入演算子における暗黙的な可変借用。

いくつか例を示します。

#![allow(unused)]
fn main() {
// ソースコード内

// ケース 1:
let mut v = Vec::new();
v.push(v.len());
let r = &mut Vec::new();
r.push(r.len());

// ケース 2:
std::mem::replace(r, vec![1, r.len()]);

// ケース 3:
let mut x = std::num::Wrapping(2);
x += x;
}

これらを 2フェーズ借用が分かる程度に展開すると、次のようになります。

// ケース 1:
let mut v = Vec::new();
let temp1 = &two_phase v;
let temp2 = v.len();
Vec::push(temp1, temp2);
let r = &mut Vec::new();
let temp3 = &two_phase *r;
let temp4 = r.len();
Vec::push(temp3, temp4);

// ケース 2:
let temp5 = &two_phase *r;
let temp6 = vec![1, r.len()];
std::mem::replace(temp5, temp6);

// ケース 3:
let mut x = std::num::Wrapping(2);
let temp7 = &two_phase x;
let temp8 = x;
std::ops::AddAssign::add_assign(temp7, temp8);

借用が 2フェーズになり得るかどうかは、型検査後に AutoBorrow 上のフラグで追跡され、その後 MIR 構築中に BorrowKind変換 されます。

各 2フェーズ借用は、一度だけ使用される一時変数に割り当てられます。そのため、次のように定義できます。

  • 一時変数が代入されるポイントを、その 2フェーズ借用の予約ポイントと呼びます。
  • 一時変数が使用されるポイントは、実質的に常に関数呼び出しであり、有効化ポイントと呼びます。

有効化ポイントは GatherBorrows visitor を使用して見つけられます。その後、BorrowData はその借用の予約ポイントと有効化ポイントの両方を保持します。

2フェーズ借用の検査

2フェーズ借用は、次の例外を除き、可変借用であるかのように扱われます。

  1. MIR 内のすべての location で、この location において有効化される 2フェーズ借用があるかどうかを 検査 します。live な 2フェーズ借用がある location で有効化される場合、その 2フェーズ借用と競合する借用がないことを検査します。
  2. 予約ポイントでは、競合する live な可変借用があればエラーにします。また、競合する共有借用があれば lint します。
  3. 予約ポイントと有効化ポイントの間では、2フェーズ借用は共有借用として振る舞います。そのようなポイントにいるかどうかは、MIR グラフの Dominators を使用して(is_active 内で)判定します。
  4. 有効化ポイントの後では、2フェーズ借用は可変借用として振る舞います。