为什么在“const”上的可变引用不会报错?

3

由于Rust book v1.30明确表示:

... Rust中的常量在内存中没有固定的地址。这是因为它们在使用它们的每个位置上都被有效地内联。由于这个原因,对同一常量的引用不能保证引用相同的内存地址。

那么为什么编译器允许获取一个const变量的可变引用。只会发出警告/提示而不是错误。

warning: taking a mutable reference to a `const` item
 --> src/main.rs:5:22
  |
6 |     println!("{:p}", &mut VALUE);
  |                      ^^^^^^^^^^
  |
  = note: `#[warn(const_item_mutation)]` on by default
  = note: each usage of a `const` item creates a new temporary
  = note: the mutable reference will refer to this temporary, not the original `const` item

为了测试这个,一个微不足道的代码示例:
fn main() {
    const VALUE: u64 = 0;
    println!("{:p}", &VALUE);      // 0x10622ed78 // same
    println!("{:p}", &VALUE);      // 0x10622ed78
    println!("{:p}", &mut VALUE);  // 0x7ffee9a08890 // different
    println!("{:p}", &mut VALUE);  // 0x7ffee9a088e8
}

Rust playground

->

{{链接1:Rust playground}}

正如预期的那样,const的内存位置可能会发生变化(特别是在使用可变引用时访问时)。

1
我认为警告已经很清楚地说明了正在发生的事情。你没有得到一个对const变量的可变引用,也没有改变const变量的内存位置。你正在创建许多不同的临时变量,它们是从VALUE复制而来的,并且这些临时变量的地址是不同的。如果你将VALUE替换为像(1+2)这样的东西,那么相同的事情也会发生,就像println!("{:p}", &mut (1+2));一样。 - Brian61354270
2
如果您认为像&mut 1这样的东西是完全允许的(有时非常有用!),那么没有理由不允许&mut FOO,其中FOO是1。但是可能会发现大多数&mut FOO实际上都是错误的,这就是为什么引入了警告的原因。 - user4815162342
2个回答

6

在某些情况下,它会表现得可预测。特别是,如果您重复使用相同的参考:

const VALUE: u64 = 0;

fn main() {
    let v = &mut VALUE;
    add_1(v);
    add_1(v);
    assert_eq!(*v, 2);
}

fn add_1(v: &mut u64) {
    *v += 1;
}

我目前想不到有什么情况下这样做比先添加本地绑定有益。但它不会导致内存不安全,因此不是一个很大的问题。

鉴于这在Rust 1.0版本中不是错误,Rust开发人员以后不能将其变成错误,因为那样会破坏向后兼容性。


4
获取常量的可变引用会创建一个新的临时变量。编译器将您的代码视为:
fn main() {
    const VALUE : u64 = 0;
    println!("{:p}", &VALUE);      // 0x10622ed78 // same
    println!("{:p}", &VALUE);      // 0x10622ed78
    let mut tmp1 = VALUE;
    println!("{:p}", &mut tmp1);  // 0x7ffee9a08890 // different
    let mut tmp2 = VALUE;
    println!("{:p}", &mut tmp2);  // 0x7ffee9a088e8
}

在游乐场中尝试

这似乎是一种奇怪的做法,但当常量是函数指针具有内部可变性时,这种行为有合理的用例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接