🦀

Rust 1.82 (2024-10-17) 以降の raw pointer (生ポインタ) の作成方法

に公開

Rust 1.82 以降の raw pointer の作成方法

  • Immutable raw pointer: &raw const x
  • Mutable raw pointer: &raw mut x
let mut num = 5;

let r1 = &raw const num;
let r2 = &raw mut num;

引用元: Unsafe Rust - The Rust Programming Language

Rust 1.82 より前の raw pointer の作成方法

  • Immutable raw pointer: &x as *const _
  • Mutable raw pointer: &mut x as *mut _

let mut num = 5;

let r1 = &num as *const _;
let r2 = &mut num as *mut _;

Rust 1.51 (2021-03-25) 以降

Rust 1.51 では ptr::addr_of!, ptr::addr_of_mut! マクロが導入されました。

use std::ptr;

#[repr(packed)]
struct Packed {
    f1: u8,
    f2: u16,
}

let packed = Packed { f1: 1, f2: 2 };
// `&packed.f2` would create an unaligned reference, and thus be Undefined Behavior!
let raw_f2 = ptr::addr_of!(packed.f2);
assert_eq!(unsafe { raw_f2.read_unaligned() }, 2);

引用元: Announcing Rust 1.51.0 | Rust Blog

経緯

raw pointer の作成方法の変遷の経緯をまとめます。
プログラムは Announcing Rust 1.82.0 | Rust Blog に記載のものを引用します。

Rust 1.51 より前の問題点

Rust 1.51 より前の方法では raw pointer を作成するために一度 Safe Rust の immutable / mutable reference を経由する必要がありました。

reference が参照するメモリ領域は適切に align され、初期化されている必要があり、そうでないケースでは UB (Undefined Behavior) が起き得ます。そのため、たとえば次のようなプログラムはコンパイルが通りません。

#[repr(packed)]
struct Packed {
    not_aligned_field: i32,
}

fn main() {
    let p = Packed {
        not_aligned_field: 1_82,
    };

    // This would be undefined behavior!
    // It is rejected by the compiler.
    let ptr = &p.not_aligned_field as *const i32;
    ...
}
$ cargo check
...
error[E0793]: reference to packed field is unaligned
  --> src/main.rs:13:15
   |
13 |     let ptr = &p.not_aligned_field as *const i32;
   |               ^^^^^^^^^^^^^^^^^^^^
   |
   = note: packed structs are only aligned by one byte, and many modern architectures penalize unaligned field accesses
   = note: creating a misaligned reference is undefined behavior (even if that reference is never dereferenced)
   = help: copy the field contents to a local variable, or replace the reference with a raw pointer and use `read_unaligned`/`write_unaligned` (loads and stores via `*p` must be properly aligned even when using raw pointers)

Rust 1.51 で導入された ptr::addr_of!, ptr::addr_of_mut!

Rust 1.51 では raw pointer を reference を経由することなく作成するマクロとして ptr::addr_of!, ptr::addr_of_mut! が導入されました。これにより適切に align されていない場合や未初期化の場合であっても安全に raw pointer を作成することができるようになりました。

// This is the old way of creating a pointer.
let ptr = std::ptr::addr_of!(p.not_aligned_field);

(Rust 1.82 の資料のため old way と書かれている。)

Rust 1.82 で導入された &raw const, &raw mut

Rust 1.82 ではさらに ptr::addr_of!, ptr::addr_of_mut! と同様の機能を、マクロではなく native な構文 &raw const, &raw mut として導入しました。

// This is the new way.
let ptr = &raw const p.not_aligned_field;

// Accessing the pointer has not changed.
// Note that `val = *ptr` would be undefined behavior because
// the pointer is not aligned!
let val = unsafe { ptr.read_unaligned() };

現在 (Rust 1.82 以降)、addr_of in std::ptr - Rust, addr_of_mut in std::ptr - Rustsoft-deprecated との記載があります。

addr_of!(expr) is equivalent to &raw const expr. The macro is soft-deprecated; use &raw const instead.

addr_of_mut!(expr) is equivalent to &raw mut expr. The macro is soft-deprecated; use &raw mut instead.

その他

Announcing Rust 1.82.0 | Rust Blog には以下の記述があります。

The native syntax makes it more clear that the operand expression of these operators is interpreted as a place expression. It also avoids the term "address-of" when referring to the action of creating a pointer. A pointer is more than just an address, so Rust is moving away from terms like "address-of" that reaffirm a false equivalence of pointers and addresses.

リンク先の内容については調べきれていませんが、raw キーワードによる raw pointer の作成は単なる書きやすさの向上だけでなく Rust の pointer に関する思想を反映していそうです。

参考

Discussion