Const LazyCell calls initializer mutliple times

Consider the following code:

    #[test]
    fn foo() {
        const FOO: LazyCell<()> = LazyCell::new(|| eprintln!("init_FOO"));
        let bar: LazyCell<()> = LazyCell::new(|| eprintln!("init_bar"));
        LazyCell::force(&FOO);
        LazyCell::force(&FOO);
        LazyCell::force(&bar);
        LazyCell::force(&bar);
    }

I would expect running this test to print:

init_FOO
init_bar

However, I instead see:

init_FOO
init_FOO
init_bar

It appears that by moving the LazyCell to a global const, it stops working as expected, and instead recalls the initializer on every access.

My motivation for using LazyCell is to have the cell be a global to avoid repeating an expensive initailization call, and I don't think there is an easy way to accomplish that without making it const.

A const isn't a global variable. It behaves more like the compiler is just copy-pasting the const's value everywhere that it gets used. So your two LazyCell::force(&FOO) lines both create a new copy of FOO.

If you want a global variable, you need to use static. You'll probably need to switch your LazyCell to a LazyLock (or provide some sort of other synchronization on the LazyCell, but just using LazyLock is easiest) in that case.

2 Likes

Did you read the compiler warning? It is explained quite well:

warning: mutation of an interior mutable `const` item with call to `force`
 --> src/lib.rs:6:5
  |
6 |     LazyCell::force(&FOO);
  |     ^^^^^^^^^^^^^^^^^---^
  |                      |
  |                      `FOO` is a interior mutable `const` item of type `LazyCell<()>`
  |
  = note: each usage of a `const` item creates a new temporary
  = note: only the temporaries and never the original `const FOO` will be modified
  = help: for more details on interior mutability see <https://doc.rust-lang.org/reference/interior-mutability.html>
  = note: `#[warn(const_item_interior_mutations)]` on by default
help: for a shared instance of `FOO`, consider making it a `static` item instead
  |
4 -     const FOO: LazyCell<()> = LazyCell::new(|| eprintln!("init_FOO"));
4 +     static FOO: LazyCell<()> = LazyCell::new(|| eprintln!("init_FOO"));
  |
6 Likes