< 返回版块

destinyFvcker 发表于 2025-01-05 14:59

Tags:UnsafeCell、release mode

下面这段代码:

use std::cell::UnsafeCell;

const UNSAFE_CELL: UnsafeCell<i32> = UnsafeCell::new(10);

fn main() {
    let local_unsafe_cell = UnsafeCell::new(10);
    unsafe {
        let x = UNSAFE_CELL.get();
        println!("x = {}", *x);

        let local_x = local_unsafe_cell.get();
        println!("local_x = {}", *local_x);
    }
}

假如使用Debug mode去运行它的话,那么表现良好,输出都是10,但是使用release mode去运行它的话,对于UNSAFE_CELL的值的获取就完全是UB,这是为什么?

play_ground

评论区

写评论
Lazy 2025-01-06 17:53

const常量属于一种“值”类型,类似字面量值用法。

之所以你这里没给你提示,是因为你调用的是UnsafeCell::get返回的是*mut T,这里实际上返回的是个无效指针(因为直接调用只在方法执行期间有效),当前稳定版rust对指针的使用安全性需要使用者自行保证。指针类型的溯源规则目前应该是还不稳定,所以如第一位老哥说的需要打开nightly才能看到编译器警告。

如果你的代码调用改成let x: &mut T = UNSAFE_CELL.get_mut(),立马你就能看到编译器的错误信息了。因为引用的生命周期和借用检查器不允许出现无效引用类型,并且会提示你该常量值在调用后即会drop。

error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:9:17
   |
9  |         let x = UNSAFE_CELL.get_mut();
   |                 ^^^^^^^^^^^          - temporary value is freed at the end of this statement
   |                 |
   |                 creates a temporary value which is freed while still in use
10 |         println!("x = {}", *x);
   |                            -- borrow later used here
   |
   = note: consider using a `let` binding to create a longer lived value

关于指针溯源(Provenance)规则,目前是还不完善的,我之前看的是rustdoc文档中的std::ptr模块文档,感兴趣的话可以看下

Bai-Jinlin 2025-01-06 11:34

const不要想象成一个static的那种变量,应该类比成c里#define类的东西。

TinusgragLin 2025-01-05 16:01

UNSAFE_CELL 是个 const,所以 let x = UNSAFE_CELL.get() 就是 let x = UnsafeCell::new(10).get(),如果你开 nightly,就会得到一个很详细的 warning:

warning: a dangling pointer will be produced because the temporary `UnsafeCell<i32>` will be dropped
 --> src/main.rs:3:33
  |
3 |     let x = UnsafeCell::new(10).get();
  |             ------------------- ^^^ this pointer will immediately be invalid
  |             |
  |             this `UnsafeCell<i32>` is deallocated at the end of the statement, bind it to a variable to extend its lifetime
  |
  = note: pointers do not have a lifetime; when calling `get` the `UnsafeCell<i32>` will be deallocated at the end of the statement because nothing is referencing it as far as the type system is concerned
  = help: for more information, see <https://doc.rust-lang.org/reference/destructors.html>
  = note: `#[warn(dangling_pointers_from_temporaries)]` on by default

不过回到 stable 就看不到,开 clippy 也没有,不知道下个版本会不会加上。

1 共 3 条评论, 1 页