借用チェック

Rust の 借用チェッカー は、値を借用する方法に制約を課します。 参照が、それが借用している値を 超えて生存する ことはできないことは、すでに見ました。

// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0

fn main() {
    let x_ref = {
        let x = 10;
        &x
    };
    dbg!(x_ref);
}

借用チェッカーが強制する、もう 1 つの主要なルールもあります。エイリアシング ルールです。ある値については、どの時点でも次のいずれか一方のみが成り立ちます。

  • その値への共有参照を 1 つ以上持つことができる、または
  • その値への排他的参照をちょうど 1 つだけ持つことができます。
// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0

fn main() {
    let mut a = 10;
    let b = &a;

    {
        let c = &mut a;
        *c = 20;
    }

    dbg!(a);
    dbg!(b);
}
  • 「outlives」ルールは、最初に参照を見たときにすでに示しました。ここで 改めて取り上げるのは、借用チェックが借用を検証するためにいくつか異なる ルールに従っていることを受講者に示すためです。
  • 上のコードがコンパイルできないのは、a が同時に可変として(c を通じて) 借用され、不変として(b を通じて)借用されているからです。
    • 要件は、競合する参照が同じ時点に 存在しない ことだという点に注意して ください。参照がどこでデリファレンスされるかは関係ありません。 *c = 20 をコメントアウトして、c を一度も使わなくてもコンパイラエラーが 依然として発生することを示してください。
    • 借用の競合を発生させるのに、中間の参照 c は必須ではない点にも注意して ください。ca への直接の変更に置き換え、それでも同様のエラーが 発生することを示してください。これは、値を直接変更すると実質的に一時的な 可変参照が作られるためです。
  • コードをコンパイルできるようにするには、bdbg! 文を c を導入する スコープより前に移動してください。
    • その変更を行うと、コンパイラは bc を通じた a の新しい可変借用より 前でしか使われないことを認識します。これは借用チェッカーの 「非字句的ライフタイム」と呼ばれる機能です。

さらに詳しく

  • 厳密には、あるデータに対する複数の可変参照は、再借用によって同時に存在 できます。これにより、元の参照を無効にすることなく、可変参照を関数に渡す ことができます。この Playground の例 はその挙動を示しています。
  • Rust は、排他的参照の制約を使って、マルチスレッドコードでデータ競合が発生 しないことを保証します。これは、一度に 1 つのスレッドしかデータの一部に 可変アクセスできないためです。
  • Rust はこの制約をコードの最適化にも利用します。たとえば、共有参照が指す値は、 その参照のライフタイムの間、安全にレジスタにキャッシュできます。
  • 構造体のフィールドは互いに独立して借用できますが、構造体のメソッドを 呼び出すと構造体全体が借用され、個々のフィールドへの参照が無効になる可能性が あります。この Playground スニペット にその例があります。