Interior Mutability in Associated Constants

Hi All,

I'm trying to create a mutable associated constant, but to my surprise following does not work as expected.

use std::cell::Cell;

trait A {
    const V: Cell<bool> = Cell::new(true);
}


struct S;
impl A for S {}

fn main() {
    println!("{:#?} ",S::V);
    S::V.set(false);
    println!("{:#?} ",S::V);
}

(Playground)

Output:

Cell {
    value: true,
} 
Cell {
    value: true,
} 

S::V.set(false); should have change the interior value, but clearly, it hasn't.
Is this a limitation of associated constants or am I doing something wrong here?

Thanks a lot

You can't mutate constants at runtime, because constants are not exist at runtime. Constants are compile-time-only thing and referring their name act like copy-pasting their declaration. So S::V.set(false); would produces same machine code as Cell::new(true).set(false), which obviously doesn't change something else.

7 Likes

The latter. The outcome is a result of you misunderstanding how const values work.

In a sense they work a lot like macro invocations that have no arguments: they're expanded at compile time, verbatim.

In other words, it's as if you have written this instead:

use std::cell::Cell;

trait A {
    const V: Cell<bool> = Cell::new(true);
}


struct S;
impl A for S {}

fn main() {
    println!("{:#?} ", Cell::new(true));
    Cell::new(true).set(false);
    println!("{:#?} ", Cell::new(true));
}

In particular, consts do not contain runtime state. They're comparable to value literals in the sense that they produce the valued they're defined on.

3 Likes

Also, we are talking about constants, here, so it's a good thing they can't be made mutable :grinning_face_with_smiling_eyes:.

What you may want is an "associated static":

use ::crossbeam::atomic::AtomicCell;

trait A {
    #[allow(nonstandard_style)]
    fn V()
      -> &'static AtomicCell<bool>
    ;
}

struct S;
impl A for S {
    fn V()
      -> &'static AtomicCell<bool>
    {
        static STATIC: AtomicCell<bool> = AtomicCell::new(true);
        &STATIC
    }
}

fn main ()
{
    println!("{:#?} ",S::V().load()); // true
    S::V().store(false);
    println!("{:#?} ",S::V().load()); // false
}
  • Playground

  • Note that such a thing requires thread-safe (Sync) interior mutability, since such globals can be accessed and thus, potentially mutated, from multiple threads in parallel.

  • Finally, if you find the () on V a bit cumbersome, there is a Deref trick to hide it under the rug:

    println!("{:#?} ",S::V.load()); // true
    S::V.store(false);
    println!("{:#?} ",S::V.load()); // false
    
2 Likes

What's wrong with std::sync::atomic::AtomicBool that you need crossbeam::atomic::AtomicCell?

Simpler to transpose from Cell to AtomicCell, mental-model-wise, and relatedly, avoids the pitfall of somebody using Ordering::Relaxed and thus potentially featuring race conditions.

It's still not great, since AtomicCell is technically unsound when the type bundles padding bytes (e.g., one oughtn't use stuff like AtomicCell<(u8, u16)>).

Clippy rightfully complains about interior mutability in consts:

warning: a `const` item should never be interior mutable
 --> src/main.rs:4:1
  |
4 | const FOO: Cell<i32> = Cell::new(42);
  | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  | |
  | make this a static item (maybe with lazy_static)
  |
  = note: `#[warn(clippy::declare_interior_mutable_const)]` on by default
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const

warning: a `const` item with interior mutability should not be borrowed
 --> src/main.rs:7:5
  |
7 |     FOO.set(123);
  |     ^^^
  |
  = note: `#[warn(clippy::borrow_interior_mutable_const)]` on by default
  = help: assign this const to a local or static variable, and use the variable here
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const
3 Likes

Thanks for all your responses and sorry for being MIA for a whole week

I was under the wrong impression of how associated const work.

Thanks, @Yandros
associated static is exactly what I was trying to achieve :blush:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.