Some Rust concepts are harder to grasp, sometimes the comprehension jumps are too large for me. This should be a place to help filling more easily the comprehension gaps while going through The Book, for me and for others that feel the same.
Match
Rust has a keyword, match, that allows you to replace complicated if/else groupings with something more powerful.
Here's an example replacing a if/else
with a match
.
shadow_x
is a binding created for x
, scoped to the match
block, using the "magic" of patterns.
fn main() {
let (x, y) = (5, 5);
if x == y {
println!("x equal to y");
} else {
println!("x not equal to y");
}
match x {
shadow_x if shadow_x == y => println!("x equal to y!"),
_ => println!("x not equal to y!"),
}
println!("x: {}, y: {}", x, y);
}
Do you know about the 2 edition of the book still in progres https://github.com/rust-lang/book? I am sure they would love the feedback!
If this is written about the first book, well, then it's not going to be useful; the second edition is completely re-done from the ground up. Just a thing to keep in mind!
In the second edition, after a quick read, I notice Match
is listed under Enum
. Is Enum
used here to show the full power of Match
or is Match
intended for Enum
s?
Is the second edition an attempt at pinning down what Idiomatic Rust looks like?
OK, maybe not for the second edition, as is, but for a better understanding, I think there are a few gaps in the second edition as well that need addressing.
The second edition does not clarify how and most importantly where to use existing bindings. Upon reading match
I expected this:
let (x, y) = (5, 5);
match x {
y => println!("x equal to y!"),
_ => println!("x not equal to y!"),
}
not this
let (x, y) = (5, 5);
match x {
shadow_x if shadow_x == y => println!("x equal to y!"),
_ => println!("x not equal to y!"),
}
and I'm certain that there are other readers thinking this as well.
Match
, wherever it may be filled under, I think it should first clarify is one of these three on the left hand side :
- literals
- any (_)
- (pattern) bindings, having possible shadowing effects
and that the use of previously existing bindings is reserved for:
- the guard
- for right hand side under the rules of the move semantics
As an observation, having read the Match
in the first and the second edition, there seems to be some uncooked meat in the first edition, whereas the second edition has too much fat and not enough meat.
This is slightly OT, but I think it's worth mentioning. It's impossible to write a single book that will a) cater to everyone's level of prior knowledge, b) progress at the right speed and c) reach the level of knowledge they wish to attain from reading the content.
Every book—barring those intended as a comprehensive reference—will have elements missing. AFAICT The Book isn't intended as a comprehensive reference, but an on-boarding tool to Rust. I think that Carol and Steve are doing a very good job at listening to what people are having difficulty with. They're entitled to editorial discretion.
match
is a particularly difficult concept to write about as an author. You're attempting to relate it back to what you expect readers to already know, while also demonstrating that it's unique.
I'm not sure if we actually disagree. It seems we're in the same boat when it comes to The Book and when it comes to learning Rust: there are essential comprehensive pieces missing, one has to "hunt" elsewhere while in the process of reading The Book.
This is the kind of short code that, in my opinion, is more helpful at the start of the Match
topic.
let x = vec![5];
match x {
shadow_x => println!("pattern binding commited: {:?}", shadow_x),
}
The question I and you seem to ask is where do we get these essential pieces from, if not from The Book? While the community is outstanding, every question I had ask on IRC channel has been kindly answered, the next person will have go through the same process, again and again. Having a place where to look for for these essential pieces seems more logic.
From your "Etymology of Rust Language Terms"
My impression of reading crate documentation is that much of Rust's community assume a fairly high level of understanding of systems programming and computer science. Perhaps that high level of assumed prior knowledge is one of the reasons that the language is often described as hard to learn.
As someone who came to Rust with prior experience largely in very high level dynamic languages, especially Python, some of the terminology has been a little confusing.
Here are some notes that I've put together that may be useful for others.
Perhaps we pull in new book in-tree for now, and provide links for missing sections; for too long now, people keep pointing to it as a more respectable place to go.
The missing match
Match
General form
(literal)
match (expression) { (binding) or (pattern binding) (guard) => statement or { code block }, }
_
How it works
With match we can perform multiple succesive pattern matches in order to:
- compare (expression) to a (literal)
- (bind) or (pattern bind) the (expression), bindings that are scoped to the match block
- exhaustive check of the (expression): _
while the result of the (compare/bind/exhaustive check) can each be (guard)ed, before executing a corresponding (statement or a { code block }).
Catch points
Previously existing bindings can only be used in the (guard) and/or in the (statement or { code block }), using them before the (guard) will result in (binding or pattern binding) and shawdowing the variable names to the match block.
The use of the previously existing bindings follows the rules of the move semantics.
match is an expression, it can be used where expression are normally used, like on the right hand side of a binding.
Examples
Literal, guard, previously existing bindings, exhaustive check
let x = 1;
// try "x = 5", equality
let mut verbosity_level = 1;
// try "message_level = 0", less verbose
match x {
// verbose
5 if verbosity_level == 1 =>
{
println!("Let me start by saying this to you:");
println!("{} and 5 are equal!", x);
println!("But that's only ...");
}
_ if verbosity_level == 1 =>
// optional exhaustive check,
// a guard is possible here:
// there's a unguarded exhaustive check below
{
println!("... and also let me point out the");
println!("exhaustive equality comparison of {} and 5!", x);
},
// less verbose
5 => println!("{} and 5 are equal", x),
_ =>
// last exhaustive check: needed
// can't have a guard here as well:
// error[E0004]: non-exhaustive patterns: `_` not covered
println!("exhaustive equality comparison of {} and 5", x),
}
Binding
let x = vec![1];
match x {
shadow_x => println!("{:?}", shadow_x), // read this as: let shadow_x = x
// shawdowing semnatics apply
// move semantics apply also, see below
// exhaustive check not needed
}
// println!("{:?}", x); // error[E0382]: use of moved value: `x`
Also remember the http://rustbyexample.com/flow_control/match.html thought adding or helping the new docs will be nice.
Just wanted to update this thread that we've got a PR open starting a chapter solely on patterns that will be in the new book. This particular chapter will be more of a reference about all the different things patterns do and all the different places you can use patterns and how they interact with other code. There's a section that addresses shadowing explicitly, which should address the concerns relayed in this thread. Thanks!