Mini idea: slice length patterns

The point of pattern matching is that you don't just want to check its length. You want to check the length and usually several other things. For instance that the first and third and last elements of a length 10 slice are 1; and then maybe there are other match arms.

To me the main justification of this is that pattern matching should feel like a complete language, having odd gaps in the language usually adds friction in ways that don't get bubbled up and reported. Is length matching important? Maybe not, but if somebody feels motivated to add it it certainly makes sense. In the meantime I don't think the current syntax is overly burdensome, except maybe wrt exhaustiveness checking.

4 Likes

Maybe this could overlap with pattern types to allow capturing the length as a variable too

match slice {
    &[..; n is 13..=23] => println!("slice has length {n}"),
    _ => (),
}
1 Like

I don't think this needs pattern types:

match slice {
    &[..; n @ 13..=23] => println!("slice has length {n}"),
    _ => (),
}
2 Likes

I'd say the proposed syntax doesn't scale well to more complex conditions. [..; 20] kinda makes sense, it reminds of the array syntax. Unfortunately, it's also basically useless for something more than a length check. A simple pattern could be applied to each element, but I doubt it would be a common use case.

[..; 20..=30] is pushing it, imho. It also kinda makes sense, but personally I find it looks unwieldy. Maybe it's just a matter of familiarity.

But [.., 3; 20..=39] is definitely something outlandish. It doesn't have any parallel in the syntax of arrays or vec!, and my immediate reaction is that ".., 3 pattern is applied 20-30 times`, which is neither a correct interpretation nor makes any sense.


My core objection is that such patterns are simply not something that I missed in my code. Yes, it allows to simplify conversion of slices into arrays since you don't need a redundant unreachable!() branch, but that's a non-issue in practice since we already have a TryFrom<&[T]> impl for [T; N].

On the other hand, I have often missed a more simple thing: if we pop an element from the front or back of an array, we get an array of size 1 less. That kind of stuff is very common in low-level parsing, and doesn't need any weird syntax for the inner slice length. I.e. basically this:

let arr: [T; N] = ...;
match arr {
    [a, b, c @ .., d] => {
            // Here a: T, b: T, d: T, and c: [T; N - 3]
            // We don't need to consider "what if N < 3" even in const-generic code,
            // since the pattern above already guarantees N >= 3.
        }
    _ => { todo!() }
}
1 Like

The .., 3 part has meaning now, that meaning will not change. A syntax for "repeating 3's" would be nice, and should probably be thought through even if it isn't implemented at first.

As for [.., 3; 20..=39], but I can think of multiple places (mostly command parsing) where I have wanted to match against something like ["foo", stuff @ .. "separator", more_stuff; 4..=5]. Obviously this can be accomplished in other ways (you can express many things in a programming language); it is a hole in what can be expressed via the pattern language.

2 Likes

Without this feature, couldn't one write this?

match (slice, slice.len()) {
    ([1, x, 1, .., 1], 10) => todo!(),
    _ => todo!(),
}

Of course, it does require writing the name of the slice twice.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.