1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
//! 生ポインタ。
//!
//! ## Preliminaries
//!
//! ### 参照とポインタの作成
//!
//! Safe Rust においては、下記のようなコードはコンパイルエラーとなる。
//!
//! ```compile_fail
//! let mut a = 1;
//! let mut_ref_1 = &mut a;
//! let mut_ref_2 = &mut *mut_ref_1;
//! *mut_ref_2 = 2;
//! *mut_ref_1 = 3;
//! let _invalid = *mut_ref_2; // (!)
//! ```
//!
//! Unsafe Rust において、生ポインタを使うことで、コンパイルを通すことはできる。
//!
//! ```ignore
//! let mut a = 1_u32;
//! let mut_ref_1 = &mut a;
//! let mut_ref_2 = unsafe { &mut *(mut_ref_1 as *mut u32) };
//! *mut_ref_2 = 2;
//! *mut_ref_1 = 3;
//! let _invalid = *mut_ref_2; // (?)
//! ```
//!
//! ただし、(たとえば C++ がそうであるように)コンパイルが通り、プログラムが正常終了したというのは、
//! コードに問題がないということの証明にはならない。実際、Miri でテストすることで未定義動作が検出され、
//! 次のような出力が得られるであろう。
//!
//! ```zsh
//! % cargo miri test
//! ```
//!
//! ```txt
//! error: Undefined Behavior: attempting a read access using <90194> at alloc23256[0x0], but that tag does not exist in the borrow stack for this location
//! --> src/lib.rs:8:20
//! |
//! 8 | let _invalid = *mut_ref_2; // (?)
//! | ^^^^^^^^^^
//! | |
//! | attempting a read access using <90194> at alloc23256[0x0], but that tag does not exist in the borrow stack for this location
//! | this error occurs as part of an access at alloc23256[0x0..0x4]
//! ```
//!
//! こうした検出は Stacked Borrows と呼ばれる機構によって行われている。
//! Miri では Stacked Borrows のサポートは experimental とのことであるが、一旦はこれを信用することにする。
//!
//! さて、safe なスマートポインタとして [`Box`] があるので、それを使ってみよう。
//!
//! ```
//! struct Foo(u32);
//! impl Foo {
//! pub fn new() -> Self { Foo(0) }
//! }
//!
//! let mut foo = Box::new(Foo::new());
//! assert_eq!(foo.0, 0);
//! foo.0 = 10;
//! assert_eq!(foo.0, 10);
//! ```
//!
//! [`Box::leak`] を用いることで [`&'a mut T`][reference] を取り出すことができる。
//!
//! ```ignore
//! # struct Foo(u32);
//! # impl Foo {
//! # pub fn new() -> Self { Foo(0) }
//! # }
//! let foo = Box::new(Foo::new());
//! let foo_mut_ref = Box::leak(foo);
//! assert_eq!(foo_mut_ref.0, 0);
//! foo_mut_ref.0 = 10;
//! assert_eq!(foo_mut_ref.0, 10);
//! // (?)
//! ```
//!
//! [`&'a mut T`][reference] は、生ポインタ (raw pointer) [`*mut T`][pointer] にキャストすることもできる。
//! 参照外し (dereference) は `unsafe` となる。
//!
//! ```ignore
//! # struct Foo(u32);
//! # impl Foo {
//! # pub fn new() -> Self { Foo(0) }
//! # }
//! let foo = Box::new(Foo::new());
//! let foo_mut_ptr: *mut _ = Box::leak(foo);
//! assert_eq!(unsafe { (*foo_mut_ptr).0 }, 0);
//! unsafe { (*foo_mut_ptr).0 = 10 };
//! assert_eq!(unsafe { (*foo_mut_ptr).0 }, 10);
//! // (?)
//! ```
//!
//! ところで、スマートポインタから生ポインタを取り出したままだと、drop
//! されないのでメモリリークしてしまう。
//! これも Miri によって検出でき、次のような出力が得られるであろう。
//!
//! ```txt
//! error: memory leaked: alloc23417 (Rust heap, size: 4, align: 4), allocated here:
//! --> .../lib/rustlib/src/rust/library/alloc/src/alloc.rs:98:9
//! |
//! 98 | __rust_alloc(layout.size(), layout.align())
//! | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//! ```
//!
//! [`Box::into_raw`] にある例に従い、次のようにすることで、`Box` 側に destructor を呼ばせることができる。
//! 逆に、勝手に destructor を呼ばれると困るような状況においては、`Box` を使うと厄介なことになる。
//!
//! ```
//! # struct Foo(u32);
//! # impl Foo {
//! # pub fn new() -> Self { Foo(0) }
//! # }
//! let foo_mut_ptr: *mut _ = Box::leak(Box::new(Foo::new()));
//! unsafe { drop(Box::from_raw(foo_mut_ptr)) };
//! ```
//!
//! [`*mut T`][pointer] の代わりに [`std::ptr::NonNull`] を用いることもできる。
//!
//! ```
//! use std::ptr::NonNull;
//!
//! # struct Foo(u32);
//! # impl Foo {
//! # pub fn new() -> Self { Foo(0) }
//! # }
//! let foo_mut_ref = Box::leak(Box::new(Foo::new()));
//! let foo_nonnull = NonNull::from(foo_mut_ref);
//! unsafe { drop(Box::from_raw(foo_nonnull.as_ptr())) };
//! ```
//!
//! `*mut T` に対する `NonNull` の違いは、null でないことの他に covariant
//! であることが挙げられるが、これに関しては [`variance`] を参照せよ。
//!
//! [`variance`]: ../variance/index.html
//!
//! ## See also
//! - [rust-lang / **miri**](https://github.com/rust-lang/miri)
#[test]
#[ignore]
fn invalidate() {
struct Foo(u32, u32);
let mut foo = Foo(10, 20);
let foo_mut: *mut _ = &mut foo;
unsafe { (*foo_mut).0 += 1 };
unsafe { assert_eq!((*foo_mut).0, 11) };
// let foo_const: *const _ = &foo;
let foo_const = &foo;
unsafe {
let _foo = &(*foo_mut);
}
assert_eq!(foo.0, 11);
// unsafe { assert_eq!((*foo_const).1, 20) };
assert_eq!(foo_const.1, 20);
unsafe { (*foo_mut).0 += 1 }; // UB detected when using -Zmiri-tree-borrows
unsafe { assert_eq!((*foo_mut).0, 12) };
}