Rust pattern matching over a vector
Asked Answered
M

3

38

The tutorial shows some very basic examples of pattern matching, such as matching over an integer to emulate a c-style switch statement. The tutorial also shows how to do basic destructuring over a tuple type, and destructuring structures.

It seems like it should be possible to pattern match over a vector but I cannot figure out the right syntax for it and I haven't found any examples of it.

For example, in Haskell you can easily destructure a list:

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr func initValue []     = initValue
foldr func initValue (x:xs) = func initValue $ foldr initValue func xs

So, looking at a rough translation, it would be nice to be able to do:

fn foldr<A, B>(func: fn(A, B) -> B,
               initValue: B,
               vals: [A]) -> B {
  alt vals {
    [] { ret initValue; }
    _  {
      let h = vec::head(vals),
          t = vec::tail(vals);
      ret foldr(func, func(initValue, h), t);
    }
  }
}

Note: I know you could use an if statement here, I am just using this as an example of pattern matching over a vector.

This currently returns:

patterns.rs:10:4: 10:5 error: constant contains unimplemented expression type
patterns.rs:10     [] { ret initValue; }
                ^
error: aborting due to previous errors

There is an example in the tutorial for destructuring structures (defined with { .. }) and tuples (defined with ( .. )), so it seems like there should be built-in support for vectors as well considering they also contain a special syntax (defined with [ .. ]).

Feel free to correct me if I am using vectors in the wrong way as well.

Macfarlane answered 14/2, 2012 at 19:16 Comment(2)
Somewhat tangential, but for tail calls you should use "be" instead of "ret".Sidonie
@ian-b Interesting, the tutorial and language reference seem out of date then, they mention be as a keyword but they don't make an references to it at the momentMacfarlane
B
8

I wish I could give more general advice on how to best use pattern matching on vectors, but here's how you can use them to test for empty vectors (at least I think that's what that Haskell code is doing...):

use std;
import std::io::println;

fn main() {
    let empty: [int] = [];
    println(vec_alt(empty));
    println(vec_alt([1,2,3]));
}

fn vec_alt<A>(vals: [A]) -> str {
    alt vals {
        x if x == [] { "empty" }
        _ { "otherwise" }
    }
}

Note that trying to simply pass [] as an argument fails because the compiler can't infer a type for the vector. It seems to be possible to pass [()] (a vector with a nil inside) without first declaring it, but the alt statement seems incapable of testing to see if the head expression matches [()] (it simply falls through to the default).

All in all, vectors seem a little rough at the moment. If there's some specific use you have in mind that Rust doesn't seem to support, the developers are quite open to suggestions and critcism: https://mail.mozilla.org/listinfo/rust-dev

Also see the reference manual for a more formal definition, and a few more examples to help clarify things: http://doc.rust-lang.org/doc/rust.html#alternative-expressions

Bichromate answered 14/2, 2012 at 21:27 Comment(1)
I think ultimately this isn't supported at the moment. I filed a RFC to extend destructuring to support vectors. I have another example on that issue that I think helps show the issue. It would be a nice feature to have anyway. Hopefully in the future, something like this will work.Macfarlane
A
56

You need slice patterns:

fn vec_alt<T>(vals: Vec<T>) -> &'static str {
    match vals[..] {
        [a, b] => "two elements",
        [a, b, c] => "three elements",
        _ => "otherwise",
    }
}
Attributive answered 15/7, 2019 at 11:8 Comment(3)
Clippy doesn't like this: "error: indexing into a vector may panic"Mandragora
Without specifying T: Copy, you need to add a reference: match &vals[..]. Without the &, you're attempting to move the non-copy slice vals[..] into the match. (You don't need references on the patterns inside the match; I assume there's an auto-deref switcharoo happening.)Trusting
How does slice patterns compose? Matching a Option<Vec<Vec<u8>>> for example?Conchoid
B
8

I wish I could give more general advice on how to best use pattern matching on vectors, but here's how you can use them to test for empty vectors (at least I think that's what that Haskell code is doing...):

use std;
import std::io::println;

fn main() {
    let empty: [int] = [];
    println(vec_alt(empty));
    println(vec_alt([1,2,3]));
}

fn vec_alt<A>(vals: [A]) -> str {
    alt vals {
        x if x == [] { "empty" }
        _ { "otherwise" }
    }
}

Note that trying to simply pass [] as an argument fails because the compiler can't infer a type for the vector. It seems to be possible to pass [()] (a vector with a nil inside) without first declaring it, but the alt statement seems incapable of testing to see if the head expression matches [()] (it simply falls through to the default).

All in all, vectors seem a little rough at the moment. If there's some specific use you have in mind that Rust doesn't seem to support, the developers are quite open to suggestions and critcism: https://mail.mozilla.org/listinfo/rust-dev

Also see the reference manual for a more formal definition, and a few more examples to help clarify things: http://doc.rust-lang.org/doc/rust.html#alternative-expressions

Bichromate answered 14/2, 2012 at 21:27 Comment(1)
I think ultimately this isn't supported at the moment. I filed a RFC to extend destructuring to support vectors. I have another example on that issue that I think helps show the issue. It would be a nice feature to have anyway. Hopefully in the future, something like this will work.Macfarlane
A
1

match the vector in complete and guard the match arms:

fn vec_alt<T>(vals: Vec<T>) -> &'static str {
    match vals {
        v if v.len() == 2 => "two elements",
        v if v.len() == 3 => "three elements",
        _ => "otherwise",
    }
}

The slice pattern solution becomes a bit bulky from vector lengths above 10 and for dynamic lengths it doesn't work at all, e.g. for a query like if v.len() > 7.

Playground

Aleris answered 31/1 at 13:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.