More love for Rust pattern matching please


#1

In a recent good blog post Graydon discusses about various possible future improvements for programming languages. A section is about “Richer patterns”. In that section Graydon seems to miss the very refined pattern matching of Mathematica ( http://reference.wolfram.com/language/tutorial/PatternsAndTransformationRules.html ).

I’ve found that in my Rust code the parts where I use pattern matching are often more bug-free than other parts of code. So I’d like more care and love for pattern matching in Rust (in match, if-let and function signatures). About this there are some enhancement requests, RFCs and bug reports, but the progress is very slow.

Features like slice_patterns and their advanced version are experimental still after two years. Code like this compiles (and this is silly in a reliable language like Rust):

fn main() {
    let x: u8 = 5;
    match x {
        0 ... 100 => (),
        100 ... 255 => (), // 100 covered two times
        _ => (), // required
    }
}

The compiler sometimes compiles the Less/Equal/Greater variants not efficiently (compared to if/then).

This is redundant and I’d like a shorter syntax:

struct Foo(u32);
fn bar(Foo(x): Foo) {}

Like:

fn bar(Foo(x): _) {}

There are situations where you want “if let … && …”, there’s even a crate to help this (yes, you can use if let on a tuple and this solves some cases, but not where the second item of the tuple must be what you have extracted in the first part).

Something like the Scala unapply() method could be handy.


#2

I just wish to state for the record, that I consider developing Rust without clippy to be like buying a super duper new car with gazillions of safety features and then ignoring the beeping you get when not putting on your seatbelt:

warning: some ranges overlap
 --> src/main.rs:4:9
  |
4 |         0 ... 100 => (),
  |         ^^^^^^^^^
  |
  = note: #[warn(match_overlapping_arm)] on by default
note: overlaps with this
 --> src/main.rs:5:9
  |
5 |         100 ... 255 => (), // 100 covered two times
  |         ^^^^^^^^^^^
  = help: for further information visit https://github.com/Manishearth/rust-clippy/wiki#match_overlapping_arm

#3

Seatbelts and the chime come with the car though, not a separate add-on :slight_smile: The more appropriate analogy is buying a super duper car with a navigation system that is less than stellar in getting you to some places, and using a 3rd party one instead.

The logical question is then why aren’t these warnings part of rustc itself?


#4

We’re in the process of getting this to be a component that all of your cars will have: https://github.com/rust-lang/rust/pull/43886

Because developing new lints or improving the existing ones is much faster out of tree. Also we don’t want one gigantic monolithic compiler that does everything, but nice separate modular components.


#5

For me spotting duplicated patterns in a match is a job for the compiler errors.

Also Clippy can’t fix the language (the need for the useless and bug-prone gotta-catch-all _=>).


#6

Yup, I think that’s a fine adoption path. It’s just that your post sort of read as, because clippy lints this, it’s sort of ok :slight_smile:. Apologies if I misconstrued it.


#7

You didn’t :wink: You completely read my post right. I think it’s a good idea to move some lints from clippy to rustc, but as long as the lint exists, the world is ok in my opinion.

But yea, we’re also trying to bundle clippy via rustup and on a stable compiler, which will blend the borders between separate tool and a component even further.


#8

I’d like to see the numeric exhaustive match RFC revisited. I can’t find the link currently with GitHub being down, but essentially

use std::u8;

fn match_range(n: u8) {
    match n {
        0...u8::MAX => println!("{}", n),
    }
}

which clearly is an exhaustive match, fails to compile with

error[E0004]: non-exhaustive patterns: `_` not covered
 --> test.rs:4:11
  |
4 |     match n {
  |           ^ pattern `_` not covered

error: aborting due to previous error(s)

It was very surprising to me that the compiler couldn’t catch this.


#9

https://github.com/rust-lang/rfcs/issues/1550


#10

I think about 5%-10% of Clippy lints should be moved into the core compiler (so the bugs get caught in case of code generation too, etc).

Probably the main factor that attracted me to Rust v.1.0 what seeing that it tried hard to do the right thing (like the handling of integer overflows, not implicit type conversions, and so on).


#11

Matching on Vecs inside other things is a problem:

#![feature(slice_patterns)]
#![allow(dead_code)]

fn foo1(a: Vec<i32>) {
    match a.as_slice() {
        &[] => (),
        &[_, ..] => (),
    }
}

fn foo2(a: Option<&[i32]>) {
    match a {
        None => (),
        Some(&[]) => (),
        Some(&[_, ..]) => (),
    }
}

fn foo3(a: Option<Vec<i32>>) {
    match a {
        None => (),
        Some(ref av) if av.is_empty() => (),
        Some(av) => println!("{:?}", av),
    }
}

fn main() {}

Scala unapply() standard methods could help: http://www.scala-lang.org/old/node/112

Allowing code similar to:

fn foo4(a: Option<Vec<i32>>) {
    match a {
        None => (),
        Some(&[]) => (),
        Some(&[_, ..]) => (),
    }
}