A fn match syntax sugar


#1

(This is a low-importance idea, that’s why I originally wrote it in the users group) A way to shorten code like this:

fn foo(a: u32, b: u32, c: u32, d: u32) -> u32 {
    match (a, b, c, d) {
        //...
    }
}

Is to support a “fn match” shortcut:

fn match foo(a: u32, b: u32, c: u32, d: u32) -> u32 {
    //...
}

This makes the code a little shorter, makes the code more DRY (because there’s no need to re-state the argument names), and encourages more usage of match. Haskell syntax allows something similar. Are match-only functions common enough in Rust to justify adding such syntax sugar?


#2

#3

I love this because of how much my rust style has become match-centric. This would probably simplify 50% of my fns.


#4

I initially thought this would really help with one module I’d written, but taking a quick look over it I realized I’ve used a pattern that doesn’t quite work with this: matching on a dereferenced &mut self parameter. Currently that looks like (assuming Bar and Baz are enum variants used in the current module):

fn foo(&mut self) {
    match *self {
        Bar(ref mut bar) => {
            println!("bar {}", bar);
        }
        Baz(ref mut baz) => {
            println!("baz {}", baz);
        }
    }
}

applying this sugar to it gives

fn match foo(&mut self) {
    &mut Bar(ref mut bar) =>  {
        println!("bar {}", bar);
    }
    &mut Baz(ref mut baz) =>  {
        println!("baz {}", baz);
    }
}

which, while it drops a level of indentation, forces adding the &mut back onto the start of each pattern; that makes scanning the patterns seem much harder to me. It would be amazing if there were some way to enhance this sugar to make this pattern work nicely as well.


Also, as mentioned in the linked issue, something similar for closures would be wonderful. I have a lot of cases where I .map a vector of enums so being able to skip the named closure argument there would really simplify the code.


#5

FWIW, I plan to propose (or would support a proposal…) to “autoderef” on match, at least at the outermost level (though I’d prefer to do it at all levels). In that case, match self and match *self would be equivalent.


#6

That would be really useful, especially if combined with something like this feature :slight_smile:

I presume doing it at all levels would include doing it inside tuples (needed for the multi-argument version of this sugar), I also have a lot of methods implementing binary operators so they’re all like

fn foo(&mut self, other: &Self) {
    match (self, other) {
        (&mut Bar(ref mut bar1), &Bar(ref bar2)) => {
            println!("bars {} {}", bar1, bar2);
        }
        (&mut Baz(ref mut baz1), &Baz(ref baz2)) => {
            println!("bazs {} {}", baz1, baz2);
        }
        (_, _) => unimplemented!(),
    }
}

fn match sugar + autoderef of all arguments would drastically simplify this

fn match foo(&mut self, other: &Self) {
    (Bar(ref mut bar1), Bar(ref bar2)) => {
        println!("bars {} {}", bar1, bar2);
    }
    (Baz(ref mut baz1), Baz(ref baz2)) => {
        println!("bazs {} {}", baz1, baz2);
    }
    (_, _) => unimplemented!(),
}

#7

Yeah, this is basically exactly why I would prefer to do it at all levels.