简体   繁体   中英

Why mutable reference on `const` is not an error?

Since the Rust book v1.30 says explicitly:

... constants in Rust have no fixed address in memory . This is because they're effectively inlined to each place that they're used. References to the same constant are not necessarily guaranteed to refer to the same memory address for this reason.

why the compiler allows getting a mutable reference on a const variable. It only says a warning/note and not an error.

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

To test this, a trivial code sample:

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

As expected, the memory location of the const may change (specially when accessed using a mutable reference).

There are some cases where it will behave predictably. In particular, if you re-use the same reference:

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;
}

I can't immediately think of a case where doing this is beneficial compared with adding a local binding first. But it can't cause memory unsafety, so it isn't a huge worry.

Given that this was not an error in Rust version 1.0, the Rust developers cannot later make it an error, because that would break backwards compatibility.

Taking a mutable reference to a constant creates a new, temporary variable. Your code is treated by the compiler as:

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
}

Try in playground

This seems like a weird way to do things, there are legitimate use cases for this behaviour when constants are function pointers or have interior mutability .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM