The Rust 2018 edition landed about a year ago now, and the way these things go, I expected to skim the release notes, nod, and carry on writing exactly the same Rust as before. That's not what happened. A couple of the changes have genuinely rewired how I write the language, and it's only with a year of hindsight that I can say which ones.
The headline features got the attention. async/await was the thing people talked about, and it's important, but it didn't change my day-to-day much because most of what I write isn't I/O-bound in that way. The changes that actually mattered were smaller and less glamorous.
The module system finally makes sense
Pre-2018 module paths were a source of low-grade confusion I'd stopped noticing, the way you stop noticing a draught. extern crate at the top of every file, mod.rs everywhere, paths that were relative or absolute depending on rules I never fully internalised.
2018 cleaned this up. extern crate is mostly gone, paths starting with a crate name or crate:: are unambiguous, and you can drop the mod.rs dance in favour of a sibling file. None of it is exciting. All of it removed a small tax I was paying on every new file.
Non-lexical lifetimes changed what I dare to write
This is the one. NLL means the borrow checker reasons about where a borrow is actually used, not where its lexical scope happens to end. In practice, code that was perfectly safe but that the old checker rejected now just compiles.
let mut v = vec![1, 2, 3];
let first = &v[0];
println!("{}", first);
// Old checker: error, `first` still "borrows" v here even though we're done.
// 2018 with NLL: fine, the borrow ended after the println.
v.push(4);
It sounds minor. It isn't. Before NLL I'd developed a set of little contortions, an extra scope here, a clone() there, to placate a checker that was being more conservative than it needed to be. I'd internalised those workarounds so thoroughly I'd forgotten they were workarounds. NLL let me delete a layer of defensive habits I didn't know I had.
The bit nobody mentions
What surprised me most is how little drama the edition system caused. Editions are opt-in per crate, the compiler keeps building the old ones, and you migrate when you fancy it with cargo fix. I'd braced for a Python 2-to-3 saga. Instead it was a flag in Cargo.toml and an afternoon.
A year on, that's the thing I'd point new people at. Not async, not any single feature, but the fact that Rust found a way to make breaking improvements without breaking everyone. The 2018 edition didn't just change how I write Rust. It made me trust that the next set of improvements won't cost me a weekend.