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)
ptr::addr_of!
, ptr::addr_of_mut!
Rust 1.51 で導入された 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
と書かれている。)
&raw const
, &raw mut
Rust 1.82 で導入された 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 - Rust は soft-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