
Rust 1.42 - steveklabnik
https://blog.rust-lang.org/2020/03/12/Rust-1.42.html
======
computerphage
Better line numbers is huge. Actually huge for people leaning rust. In a
pretty big way it's like the difference between Java where you get a stack
trace (where you mostly care about the live number all the way at the bottom)
and C++ where you don't. When I tutored students in college that was one of
the most salient differences to students who were just starting out. They
would say things like "C++ is hard because you don't even get a stack trace".
Sure, that's a naive view of the differences between the languages, but it's a
_real_ view that beginners see.

~~~
saagarjha
C++ _does_ give you a stack trace; you just need to set some things up before
it does (namely, use a debugger). Rust does the same except it tells you what
you need to do (set an environment variable).

~~~
nicoburns
You get this single-line error without enabling backtraces however. So the
fact that this is now useful for the common case of "which line caused the
error" is a pretty big deal.

~~~
est31
Yeah for backtraces to work you also need to have debug info enabled which
puts a major burden onto linking time. And once they are enabled, you have to
scan the backtrace carefully to filter out the parts that are in the panic
library. The line message is displayed prominently and thus allows for a much
faster development cycle in finding the error.

~~~
umanwizard
Backtraces even without line numbers are still quite useful, as long as they
have function names.

~~~
nicoburns
I mean, they're better than nothing. But they leave you guessing at exactly
where the error is if you do similar operations more than once in your
function. A line number is much better.

~~~
saagarjha
Offsets are occasionally _more_ useful than line numbers, especially if you
have multiple things on one line.

------
mmastrac
> Due to our stability policy, description will never be removed, and so this
> is as far as we can go.

What Rust _should_ do is gradually increase the noisiness of warnings about
using this. Eventually it'll disappear from the crate and end-user ecosystem
(possibly with a small effort to tidy up abandoned crates).

Java's "never remove a deprecated API" was definitely one of their big
mistakes. Even a 10-year deprecation cycle is better than none.

~~~
baby
look at the monster that C++ is now because of this as well.

I think this is why people are turning to Golang, Rust, and Swift. But these
languages could follow the same path.

~~~
dodobirdlord
Rust has Editions, so deprecated features can slowly age out of the ecosystem.
People using, say, the 2015 edition will probably eventually want features
introduced in the 2018 edition, and they will have to remove their usage of
features that were dropped in the 2015->2018 edition bump. And if they never
want any new features, maybe because their library is totally complete? Well,
crates using 2018... can still depend on them, they just can't use those
features.

------
_bxg1
I didn't realize how rich the normal match { } patterns can get until I saw
these matches!() examples and was trying to figure out what that pattern
syntax was. I've only ever used the basic enum matching/enum value unwrapping
patterns.

~~~
ojosilva
I'm actually planning on declaring war against pattern matching in EcmaScript,
which is stage 1 right now. I can't believe how popular it can be. To me it
looks like a huge antipattern, a complete different way of writing
conditionals from ifs, built in a way that can lead to completely unreadable
code (for complex matching) and very inconsistent behaviour. And just like
switch-case, become something to avoid outside of byte-sized or plain literal
matching.

Not to mention how much each language differs in how the syntax and behavior
is implemented, making it harder for beginners everywhere to ramp up.

Like the smart-match clusterf __* in Perl, pattern matching is just too darn
clever for its own good.

