A moderately common source of beginner confusion is that they encounter unexpected behavior due to not specifying a language edition, and getting the default 2015 edition. This can be either due to calling rustc instead of cargo and not specifying any options, or writing a Cargo.toml from scratch (that is, not using cargo new) without remembering to include the edition field.
Users in this position encounter various surprising behaviors, such as needing extern crate to use dependencies, and not being able to write async code. As new language editions are released, the number of such surprises will increase.
Therefore, I propose that tools should warn if the edition is unspecified. More precisely, there should be a warning in any of the cases where 2015 is chosen by default:
rustc shall warn if no --edition is passed.
cargo shall warn if the manifest of a package does not contain a package.edition in its Cargo.toml.
The warning would of course be hidden for downloaded dependencies, like other warnings already are. It would tell the reader that "2015" should be used if the code is old code not being modified, and the current edition should be used for new code.
There's some mildly interesting complications in implementing this — for example, Cargo currently translates explicit edition = "2015" into not passing --edition to rustc at all, and there are a very large number of UI tests (tests that assert the output of the compiler, including warnings) in the rust-lang/rust repository that have unspecified edition. All that I know of are solvable, but it made the problem bigger than "I'll just make a PR or two" as I thought when I came up with this idea.
So, I'd like to know: Does this sound like the right direction to go in? Are there situations where the warning would not be straightforwardly fixable?
I'm all for it. I presumably know what I'm doing and have still confused myself with an accidentally missing edition specification as well. I'd even suggest doing so for virtual workspace manifests without a resolver key (usually inferred from package edition) as well.
While it's a bigger change, the change should still be reasonably straightforward:
Make cargo always pass --edition.
Emit the warning. Bless the changed UI test outputs containing the warning to automatically update them.
Slowly update UI tests over time to specify edition.
cargo should flat out error if the edition is unspecified (with a carve out for dependencies)
2015 default is going to look increasingly ridiculous as the time goes by, so it seems like it’s the question of when.
And now seems like a decent time: 2015 was a while ago, and a lot of code from those days doesn’t build any more anyway (there were a couple of breaking macro changes, and mem::unitialized).
This would break backwards compatibility, repeatedly. Emitting a loud warning seems fine, but using the latest edition by default would break existing Rust 2015 code in mysterious ways.
Worse, using the latest edition would induce new code to rely on that, which will then cause that code to get broken every 3 years, eliminating some of the benefit of the edition system.
If we were going to change the default and break such builds, I'd say we should change rustc to error out without an edition specified, rather than using the latest edition. But before we consider doing that we'd need to warn for a nice long time.
For a first pass, I think it makes sense to emit a warning, and let the usual warning-suppression for dependencies handle it. That'll get any actively maintained crate switching over, which will then just leave all the existing crates.
I'm fine with a warning on an unset Edition in Cargo.toml.
Since cargo-script is a new thing and needs very low overhead for its target audience, it defaults the edition to "current" with a warning that it is unset. I feel this gets us a nice middle ground of people assuming they will get the currently documented behavior while ensuring long-lived scripts don't break (since they'll be annoyed into silencing the warning).
For what it's worth, feel free to file tickets for errors that are caused by using later edition features in earlier editions. There's precedent for errors telling you that you're using the wrong edition and to pass in --edition=2021 when needed, but I'm sure there are tons of cases we don't handle today.
I think doing this in cargo is a great idea. I'm less sure about doing it for rustc. My workflow of working on the compiler is certainly not standard, but I (and everyone else who frequently invoked rustc for testing things probably) would find such a warning really annoying and would probably try to hack in some environment variable to turn it off (that I would also want to enable in UI tests). I guess adding such an environment variable would be fine, but also comes with downsides.
No one should be using rustc directly anyways, but I'm not sure how we can effectively tell people about that.
Many people do, and it's valid to use rustc without cargo. (It's annoying when those uses break other rust conventions, or make it deliberately difficult to integrate crates and cargo as well, but it's still a valid use case.)
Do we promise a stable cli for rustc across versions? I searched the rustc dev guide and the rustc book but didn't see anything definitive.
To clarify, you mean Rust 2015 code using a build system other than cargo?
Thoughts not necessarily directed at you, but which I want to write down before I forget
Build systems which wrap around rustc (e.g. cargo, buck) should already be passing --edition for every invocation. This might be an unnamed rule in the software world, but I generally expect wrapper tools to provide the "maximal set" of flags to the tools they wrap, even those flags that could technically be omitted.
As for users who are directly using the rustc cli, I suspect they skew towards experts in build-systems and compilers who are well-accustomed to working in very unstable corners of the Rust ecosystem. Breaking changes are their bread and butter.
I wouldn't go as far as "maximal set", but I agree that build systems that invoke rustc for the user should always be passing --edition. (Of course, if the user is writing the rustc invocation themselves without help from the build tool, then they're the one responsible for adding --edition.)
I think it's a reasonable idea to furthermore include a suggestion to use cargo in the warning that invocation of rustc without --edition would generate. I think a significant portion of the users that use rustc manually and forget the --edition argument are users that mainly made the mistake of not using cargo; this is in line with the "no one should be using rustc directly anyways" kind of sentiment above, the weaker version of which is "no one should be using rustc directly unless they know exactly why they 'need' to use it manually"[1]
Also, in addition to usage of explicit --edition 2015 for silencing the warning, it might also be reasonable to silence the warning when flags are detected that are characteristic for rustc invocation by automatic tooling, such as cargo; the warning is meant to address manual users of the rustc command, after all. This way, it would even be an option that cargo keeps current behavior of not passing --edition 2015. (I'm not sure what the best contenders are. For example, cargo probably always uses JSON output mode, so that's one potential candidate. Maybe there is no good candidate, or this is deemed too inconsistent, I'm only meaning this as an idea that might be sensible, anyways.)
and the set of users that don't know why they use rustc manually and the set of users that don't know how to use rustc manually (i.e. to pass --edition) should have good overlap ↩︎
My gut feeling is that the overwhelming majority of cargo-less rustc invocations are just people trying isolated snippets of code, as a local playground of sorts.
This use case will be fixed by cargo scripts, right? And if it doesn't, for this use case you almost certainly don't want to use the 2015 edition and explicitly specifying the edition would suppress the warning.
No, because at least two-thirds of the time, if I'm messing with an isolated snippet, I'm about to file a bug report on something, and I want to know whether the problem changes or disappears if I take cargo out of the loop.
I might want cargo to tell me the options I need to give rustc to make some already-built dependency crate available to the test program, but that's it.
Indeed. In the context of test snippets, I wouldn't mind it being an error not to specify an edition.
Come to think of it, why can't I specify the edition in the code? Like
FYI I have posted cargo#13499 to ensure cargo always passes --edition so rustc would be able to produce a warning without negatively affecting cargo users, if the compiler team so chooses.
Still working on the cargo warning. Its a lot of test updates...