How to destructure a `&str` and pattern match

Hey, taking a shot on aoc2022 day5
I need to parse a part of String and pattern match + destructure three chars in order to build a structure.
Now this compiles but looks wasteful:

fn parse_crate(input: &str) -> Result<Crate, String> {
    let chars: Vec<char> = input.chars().take(3).collect();
    match chars[..] {
        [_, c, _] => Ok(Crate(c)),
        _ => Err("could not parse crate".into()),
    }
}

So, intuitively I have tried to get rid of the vec:

fn parse_crate(input: &str) -> Result<Crate, String> {
    // let chars: Vec<char> = input.chars().take(3).collect();
    // match chars[..] {
    match input[..=3] {
        [_, c, _] => Ok(Crate(c)),
        _ => Err("could not parse crate".into()),
    }
}

which yells at me:

44 |         [_, c, _] => Ok(Crate(c)),
   |         ^^^^^^^^^ pattern cannot match with input type `str`

what is going on here?
isn't the slicing operator [..] returns a str anyway in the first example?
what is the idiomatic way of doing this?
Thanks!

You cannot index a str or a String. It is that way because there is no sensible way to index a UTF-8 string.
For the AOC problem, it is adequate to convert the String to Vec<char>. If you want to avoid allocations, you can do something like:

let mut it = input.chars().take(3);
match (it.next(), it.next(), it.next()) {
    (_, Some(c), _) => Ok(Crate(c)),
    _ => Err("could not parse crate".into()),
}
5 Likes

You actually can index them with ranges. Our comrade is probably just misunderstanding the &str type.

&str is not an array of char, but just two usize’s, first of which is pointing to a buffer of bytes ([u8]) and second is just a length. Note that these bytes are not chars, and you probably need to use Chars iterator.

1 Like

so this gous back to @RedDocMD solution, right?

Yes

Note that you can also use Itertools::next_tuple for this purpose.

1 Like

For AoC puzzles it's typically overkill to use strings because everything is ASCII; Vec<u8> / &[u8] / b"byte string literals" should suffice, or alternatively you can use str::as_bytes() to get a byte slice from a string.

5 Likes