生ポインタのデリファレンス

ポインタを作成すること自体は安全ですが、それらをデリファレンスするには unsafe が必要です:

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

fn main() {
    let mut x = 10;

    let p1: *mut i32 = &raw mut x;
    let p2 = p1 as *const i32;

    // SAFETY: p1 と p2 はローカル変数への生ポインタを取得して作成されたため、
    // null ではなく、アラインされており、単一の(スタックに)確保された
    // オブジェクトを指すことが保証されています。
    //
    // 生ポインタが参照しているオブジェクトは関数全体のあいだ生存するため、
    // 生ポインタが存在しているあいだに解放されることはありません。また、
    // 生ポインタが存在しているあいだは参照経由でアクセスされず、
    // 他のスレッドから同時にアクセスされることもありません。
    unsafe {
        dbg!(*p1);
        *p1 = 6;
        // C と同様に、変更は生ポインタを通して適切に観測できます。
        dbg!(*p2);
    }

    // UNSOUND. こうしてはいけません。
    /*
    let r: &i32 = unsafe { &*p1 };
    dbg!(r);
    x = 50;
    dbg!(r); // 参照が指す基底オブジェクトは変更されています。これは UB です。
    */
}

unsafe ブロックについて、その内部のコードが実行している unsafe 操作の安全要件をどのように満たしているかを説明するコメントを書くのは、よい実践です(そして Android Rust スタイルガイドでも必須です)。

ポインタをデリファレンスする場合、これはポインタが 有効 でなければならないことを意味します。つまり:

  • ポインタは null であってはなりません。
  • ポインタは デリファレンス可能 でなければなりません(単一の確保済みオブジェクトの境界内で)。
  • オブジェクトが解放済みであってはなりません。
  • 同じ位置への同時アクセスがあってはなりません。
  • ポインタが参照をキャストして得られたものである場合、基底オブジェクトは生存していなければならず、そのメモリへのアクセスに参照を使用してはなりません。

ほとんどの場合、ポインタは適切にアラインされている必要もあります。

「UNSOUND」セクションは、よくある種類の UB バグの例を示しています。生ポインタのデリファレンス結果に安易に参照を取ると、参照が実際にどのオブジェクトを指しているのかについてのコンパイラの知識を迂回してしまいます。その結果、borrow checker は x を凍結しないため、その参照が存在しているにもかかわらず x を変更できてしまいます。ポインタから参照を作成するには、細心の注意 が必要です。