Rust 1.83.0 released
This release includes several large extensions to what code running in const contexts can do. This refers to all code that the compiler has to evaluate at compile-time: the initial value of const and static items, array lengths, enum discriminant values, const generic arguments, and functions callable from such contexts (const fn).
There are also quite a few new stabilized APIs.
Posted Nov 30, 2024 20:24 UTC (Sat)
by alsuren (subscriber, #62141)
[Link] (16 responses)
Posted Nov 30, 2024 22:58 UTC (Sat)
by proski (subscriber, #104)
[Link]
Posted Dec 1, 2024 0:20 UTC (Sun)
by tialaramex (subscriber, #21167)
[Link] (14 responses)
The (as yet unfinished) 2024 Edition guide lists 13 distinct items, let's review:
1. RPIT lifetime capture rules -- The use<'lifetime> feature landed already, today it's sometimes useful to properly say what you meant, if you omit this your code doesn't compile and you need an awkward workaround. in many cases in 2024 Edition what you meant will be the default anyway, so what people write naturally will just compile in most cases.
2. if let temporary scope -- You can of course write an awkward match to get the same results today, but 2024 Edition will make this do what people expect.
3. Tail expression temporary scope -- You can write what you meant today but it's pretty awkward, and 2024 Edition will result in code that people would write naturally doing what they expected in more cases.
4. Match ergonomics -- Apparently "This is a placeholder, docs coming soon!". Very soon I hope then.
5. Unsafe extern blocks -- New "unsafe extern" syntax becomes mandatory instead of "extern". This feature landed already, it will merely become mandatory, if you don't need it this is one word of extra boilerplate, if you do need it then this unlocked a valuable feature (safe external functions)
6. Unsafe attributes -- Rust 1.82 (so, last release) made it possible to say that your attribute is unsafe, 2024 Edition will require that some specific attributes are always marked this way, as previously it was possible to e.g. turn off name mangling (potentially causing chaos) for a symbol, without uttering the unsafe keyword. You can do it today, but it will become necessary in 2024 Edition
7. unsafe_op_in_unsafe_fn warning -- This warning (long available) is now default on in 2024 Edition
8. Disallow references to static mut -- Again, change to defaults for a diagnostic in 2024 Edition
9. Never type fallback change -- If you relied on the compiler inferring that the type of ! could be () now it doesn't in 2024 Edition, by default relying on this already caused a warning, now it's fatal
10. Macro Fragment Specifiers -- in declarative ("by example" or "macro rules") macros the meta-type expr now includes const expressions and the destructuring placeholder _ in 2024 Edition
11. Missing macro fragment specifiers -- in declarative macros there was a lint to point out mistakes where you forgot to say the meta-type for a variable, in 2024 Edition this becomes a hard error.
12. gen keyword -- "gen" is now reserved in 2024 Edition, ready for some day providing generators
13. Reserved syntax -- carving off some unused syntax for possible future use
Of these mostly only 2, 3 and maybe 4 (if we ever find out what it is) involve significant work that we can't just use today by changing our defaults or habits. 10 involved some behind the scenes work but is not something most people will ever need to care about, many of the others are stuff that existed a week ago, a month ago, even five years ago, but the defaults and the way you're encouraged to write idiomatic Rust change in the next edition to prefer or even require this practice.
Posted Dec 1, 2024 1:35 UTC (Sun)
by intelfx (subscriber, #130118)
[Link] (2 responses)
I believe this is the one:
Relevant tracking issues:
Posted Dec 1, 2024 17:34 UTC (Sun)
by NYKevin (subscriber, #129325)
[Link] (1 responses)
If you have written old code that contains complicated matching logic, then you should follow the instructions in [1] to make sure that your patterns don't silently change meanings. If you have written complicated matching code inside of a macro_rules!, then the automated migration logic may be unable to fix things without human intervention, but "such code should be extremely rare in practice" according to the RFC.
If you forget to do that (and just bump the edition without doing any migration), then it will *probably* fail to compile in some other part of your code, because the silent change in meaning also changes the type of the resulting binding(s). So I think this is unlikely to cause a major disaster even for very large and disorganized codebases. In an absolute worst-case scenario (where you have a lot of macros with a lot of complicated matching logic, and you lack the developer-hours to fix it all by hand), you can just keep using the old edition indefinitely.
[1]: https://doc.rust-lang.org/edition-guide/editions/transiti...
Posted Dec 1, 2024 19:38 UTC (Sun)
by NYKevin (subscriber, #129325)
[Link]
Self-nitpick: This is obviously not the case if the binding is _, because that is a write-only binding and so there will never be any subsequent code that tries to use it. There are probably also a few very weird cases where type inference causes the new type to be accidentally compatible with old code, but in some of those cases (most likely involving Copy and/or Deref) the resulting program will still have the same overall behavior anyway (most real-world implementations of Deref try very hard to make copying and immutable borrowing almost-but-not-quite equivalent, so there are probably some cases where the program as a whole doesn't "care" which you do).
Posted Dec 1, 2024 18:38 UTC (Sun)
by khim (subscriber, #9252)
[Link] (10 responses)
This sentence requires clarification. If you recall that most languages out there are Turing complete then your sentence is, quite obviously, true. But on the other hand there are such things as Turing tarpits… and in that sense, when you talk about ergonomics… Rust editions are very important. E.g.: if let temporary scope -- You can of course write an awkward match to get the same results today, but 2024 Edition will make this do what people expect. That's only half-truth, and if that was the only reason to introduce that change then it wouldn't have been pushed at the 11th hour. Sure, Rust 2024 changes the scope of The real target here are let chains. The ability to write something like That is why And similarly with many other features: things that they are enabling are “already possible” today, but they sure are awkward in many cases.
Posted Dec 1, 2024 20:57 UTC (Sun)
by NYKevin (subscriber, #129325)
[Link] (9 responses)
* Encapsulated possibility: Whether you can create a language construct (trait, struct, function, etc.) that has some semantic property. For example, the Copy trait makes it possible to create types with value (copy-instead-of-move) semantics. Technically, you don't "need" to create objects with this semantic property, because there are unsafe raw pointer methods that do the same thing, but the Copy trait makes it possible for a type to affirmatively declare that this is a supported operation, which facilitates encapsulation (i.e. foreign code does not need to magically "know" that it can copy your type, because the trait communicates this information).
Posted Dec 1, 2024 21:15 UTC (Sun)
by khim (subscriber, #9252)
[Link] (8 responses)
And where in that classification would let chains live? Initially it feels as if what they offer is tiny, very minor, extension… but reality is somewhat different. Of course instead of That's not a rewrite of something from scratch, but it's much harder to understand and follow logic…
Posted Dec 1, 2024 21:26 UTC (Sun)
by NYKevin (subscriber, #129325)
[Link]
In this case, you are extending if let ... into if let ... && let ..., so I would consider this a case of "local possibility." The definition I gave may be a tiny bit too strict to allow for that, but oh well (again, English is not Lojban).
Posted Dec 3, 2024 10:12 UTC (Tue)
by aliceryhl (subscriber, #168048)
[Link] (6 responses)
Posted Dec 3, 2024 10:22 UTC (Tue)
by khim (subscriber, #9252)
[Link] (4 responses)
Can you show me how that's done with match? Note that AFAIK you can't have something like this in one
Posted Dec 3, 2024 10:51 UTC (Tue)
by aliceryhl (subscriber, #168048)
[Link] (3 responses)
Ah you are right, it's not as simple as I thought. Match doesn't always provide a replacement. Though for this specific case, you can do this:
Posted Dec 3, 2024 18:19 UTC (Tue)
by khim (subscriber, #9252)
[Link] (1 responses)
Would that work if Let chains are just simply incredibly natural to expect – it's not that it's best solution, but more of “hey, that obviously should work… but compiler just simply doesn't want to understand me”.
Posted Dec 3, 2024 18:22 UTC (Tue)
by aliceryhl (subscriber, #168048)
[Link]
Posted Dec 3, 2024 18:28 UTC (Tue)
by matthias (subscriber, #94967)
[Link]
Posted Dec 3, 2024 11:00 UTC (Tue)
by mathstuf (subscriber, #69389)
[Link]
Even more below the surface
Rust 2024 will be stabilized in Rust 1.85 so the will be one more release before that.
Indeed, there is a lot of work to enable transition to Rust 2024. For instance, the precise capturing of lifetimes enabled in Rust 1.82 would make it possible to change the default behavior in Rust 2024 while allowing the old behavior to be selected in needed.
Even more below the surface
Some more below the surface
Some more below the surface
- https://rust-lang.github.io/rfcs/3627-match-ergonomics-2024.html
- https://github.com/rust-lang/rust/issues/123076
- https://github.com/rust-lang/rust/issues/131414
Some more below the surface
Some more below the surface
> For the most part new Editions don't change what's possible
Some more below the surface
if let
in a very minor and safe – but no backward compatible way… but why?if Some(foo) = bar && Some(baz) = foo.qux
is a much requested feature, it was almost stabilized two years ago and then… oops, the whole thing is unsound and generates 🧊 left and right.if let
semantics is changed in Rust 2024 and that is why let chains, now, would only be available in Rust 2024.Some more below the surface
* Local possibility: Given you have some code that does semantic thing X using syntactic construct Y, whether you can extend this use of Y to accomplish semantic thing Z in addition to X (as opposed to removing Y and rewriting the whole X+Z thing from scratch). For example, the use of RPIT makes it much easier to change the return type of a function, so you can extend it to do a wider variety of things without having to modify any callsite (even in foreign code).
* Global possibility: Whether it is possible to write a program that does semantic thing X, under syntactic restriction Y. In the case of Rust, Y is often "don't use unsafe," and X is often more implementation-specific than what computability theory traditionally accounts for (X might involve doing something without unnecessary intermediate copying, or it might involve calling into some FFI binding, etc.).
Some more below the surface
if let Some(foo) = bar && let Some(baz) = foo.qux
you can already, today, write two nested ifs… but then you have two else's, too! You can still write something like this, if you use goto
(called break
in Rust):
'out: {
if let Some(foo) = bar {
if let Some(baz) = foo.qux {
println!("{baz}");
break 'out;
}
}
println!("No baz");
}
instead of
if let Some(foo) = bar && let Some(baz) = foo.qux {
println!("{baz}");
} else {
println!("No baz");
}
Some more below the surface
Some more below the surface
Some more below the surface
foo.qux
doesn't make sense if bar
is not Some
.match
and if there are two then we are back to square one: need some way to handle two catch-all cases in match
.Some more below the surface
match bar {
Some(foo @ MyStruct { qux: Some(baz) }) => println!("{baz}"),
_ => println!("No baz"),
}
Some more below the surface
qux
is not the sole field in that struct?
Yes, but it requires that it is a field, and not e.g. the return value of a function call on Some more below the surface
foo
.
You can use a match, but you also just use if let. The patterns work the same in match, if let, and while let: Some more below the surface
if let Some(Foo { qux: Some(baz)}) = bar {
println!("{baz}");
} else {
println!("No baz");
}
The "foo @" in your example is only needed if you want to also access foo. But this only works if the type of baz is Copy because baz is otherwise moved out of foo.
Some more below the surface