Announcing Rust 1.27.2
blog.rust-lang.org
blog.rust-lang.org
It does indeed appear as though the new match features were designed with the theoretical model of the borrow checker in mind, to which the new borrow checker hews much more strongly than the original (in conversations with Niko Matsakis he suggests that the new borrow checker is implemented in the fashion that he had wanted to implement the original one, but the other machinery in the compiler (e.g. a CFG-aware IR like MIR) wasn't ready at the time, and wouldn't be for some years). Obviously the degree to which the original borrow checker would be deficient in the face of the new match features wasn't fully understood, or else I expect that its stabilization would have been delayed. Fortunately, NLL-aware borrowck is enabled by default in nightly right now (or should be imminently: https://internals.rust-lang.org/t/possible-stabilizations-fo... ) and hopes to reach the stable channel within a few months. :)
OP: Original poster
NLL: Non-lexical lifetimes. New Rust feature. Actually, they're still lexical, in that they're bound to a specific section of source code. It's just that the lifetime region might be smaller than a block. Variables living too long is a problem in Rust because, if they're borrowing something, they block other uses of the same thing they're borrowing. See [1].
CFG: "Control Flow Guard"? Microsoft feature to prevent overwriting return points on the stack? Not sure about this one.
IR: "Intermediate representation", the data format used between compiler passes. Now broken down within Rust to a "high level representation" (HIR), a "medium level representation" (MIR), and a low-level representation. The low level representation is called LLVM, not "LIR", because it's borrowed from the LLVM system.
LLVM: Originally "Low Level Virtual Machine", now just an identifier for a a specific compiler optimizer and code generator.
[1] http://smallcultfollowing.com/babysteps/blog/2016/04/27/non-...
CFG would be Control Flow Graph, a structure compilers use to track all the possible paths through your code.
http://smallcultfollowing.com/babysteps/blog/2018/06/15/mir-...
=======================
fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
match (&t, ()) {
((t,), ()) => t,
}
}=======================
I thought Obfuscated-C was hard to read. Then C++ with all its templates. Now, I'm thinking Rust has taken the prize.
A function 'transmute_lifetime' that takes a tuple of generic type T with lifetime 'a and returns a T with lifetime 'b...
And then the code itself, beyond the function signature, is trivial destructuring matching.
If you're not familiar with the syntax of a language, of course it won't be easy to read.
perl and bash can rapidly devolve into symbol salad if you don't know them. If you don't know es6, javascript's spread and destructuring assignment syntax can look every bit as strange.
That all being said, the point of this rust code was to be a minimal example. Minimal reproducers are often nonsensical and unidiomatic, which helps put this in further context.
I think the original un-minimized crashing code was this: https://github.com/rust-lang/rust/issues/52213#issuecomment-...
That's clearly more readable, and that's what more typical rust looks like.
It's being presented as a terse explanation of what the bug was and how it was triggered.
I didn't spend an hour or two understanding that code, I spent less than a minute because I already know rust.
I don't understand your point. The code served its purpose well: for the audience (rust users) it demonstrated the bug (lifetime unsafety in match ergonomics change).
If the audience were "people who don't know rust", then of course that code wouldn't be a good demonstration, but that's not who the audience for this post is.
I don’t really know Rust all that well beyond a few toy projects. But if you were familiar with core aspects of the syntax and semantics - lifetimes, generics, tuples and the void type in this case - then it’s not particularly complex. Add to the fact that it’s code you’re not actually going to see in practice and I don’t think it’s so bad!
- Every reference has a different explicit lifetime—one input, one output—which would rarely ever show up in real code.
- There is a tuple with 1 element, which I have never seen in the wild.
- It's matching against `()`, the void type, which is never done because it's useless.
Also, there's nothing obfuscated here, and everything is explicit if you know the syntax.
The alternative is not caring and leaving everything for a GC and/or validating values with runtime checks instead. There are other languages which chose these approaches.
Here's a similarly horrible Java example, from a recent HN post on Java's type system being unsound: https://twitter.com/joshbloch/status/822948565433466881
It's more verbose, because Java, but equally dense relative to the language as a whole.
This is not supposed to be good code.
1. The Rust team is on top of releasing fixes for these bugs. Additionally, the new borrow checker has detected all of the recent borrowck bugs.
2. The code required to produce this does not look like idiomatic Rust. One would hope an organization adopting Rust would choose to review such blocks of code with more scrutiny. That said, this isn't enough of a guarantee -- such code could be buried deep inside a large patch, making it challenging to discover.
I eagerly await the new borrow checker, and other projects aimed at increasing confidence in the Rust compiler's checks.