There’s a whole section on the page with the header “Concern over numerous patches to the match ergonomics feature”.
So if your question is literally, “Does anybody else...” then the answer is yes:
“Users have expressed concern with the frequency of patch releases to fix bugs in the match ergonomics verification by the current borrow checker on a variety of Rust’s forums. ”
If the question is more about being concerned yourself, do the reasons listed in the link address your concerns?
If you're familiar with rust syntax, that function is readable:
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.
and that is where the problem lies. All code is readable if you spend an hour or two. The point is that it is not lucid. You have to strain to understand such code. The issue is people thinking this code is acceptable.
It's not being presented as acceptable code for maintenance or other long-term usage.
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.
Expressing complex concepts almost always requires complex expressions of intent!
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!
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.
To reiterate what the OP suggests at the bottom, the new (in-progress) borrow checker (the one designed from the ground-up to support non-lexical lifetimes (NLL)) suffers from none of the match bugs that have inspired the recent slew of patch releases.
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. :)
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.
I was under the impression that the upcoming, shiny, great borrow checker is a tad slow, pathologically so in some cases. https://github.com/rust-lang-nursery/polonius
But I do love the way they're tackling the issue know, as this is a much more generic solution.
The bug being fixed was that you could use `match` to return a borrowed object with a new lifetime? Am I interpreting this correctly? You should never be able to do this, right?
Yes, you should never be able to do this, thus it is a bug. The bug, at least as it appears in the example presented in the blog post, allows you to obtain a reference to a member of a tuple that will outlive the tuple itself.
Despite these bugs, I still feel confident in Rust's ability to provide strong memory-safety guarantees. Two reasons:
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.
33 comments
[ 3.6 ms ] story [ 93.6 ms ] threadSo if your question is literally, “Does anybody else...” then the answer is yes:
“Users have expressed concern with the frequency of patch releases to fix bugs in the match ergonomics verification by the current borrow checker on a variety of Rust’s forums. ”
If the question is more about being concerned yourself, do the reasons listed in the link address your concerns?
=======================
fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b 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.
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-...
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.