~~~
chrismorgan
For reference, the proposal: [https://github.com/tc39/proposal-pattern-
matching](https://github.com/tc39/proposal-pattern-matching)

Rust’s pattern matching is deliberately fairly limited in what it can do; it’s
all about destructuring types, not running arbitrary code like Perl’s smart-
match (which even so I would not describe as a disaster). It’s conceptually
very clean—arguably simpler than what ECMAScript _already_ has. `let PATTERN =
EXPRESSION;`, `if let PATTERN { … }`, `match EXPRESSION { PATTERN => … }`, `fn
NAME(PATTERN: TYPE)`, _& c._, everything that can introduce a new binding is a
pattern. Some of the uses of patterns are refutable (match branches, `if let`,
`while let`), and some irrefutable (`let`, `for`, function arguments, closure
arguments).

ECMAScript already has destructuring in various places, corresponding to
irrefutable pattern matching; what the proposal’s `case` expression introduces
is essentially just refutable destructuring, plus a smidgeon of expression-
orientation (`->` corresponding to `=>`, that `case` is an expression that
yields a value, rather than a statement) in a place that sorely needs it. This
is a very logical extension of the language.

If TC39 or equivalent were to design a new language to actively replace
JavaScript (meaning something that all extant JavaScript code should be able
to migrate to easily, preferably automatedly), there is no doubt in my mind
that the language would be more expression-oriented than ECMAScript is, that
destructuring syntax would be brought in line with what we call pattern
matching so that there was one coherent concept underneath (probably called
pattern matching), and that `switch` would use it, becoming equivalent to this
proposal.

The way people use JavaScript these days, these things are useful.

(I write all this as an expert and heavy user of both Rust and JavaScript; I
use JavaScript more, most of the time, but prefer Rust. Rust was the first
language I learned with each of algebraic data types, pattern matching and
expression orientation.)

~~~
ojosilva
Perl smart match IS a disaster, we fought with lot of bad smart-match behavior
for a while in the late 2000s, then educated programmers over and over on how
to do smart matching just to see how the Perl community and the language took
action to deprecate most of it. We then dove into 100k+ lines of code to
remove all smart matching entirely, including all `when` clauses. To me that's
the definition of a disaster for a programming language.

Pattern matching is a can of worms in the way people think these should work
and the way compilers implement them. It holds unexpected behavior in so many
ways and it's not at all about language abuse, but about giving a tool that is
prone to misunderstanding and flimsy enough for programmers to shoot
themselves and others in the foot. And it's not the same pattern matching in
Haskell (which is fine) that it is in ES (where it's not).

Fortunately the TC39 proposal looks somewhat stalled and you can already see
in the Babel plugin and proposal discussion questions on why or how this does
or does not do behavior X, Y or Z or how syntax should be implemented. This is
a bad sign on how pattern matching can be confusing, but then maybe this may
be a sign it never gets past stage 1. Here are a few soundbites from the
proposal:

    
    
         when true        -> ... // ok, if x === true
         when undefined   -> ... // not ok, this creates a local var called "undefined"
         when Infinity    -> ... // ok, if x === Infinity
         when -Infinity   -> ... // Syntax error!
         when /.*/        -> ... // not ok, unsupported, surprise surprise.
         when {x} = {x:1} -> ... // great, we check if x exists and set a value on it if it doesn't?!?!
         when {status = 200} if (status === 200) -> ...   // left as an exercise for the reader
    

Now I just love this one, as it just subsumes many of my concerns:

    
    
         const y = 2;
         case (1) {  // matching on a value itself, lovely
             when y if (y === 2) -> 'does not match', // y is (re)created locally for the if!
             when x if (x === 1) -> 'x is 1'  // you're a psycho if you write code like this
         }
    

Now, I must confess I do like matching on types as they can substitute method
dispatching elegantly and should be simple to understand and implement, but
this is more suited for typed languages, not ES. Same for basic parameter
existence as a destructuring dispatch for arrays and objects. But never on
value, ranges (`[1..=x]` - yuck, Rust!) or anything more complex.

~~~
lizmat
Perl smart match was a disaster because it was symmetric.

Historically, smart match in Perl was "stolen" from the ideas of Perl 6. Perl
6 (now named Raku [https://raku.org](https://raku.org) using the #rakulang tag
on social media) learned from the problems found in Perl, and changed its
smart-matching model from symmetric to asymmetric. Basically, `a ~~ b` is
syntactic sugar for `b.ACCEPTS(a)`. By transferring the responsibility for
what smart matching means to an object of a given class, made it possible to
make much more sane default behaviour, as well as allowing authors to define
their own smart-matching behaviour for custom classes.

------
nathcd
> Due to our stability policy, `description` will never be removed ..

Can this sort of removal not happen as part of an edition? Is it because it's
part of std, rather than core, or something like that? It's a bummer, but the
commitment to stability is appreciated!

matches!, subslice patterns, and .unwrap messages look excellent!

~~~
burntsushi
Editions do not permit arbitrary breaking changes. Notably, the same version
of std must work across all extant editions of std. For example, many people
use the `regex` crate in their Rust 2018 projects despite the fact that
`regex` still uses Rust 2015. If Rust 2018 made a breaking change to std, this
wouldn't be possible in general.

~~~
ithrow
If you know Python and putting libraries aside, talking about raw code, do you
think you can be as productive in Rust as in Python?

~~~
burntsushi
Bad person to ask. I am personally terribly unproductive with Python. I might
be able to hammer out a 50 line script that does some data shuffling faster
than I could write an equivalent Rust program, but anything more than that---
especially when it comes to maintaining it---and Rust easily wins for me in
terms of productivity.

Nothing against Python specifically. I've just tried and failed over and over
again to build large programs in untyped languages. I like my types and think
they are invaluable, especially when it comes to refactoring.

------
est31
Very grateful about the better unwrap location reporting. This has annoyed me
last this week. Didn't know that it was being stabilized (only checked the
track_caller tracking issue which is still open) so I'm very happy now :).

~~~
atoav
A good idea usually is to use unwrap only if you know there is no logical way
for it to blow up and to use .expect("My Message explaining why this blew up")
everywhere else.

------
howenterprisey
The matches! macro looks awesome! Looking forward to using it a lot
(especially to check variants).

~~~
bonzini
if let is usually good enough to check variants. Matches! should be useful to
implement methods like is_some() or is_ok().

Also for assertions, but then again there is a crate that provides
assert_matches!, which is more useful for this than matches! itself.

------
FullyFunctional
I'm very excited about this change:

"Added tier 2 support for riscv64gc-unknown-linux-gnu"

which hopefully means Rust will make its way to the RISC-V versions of
Fedora/Debian (wasn't there when I last checked). Furthermore, there are a lot
of things depending on this that might also arrive now.

------
unexaminedlife
I'm curious to know if anyone who regularly uses Rust could speak to a theory
I have on the language. I'm by no means an expert but I've dabbled enough to
know Rust is I think going to be far more important to the world than it is
already.

I'm planning to dive back into it, but this time I'm going to attempt to do
all of my programming in such a way so as to not require 'lifetime' semantics.

Not 100% sure how easy it would be to do so, but the theory is that maybe
writing code that needs lifetime semantics is an anti-pattern? Rust has just
given us the tooling so we can confirm that the "style" we are already writing
our code in adhere's to Rust's guarantees...

~~~
_-___________-_
Using Rust daily since ~2016 here. I'm assuming that by "writing code that
needs lifetime semantics" you mean "writing code that needs explicit
lifetimes", since all code in Rust has some lifetime semantics going on. If
I've misunderstood then please clarify.

Lifetimes are one tool in the toolbox, and using them is absolutely not an
anti-pattern. Being able to define and use explicit lifetimes is a really
powerful tool that allows you to safely share data in ways that can be tricky
and error-prone to do in some other languages.

For example, a deserialiser could be passed a reference to a buffer (&'a [u8])
and return a struct where some fields are &'a str which are just bytes
borrowed from that buffer. You could then perhaps trim() them - returning
another &'a str which just points to a smaller slice of that same buffer, and
pass them off to other parts of your program - potentially even pass them to
an async function that you await on, which could run in another thread - and
the explicit lifetimes mean your code will fail to compile if the buffer can
be dropped before the string is.

~~~
unexaminedlife
You're right. I worded that ambiguously. Thanks for the feedback (all
comments).

------
edflsafoiewq
Completely pointless tangent: the article uses `unwrap`ping as the -ing form
of `unwrap`. That looks weird to me. It occurs to me that `X`->`X`ing is a
better rule because of things like `leave`ing (not `leav`ing).

~~~
saghm
My guess is that they used that spelling because "unwrapping" is a real word,
and that's how it's spelled.

Tangentially, the word itself is spelled that way to preserve the vowel sound
of "unwrap"; without the second "p", the "a" sound would turn from a short "a"
to a long "a". A good heuristic for this sort of thing is that a syllable that
ends with a consonant will make a short vowel sound, whereas one that ends
with a vowel will have that vowel sound be long.

~~~
asplake
Often, avoiding ‘ing’ reads better anyway. “More useful messages when unwrap()
panics” would have been fine.

~~~
_-___________-_
Right: in my mind, by putting the () there, they're basically making it
shorthand for "More useful messages when the `unwrap` function panics"

------
dpcx
As someone who's getting in to Rust, does anyone have suggestions on blogs or
people on Twitter to follow that would be good for continuing to push the
envelope of my knowledge of Rust? I've got a couple, but I'd love more.

~~~
steveklabnik
[https://readrust.net/](https://readrust.net/) collects blog posts.

------
unlinked_dll
Can we use #[track_caller] on procedural macros?

~~~
SpaceManiac
Your macro can expand to line!() or file!() calls as needed, which will
reflect the call site.

------
throwlaplace
can I poll the audience of seasoned rust devs: how do you deal with the borrow
checker and lifetimes? do you simulate both in your head before writing your
code or do you just let the compiler yell at you and then silence it by fixing
bugs?

I ask because I've written some trivial rust code (a little TUI client for a
database) and while it was mostly fine, I didn't feel myself absorbing the
rules by osmosis (I had to wait for the compiler to yell at me and then
wrestle).

~~~
burntsushi
I'm not sure if it's a simulation so much as the rules just become normal
constraints you use when writing code. It is super rare for me these days to
write any line of Rust code that makes the borrow checker complain in a way
that surprises me. It took me a while to get to that point. I don't remember
how long though.

~~~
throwlaplace
any suggestions for exercises that drill this in? I'm going through the linked
list book but it's still not completely transparent for me.

~~~
burntsushi
Not really. Pick a project and start building. At least, that's my learning
style. Reading things like the linked list book is great, but eventually, you
probably have to put it to practice in order to really learn it (like most
things).

~~~
steveklabnik
I would like to second everything you've said here, and add a few things:

1\. If you are feeling stuck, please reach out on users.rust-lang.org or
discord.gg/rust-lang. We're here to help!

2\. If you're feeling _really_ stuck, maybe take a break and come back to Rust
in a few months. A number of people have given up on Rust in frustration, came
back after a significant amount of time, and then said "why did I think that
was so hard before?"

3\. Don't worry about a few calls to clone when you're getting started; it's
better to have a working program that does some extra copying than it is to
not have a program at all.

4\. Especially when starting out, you almost always want structs to own their
data. Don't use references as struct members unless you're absolutely sure
that's what you want. Advice #3 helps with this.

5\. I think one of the biggest mindsets that can set you up for failure with
Rust is "The compiler says no, how do I get it to do what I want anyway"
instead of "The compiler says no, what is it telling me, and how can I work
with it instead?" Especially if you come from an OO heavy background, you may
need to change the patterns that you reach for initially. Yes, you can write
any style in any language, but Rust pushes you towards its idioms much more
strongly than other languages do.

~~~
CameronNemo
>discord.gg/rust-lang

Why not plug the rust channel on Matrix?

~~~
steveklabnik
I only plug:

* Things that are run by the project

* Things that I use

I have no idea if the Matrix channel is any good or not, so I cannot recommend
it.

~~~
CameronNemo
Fair enough. Hope you do get a chance to check out the Matrix channel!
(#rust:mozilla.org)

------
saagarjha
matches! looks quite useful, I’ve been looking for a replacement to Swift’s ~=
operator and this should do nicely.

~~~
kzrdude
Matches is great - already existed in a nice microcrate. But assert_matches is
now missing! :)

~~~
Nemo157
The long term plan is to make assert! smart enough to recognise the assertion
and provide good error messages based on its shape, maybe eventually
deprecating assert_eq! etc.

------
nickysielicki
I don’t really like that procedural macros are a thing. Rust isn’t quite at
the Go level of forcing style, but it’s closer to it than it is to C++ or C,
and I think the ability to implement DSLs with procedural macros makes it
harder to look at code and understand what it does.

I guess I’d rather have the feature for when it’s truly necessary than exclude
it from the language entirely, and just hope that people use it with
forethought.

~~~
steveklabnik
No procedural macros means that libraries like serde and diesel cannot exist.
Or at least, they would have to exist in a much worse form. This was true
before Rust 1.15, when the first parts of procedural macros were stabilized. I
remember the release specifically because things got _so_ much better after
this was possible.

~~~
lmm
A macro is a bug report against the language; Rust is crying out for a way to
treat structs generically (i.e. what frunk does with LabelledGeneric), but
that only needs to be implemented once. There's no reason serde and diesel
couldn't be built on top of frunk (that's how many things work in the Scala
world: specific libraries like circe or pureconfig don't write custom macros,
they just pull in a dependency on Shapeless) - and then once there was a clear
consensus on the right way to do it, LabelledGeneric or equivalent could be
made a standard part of the language and no macros would be needed at all.

Maybe macros make sense as a pragmatic way to allow prototyping of future
language features. But language designers can, and IMO should, work towards
making them unnecessary in the production-code ecosystem, because the
disadvantages for readability and tooling are very real. And that's only going
to get worse as more advanced tooling (IDEs, profilers etc.) becomes available
and people rely more heavily on those tools to be deeply integrated with the
language, because those tools are never going to be able to understand ad-hoc
macros.

~~~
Arnavion
>Rust is crying out for a way to treat structs generically (i.e. what frunk
does with LabelledGeneric), but that only needs to be implemented once.
There's no reason serde and diesel couldn't be built on top of frunk [...]

What frunk does at runtime (well, its proc macros are still compile-time, but
the code that makes use of the metadata is runtime), syn does at compile-time.
I can understand that it's nice to have a simpler model where everything
happens at runtime, but the things that proc macros can do include things that
matter to the typesystem. For example, some of the macros I've written
generate new types based on the input tts. You can't do that at runtime.

>And that's only going to get worse as more advanced tooling (IDEs, profilers
etc.) becomes available and people rely more heavily on those tools to be
deeply integrated with the language, because those tools are never going to be
able to understand ad-hoc macros.

The mainstream tools (rls and rust-analyzer) get their information from the
compiler, and thus have access to the expansion of the macro rather than just
the macro invocation.

~~~
lmm
> What frunk does at runtime (well, its proc macros are still compile-time,
> but the code that makes use of the metadata is runtime), syn does at
> compile-time. I can understand that it's nice to have a simpler model where
> everything happens at runtime, but the things that proc macros can do
> include things that matter to the typesystem. For example, some of the
> macros I've written generate new types based on the input tts. You can't do
> that at runtime.

Again, though, that's something you want to be able to do in a standard first-
class way in the language, rather than needing a custom macro. Depending on
the use case you either want higher-kinded types (for transformations like
producing a "patch" version for a given record, where all members are
(recursively) optional) or dependent types (for transformations where you
really just want to do a bunch of specific type->type cases). It's really not
rocket science - Scala has been doing this stuff for years already.

> The mainstream tools (rls and rust-analyzer) get their information from the
> compiler, and thus have access to the expansion of the macro rather than
> just the macro invocation.

Sure, and that helps, but the tool can't possibly know what the full relation
between input and output is (because by definition the macro is an arbitrary
function), so it's never going to be as reliable as first-class code.

~~~
Arnavion
>but the tool can't possibly know what the full relation between input and
output is (because by definition the macro is an arbitrary function)

The macro can associate output tokens with the spans of input tokens, either
by reusing the input tokens (`#[derive(Foo)] struct S;` -> `impl Foo for S`,
the `S` is reused), or using the macro API which allows specifying an
arbitrary span for new tokens. This is required both for ident hygiene and for
associating errors with the source code, thus it is a core part of the macro
API. And since the compiler has this information, tools have it too.

That means if you have

    
    
        #[foo]
        struct S; // (1)
    
        let s = S; // (2)
    

and use an IDE to refactor-rename `S` to `S2` at the line marked (2), the IDE
has enough information to know that it must also rename the `S` at the line
marked (1), even though it's a macro input. This applies equally well to
function-style proc macros `foo! { S }`

