悬空指针安全吗?

Is it safe to have a dangling pointer?

我有以下 Rust 代码:

extern crate libc; // 0.2.66

use libc::{free, malloc};

fn main() {
    unsafe {
        let mut p = malloc(16);
        let mut q = p;
        free(p);
        p = std::ptr::null_mut();
        q = std::ptr::null_mut();
    }
}

free 之后更改 pq 是否是未定义的行为,就像在此代码中一样?在我的真实代码中,q 会长时间保持悬垂指针,直到清理代码可以 运行 在其上,但在那段时间它不会被使用。

Rust 的原始指针本身永远不会导致未定义的行为。由于此代码仅调用 malloc 并将值传回 free,如果那些 C 函数将调用序列定义为 UB,则它只能是 UB,而它们没有。

悬挂指针不是问题 — 解引用悬挂(或无效)指针是个问题。这不会发生在这里。


这与 Rust 的引用不同,Rust 的引用总是保证引用一个有效的值,并且永远不会悬空。这个例子是未定义的行为:

let mut p = malloc(16);
free(p);
&*p;
error: Miri evaluation error: dangling pointer was dereferenced
 --> src/main.rs:9:9
  |
9 |         &*p;
  |         ^^^ Miri evaluation error: dangling pointer was dereferenced
  |
  = note: inside call to `main` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:67:34
  = note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:73
  = note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/sys_common/backtrace.rs:129:5
  = note: inside call to `std::sys_common::backtrace::__rust_begin_short_backtrace::<[closure@DefId(1:6016 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:13
  = note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:296:40
  = note: inside call to `std::panicking::r#try::do_call::<[closure@DefId(1:6015 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:272:13
  = note: inside call to `std::panicking::r#try::<i32, [closure@DefId(1:6015 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe]>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panic.rs:394:14
  = note: inside call to `std::panic::catch_unwind::<[closure@DefId(1:6015 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:51:25
  = note: inside call to `std::rt::lang_start_internal` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:67:5
  = note: inside call to `std::rt::lang_start::<()>`

是这样的:

let mut p = malloc(16);
free(p);
p = std::ptr::null_mut();
&*p;
error: Miri evaluation error: invalid use of NULL pointer
  --> src/main.rs:10:9
   |
10 |         &*p;
   |         ^^^ Miri evaluation error: invalid use of NULL pointer
   |
   = note: inside call to `main` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:67:34
   = note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:73
   = note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/sys_common/backtrace.rs:129:5
   = note: inside call to `std::sys_common::backtrace::__rust_begin_short_backtrace::<[closure@DefId(1:6016 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:13
   = note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:296:40
   = note: inside call to `std::panicking::r#try::do_call::<[closure@DefId(1:6015 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:272:13
   = note: inside call to `std::panicking::r#try::<i32, [closure@DefId(1:6015 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe]>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panic.rs:394:14
   = note: inside call to `std::panic::catch_unwind::<[closure@DefId(1:6015 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:51:25
   = note: inside call to `std::rt::lang_start_internal` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:67:5
   = note: inside call to `std::rt::lang_start::<()>`