Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

std::sync::Once::call_once executes closure multiple times when create as const #93464

Closed
flopacero opened this issue Jan 29, 2022 · 7 comments
Closed
Labels
C-bug Category: This is a bug.

Comments

@flopacero
Copy link

I tried this code:

use std::sync::Once;

const INIT: Once = Once::new();

fn main() {
    init();
    init();
}

fn init() {
    println!("Init called");
    INIT.call_once(|| {
        println!("Initialize")
    });
}

I expected to see an output:

Init called
Initialize
Init called

Instead, the output:

Init called
Initialize
Init called
Initialize

The code starts to works as expected once const INIT is replaced by static INIT.
It can be reproduced on rust playground either on stable/betta/nightly builds.

Meta

rustc --version --verbose:

rustc 1.58.1 (db9d1b20b 2022-01-20)
binary: rustc
commit-hash: db9d1b20bba1968c1ec1fc49616d4742c1725b4b
commit-date: 2022-01-20
host: x86_64-pc-windows-msvc
release: 1.58.1
LLVM version: 13.0.0
Backtrace

$ RUST_BACKTRACE=1 cargo build
   Compiling once_bug v0.1.0 (C:\Users\Andrey_Kviatko\Documents\projects\rust\playground\once_bug)
    Finished dev [unoptimized + debuginfo] target(s) in 0.59s

@flopacero flopacero added the C-bug Category: This is a bug. label Jan 29, 2022
@Aaron1011
Copy link
Member

This is because each use of INIT results in a new copy of the underlying Once. We have a CONST_ITEM_MUTATION lint that catches calls to methods that take &mut self - however, Once uses interior mutability, so the lint doesn't catch it.

We may want to consider uplifting Clippy's lint against consts with interior mutability.

@cuviper
Copy link
Member

cuviper commented Jan 30, 2022

A simpler example is a const atomic, and similarly any mutation will only affect a temporary copy.

@flopacero
Copy link
Author

Along with a lint it might be also helpful to add some note in docs for std::sync::Once. I can't remember if I've ever seen anything about const resulting in copy of an underlying variable. Besides Once::new is const fn so it's allowed to be used in const context. Turns out storing it as a const is likely not what you need.

@the8472
Copy link
Member

the8472 commented Jan 30, 2022

They const keyword docs mention this. https://doc.rust-lang.org/std/keyword.const.html

const items looks remarkably similar to static items, which introduces some confusion as to which one should be used at which times. To put it simply, constants are inlined wherever they’re used, making using them identical to simply replacing the name of the const with its value. Static variables, on the other hand, point to a single location in memory, which all accesses share.

@scottmcm
Copy link
Member

scottmcm commented Feb 6, 2022

I'll note that the very first example on the Once page is

static START: Once = Once::new();

Which is a pretty strong encouragement to use static, not const.

@matklad
Copy link
Member

matklad commented Feb 23, 2023

Duplicate of #40543?

@Dylan-DPC
Copy link
Member

Closing this as duplicate of #40543

@Dylan-DPC Dylan-DPC closed this as not planned Won't fix, can't repro, duplicate, stale May 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug.
Projects
None yet
Development

No branches or pull requests

7 participants