Forcing edition "2021" with latest rustc/rustup

How to force newest cargo/rustc to build for "2021" edition ?
My project has edition = "2021" in the Cargo.toml but I'm still getting diagnostics for the 2024 edition.
I've even created rust-toolchain.toml file with:
[toolchain]
channel = "stable"
edition = "2021"

and I still get edition 2024 diagnostics/warnings.
My understanding is that this is not normal behaviour.
Any tips ?

Edition is set in Cargo.toml and nowhere else (unless you are using a different build system than Cargo).

Can you please post the diagnostics that you are getting — the full output from the cargo check command from start to finish — so we can see what is going on? (Here is how to format code/output in your posts.)

There are warnings that might mention edition 2024, but warn by default on all previous editions for reasons such as…

…it being suboptimal code anyway

…or the future-incompatibility being not only with edition 2024 but also with potential future changes in all editions


Maybe you got one of these?

4 Likes

Unfortunately I can't share whole output.

This is my rustup:

stable-aarch64-apple-darwin - Up to date : 1.85.1 (4eb161250 2025-03-15)
nightly-aarch64-apple-darwin - Up to date : 1.87.0-nightly (75530e9f7 2025-03-18)
1.79-aarch64-apple-darwin - Up to date : 1.79.0 (129f3b996 2024-06-10)
rustup - Up to date : 1.28.1

So, I switched from 1.79 to 1.85 and now I'm seeing "static_mut_refs" warnings (and other warnings about "never read" fields that were not there before):

warning**: creating a shared reference to mutable static is discouraged**

= note: for more information, see https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html

My bad, now I see that in the "2024" edition "static_mut_refs" is an error.
I'm guessing that in some of the previous versions warning was introduced for "static_mut_refs" ?

Using static mut is hazardous. Taking references to one is particularly hazardous, because you have to follow the borrowing rules without any help from the borrow checker. Programs that use static mut very often end up using it unsoundly.

You should look into alternatives — usually a non-mutable static containing an interior-mutable type of some sort — if nothing else works, you should still prefer a custom interior-mutable type, because that makes it possible to define a safe (or at least less subtly dangerous) API suitable for your use case.

If you can share the static mut itself and what uses of it look like, we can try to suggest solutions.

(and other warnings about "never read" fields that were not there before):

Those are likely due to improvement in the dead code analysis. I don’t see any superficially relevant PRs mentioning dead code for 1.85.0, though.

The error message itself already links to the edition guide, which contains a pretty good amount of explanation on static mut and this lint already:


Regarding dead code analysis of fields, I recently-ish felt like the lint ignoring derived Clone impls might have been newer (e.g. compared to Debug impls), though I'd have to check the issue/PR tracker and/or release notes to get a number on that.

edition is not a version of the compiler, but a version of the code.

The compiler will get new warnings anyway.

Edition only makes it possible to continue to compile old code that isn't compatible with new syntax or new rules, but that doesn't stop the compiler from warning you about what has been deprecated or removed, or simply being better at detecting problems that always existed.

3 Likes

Nevermind, Clone appears relevant for a longer time already, it seems like the newer change was that tuple struct fields get warnings about unused fields at all now. Maybe it was affecting structs in minimal test examples of mine and that’s the recent-ish change I remember; (looks like version 1.77).

I have some "static mut" vars that are used as a global state, those are initialized early in the main() before "worker threads" are spawned (I'm assuming memory consistency at this point).
Later in the app only read references are taken from them. From what I understand of rust memory safety this is considered "sound".

Still, not all sound unsafe is good unsafe. You could probably just as well use OnceLock instead. (Unsurprisingly, the linked edition guide entry has a section on this, too.)

5 Likes

By using OnceLock as @steffahn suggested, the compiler checks that you're doing this right -- that you do have memory consistency between initialization and reader access, and that you haven't accidentally modified the state after readers have accessed it. This sort of static/compile time checking is what Rust is all about.

1 Like