Teach alternatives to `match` with more intent

We get a lot of new feature suggestions that would help people avoid writing match blocks, and a lot of the time the pushback from experienced Rust coders boils down to "this isn't really better than a match block". I think this might be pointing at a documentation / teaching materials problem.

When I was first learning Rust a few years ago, the Book did not have an intentional opinion on the topic of when you should use match, but it did spend a whole lot more time on combinators than on match, probably because there are a lot of different combinators and they all need to be demonstrated. This left me with the impression that you should always use combinators if you can, especially with Result and Option, leaving match for state machines and complex N-way enums. Over the subsequent couple years of actually writing code in the language, I've come to realize that often match is more readable than combinators, especially in imperative-y code; for instance

        // Some number of connections are now pending.  Poll them until
        // all are resolved.
        while !in_flight.is_empty() {
            let status = poll.poll(&mut events, Some(timeout));
            let after = Instant::now();
            let timed_out = match status {
                Ok(_) => events.is_empty(),
                Err(e) => {
                    if e.kind() != io::ErrorKind::TimedOut {
                        return Err(e.into());
                    }
                    true
                }
            };

poll is a mio::Poll instance. The match expression initializing timed_out could be written with combinators, but you'd have to jump through extra hoops to achieve the early return when the error isn't an io::ErrorKind::TimedOut, and the control flow graph would be obscured. And it could also be written with some of the proposals from the "Elvis operator" thread but I don't think it would actually be clearer that way, you'd be trading arrows for ?: and/or else is all.

(This fragment is from non-public code used at my day job, so I can't show you the whole program, sorry.)

So I want to suggest that language tutorials should spend more time than they currently do, illustrating the style and clarity issues that go into making a choice among match, if let, combinators, ?, etc. and expressing intentional opinions about those style issues.

(Intentional here means "the authors thought about it and knew that they were expressing this opinion with their words," as opposed to the unintentional opinion that the Book had a few years ago. I know the Book has been revised substantially since I last read it; I am not making any claims about what it says now.)

3 Likes

Since we're taking about style, here's comparison with a few other ways of writing that snippet.

        // Some number of connections are now pending.  Poll them until
        // all are resolved.
        while !in_flight.is_empty() {
            let status = poll.poll(&mut events, Some(timeout));
            let after = Instant::now();
            let timed_out = match status {
                Ok(_) => events.is_empty(),
                Err(e) if e.kind() == io::ErrorKind::TimedOut => true,
                Err(e) => return Err(e.into()),
            };
        // Some number of connections are now pending.  Poll them until
        // all are resolved.
        while !in_flight.is_empty() {
            let status = poll.poll(&mut events, Some(timeout));
            let after = Instant::now();
            let timed_out = status
                .map(|_| events.is_empty())
                .or_else(|e| if e.kind() == io::ErrorKind::TimedOut {
                    Ok(true)
                } else {
                    Err(e)
                })?;

Elvis doesn't apply to this code anywhere I can spot. (Because the positive case requires a transform as well.)

I believe the match block with if guard is the most clear here. I'm not certain how best to teach it, but some discussion along the lines of "don't be too clever" could go a long way.

5 Likes

I assume you mean this thread?

(Please link to threads when referencing them, or every reader will have to re-do the same search.)

Yes, that's the thread I meant. I've edited a link into the starting post now. Sorry about that.