Fn match syntax sugar


#1

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. Are match-only functions common enough in Rust to justify this added syntax sugar?

(I’d like a “language design” category in this group too, because lot of my posts here are about this topic).


#2

Those are better served by posting on https://internals.rust-lang.org/. I’d suggesting moving this post over there, tbh.


#3

Is this something that could be accomplished via a macro rather than core language?


#4

FWIW, this is relatively straightforward to accomplish with a macro as long as you’re okay with losing some flexibility (e.g. match statements always must be surrounded by {}) Someone with better macro-foo than I may be able to improve this to the point where there isn’t such a drawback.

macro_rules! fn_match {
    ($desc:ident($($param_name:ident: $param_type:ty),*) -> $response:ty {
        $(( $( $x:expr ),* ) => $b:block),*,
        _ => $default:block,
    }) => (
        fn $desc($($param_name: $param_type),*) -> $response {
            match ($($param_name),*) {
                 $( ( $($x),* ) => $b),*
                 _ => $default,
            }
        }
    );
}

fn_match!{
  foo(a: u32, b: u32, c: u32) -> bool {
    (0, 0, 1) => { true },
    (0, 1, 0) => { true },
    (1, 0, 0) => { true },
    _ => { false },
  }
}

http://play.integer32.com/?gist=9bbd5ce096cd53626f2dc9debd4f685c&version=stable


#5

Even if you have this macro in the Prelude, this syntax is worse than the default syntax it tries to improve on :frowning:


#6

It feels a bit … specialized to me. And the implementation leaks out into the function signature.


#7

Well, you asked :smiley: the macro above falls into “because we can, not because we would/should”

A cleaner approach is to use a custom attribute to rewrite the function signature.

But, having said this, I don’t actually think this is a common enough use case to warrant trimming 2 lines of code.

If we went all the way to a compiler plugin, we could get something similar to Haskell style pattern matching:

#[match_fn] {
foo(0, 0, 1) => true
foo(_, _, _) => false
}

#8

I’m confused. If…

fn_match!{
  foo(a: u32, b: u32, c: u32) -> bool {
    // ...
  }
}

…has much worse syntax than the default it tries to improve upon, then what does…

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

…do that isn’t “much worse”?

Between these two examples, we have a difference of two characters {} and maybe some indentation, The _ + ! may not be perfect, but there’re no keystrokes lost from the two spaces that replaced them.

The macro removes the duplicated argument names, which appears to be the goal. What am I missing here?