Missing from "The Book"

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.

1 Like

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);
}
1 Like

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!

1 Like

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!

1 Like

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 Enums?

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 :

  1. literals
  2. any (_)
  3. (pattern) bindings, having possible shadowing effects

and that the use of previously existing bindings is reserved for:

  1. the guard
  2. 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.

4 Likes

@timClicks

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.

1 Like

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`
2 Likes

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!

3 Likes