为什么在不安全的块中,静态可变变量可以有多个独占借用?

huangapple go评论85阅读模式
英文:

Rust: why multiple exclusive borrows are possible for static mut variables in unsafe blocks?

问题

在Rust中,确保mut借用是独占的是一个强不变量。试图通过std::mem::transmute绕过这一规则被视为未定义行为,因为编译器使用这一不变量来应用一些优化。

即使使用unsafe,也不能禁用借用检查器。然而,似乎仍然可以禁用静态可变(mut)变量的借用检查。

例如,以下代码不会编译通过:

fn main() {
    let mut s = String::new();
    unsafe {
        let a = &mut s;
        let b = &mut s;
        println!("Result: {a} {b}");  
    }
}

错误信息如下:

error[E0499]: cannot borrow `s` as mutable more than once at a time
  --> src/main.rs:18:17
   |
17 |         let a = &mut s;
   |                 ------ first mutable borrow occurs here
18 |         let b = &mut s;
   |                 ^^^^^^ second mutable borrow occurs here
19 |         println!("{a} {b}");  
   |                   --- first borrow later used here

但以下代码可以编译并正常工作:

static mut s: String = String::new();

fn main() {
    unsafe {
        let a = &mut s;
        let b = &mut s;
        println!("Result: {a} {b}");  
    }
}

为什么会有这种差异呢?

这是否是一种应该被移除的遗留特性?或者是由于静态变量的性质或与C或LLVM的代码互操作性有一些特定的需求或要求?

英文:

In Rust, ensuring mut borrows are exclusive is a strong invariant. Wanting to circumvent that with std::mem::transmute is considered undefined behaviour, because the compiler uses this invariant to apply some optimisations.

Even with unsafe, disabling the borrow checker is not possible. However, it seems it is still possible to disabling that for static mut variables.

For instance, this does not compile:

fn main() {
    let mut s = String::new();
    unsafe {
        let a = &mut s;
        let b = &mut s;
        println!("Result: {a} {b}");  
    }
}

Error is:

error[E0499]: cannot borrow `s` as mutable more than once at a time
  --> src/main.rs:18:17
   |
17 |         let a = &mut s;
   |                 ------ first mutable borrow occurs here
18 |         let b = &mut s;
   |                 ^^^^^^ second mutable borrow occurs here
19 |         println!("{a} {b}");  
   |                   --- first borrow later used here

But this code compiles and works fine:

static mut s: String = String::new();

fn main() {
    unsafe {
        let a = &mut s;
        let b = &mut s;
        println!("Result: {a} {b}");  
    }
}

Why this difference?

Is it a kind of legacy that should be removed? Or are there some specific needs or requirements for static variables due to their nature or for code interoperability with C or LLVM, and why?

答案1

得分: 4

这是访问 static mut 不安全的原因之一。

至于为什么编译器无法验证,这是因为借用检查器使用生命周期来验证可变借用的规则是排他的。每个变量被分配一个唯一的生命周期,并且编译器验证没有人借用这样的生命周期两次。但是对 static 变量的借用总是导致一个 'static 生命周期(这是为了能够传递它,没有其他合理的生命周期),编译器无法确定它是来自哪个静态变量。当引用仅为 &'static mut 时,这在一般情况下也变得不可能,因为我们失去了关于它们来自何处的所有信息。

这也是正确使用 static mut 如此困难的原因之一,以及为什么你应该以任何代价避免它。当您需要不安全可变的静态变量时,请优先使用带有 UnsafeCellstatic

英文:

This is one of the reasons accessing static mut is unsafe.

As for why the compiler cannot verify that, it is because the borrow checker uses lifetimes to validate the rules that mutable borrows are exclusive. Each variable is assigned a unique lifetime, and the compiler verifies that no-one borrows such lifetime twice. But a borrow of static variable always results in a 'static lifetime (this is necessary to be able to pass it around, there is no other sensible lifetime), and the compiler cannot determine what static variable it is sourced from. This becomes impossible in the general case, too, when the references are just &'static mut and we lost all information about where they came from.

This is also one of the reasons why using static mut correctly is so hard, and why you should avoid it at any price. Prefer static with UnsafeCell when you need an unsafely mutable static.

答案2

得分: 0

目前,Rust编译器无法跟踪对static mut VAR的借用。
也许将来编译器会捕捉更多类似的借用错误,
但似乎不可能实现对可变静态变量的完全借用检查
这就是为什么甚至没有人为像您这样的简单借用情况而费心去做的原因。

可以说,对于像您这样的情况,实现一些特定情况的借用检查是可能的。
然而,普通程序员很少会引入这种特定的错误。
因此,没有人费心实现这样的编译时错误,尽管对于这种简单情况是可能的。

英文:

Currently, Rust compiler is incapable of tracing borrows of static mut VAR.
Maybe compiler will catch more such borrowing mistakes in the future,
BUT it seems to be impossible to implement FULL borrow checking for mutable statics.
That's the reason no one even bothered to do it even for trivial borrowing cases like yours.

Arguably, it would be possible to implement some case-specific borrow-checks for cases like yours.
Yet, it's unlikely for an average programmer to introduce this-specific bug.
So that's why no one bothered to implement such compile time error, though it would be posible for this trivial case.

huangapple
  • 本文由 发表于 2023年7月23日 15:53:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/76747175.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定