Spread array shortcut for match branch and array destruction

I already read some RFC and issues on GitHub related to this, but I thinking about shorcut, not about global changes in syntax.

I would glad to see and use next patterns for destruction and matching:

// Destruct array
let [first, ..rest, last] = [1, 2 ,3 ,4];

// Matching
match [1, 2, 3, 4] => {
    [first, ..rest] => {}
}

Instead of unusual for me and, I think, many other devs:

// Destruct array
let [first, rest @ .., last] = [1, 2 ,3 ,4];

// Matching
match [1, 2, 3, 4] => {
    [first, rest @ ..] => {}
}

Related: https://github.com/rust-lang/rust/issues/37854

In the related issue the discuss going around numeric ranges, which is not much important for me. So I decided to discuss about alternatives array spread syntax here

This already works on nightly (using an unstable feature)

    match [1, 2] {
        [first, ..rest] => {}
        _ => println!("How did we get here?")
    }

it’s just… it has an entirely different meaning!

#![feature(exclusive_range_pattern)]
const rest: i32 = 42;

fn main() {
    // Matching
    match [1, 2] {
        [first, ..rest] => {}
        _ => println!("How did we get here?")
    }
}
9 Likes

That sounds like a job for macros! You could totally create a macro that makes this work:

match [1, 2, 3, 4] => {
    rest![first, ..rest] => {}
}

...but I'm not sure if it's worth the effort.

1 Like

That said, the pattern rest has different behavior based on whether rest is an in-scope const, so I'm not convinced that ..rest meaning something if rest is a const is necessarily a deal breaker.

1 Like

I mean, identifiers in patterns already do something different if there is a const of the same name in scope.

If we want to have a splat operator we should make it possible to use it in both patterns and expressions. That means it would have to use different syntax to ranges. The three-dot ellipsis is free now, so we could use that:

let array: [u32; 2] = [2, 3];
const ARRAY: [u32; 2] = [2, 3];
let slice: &[u32] = &[4, 5, 6];

assert_eq!([1, 2, 3], [1, ...array]);
assert_eq!([1, 2, 3], [1, ...[2, 3]]);
assert_matches!([1, 2, 3], [1, ...rest] if rest == array);
assert_matches!([1, 2, 3], [1, ...[2, 3]]);
assert_matches!([1, 2, 3], [1, ...ARRAY]);
assert_eq!(
    vec![1, ...[2, 3], ...*slice],
    &[1, 2, 3, 4, 5, 6],
);
1 Like

I would like to leave this syntax open for a variable in that position as well. I'd like to be able to use pattern syntax to achieve various splitting stuff

fn split_at_mut<T>(slice: &mut [T], position: usize) -> Option<(&mut [T], &mut [T])> {
    match slice {
        [a @ ..position, b @ ..] => Some((a, b)),
        _ => None,
    }
}

IMO that's a flaw of the current language and should not be taken as an example for new features. It's weird to explain and can catch people off guard. I wish we got rid of this inconsistency and just use const { IDENT } instead.

4 Likes