The original title: Daily Rust: Slice Patterns links: the original adventures.michaelfbryan.com/posts/daily… Praying for Rust

A nice little feature introduced in Rust 1.26, Basic Slice Patterns, enables pattern matching on slices of known length. Later in Rust 1.42, this feature was extended to use.. To match “everything else.”

This may seem like a small addition in terms of features, but it allows developers to write more expressive code.

remind

The code in this article can be found in the corresponding playground link. The code can be obtained and modified freely. If you think this article is good, or find any bugs in this article, please let me know in the issue Tracker of this blog.

Dealing with diversity

One of the simplest applications of slice pattern is to provide user-friendly information by matching fixed-length slices.

In general, it is good to be able to customize a language based on zero, one or more words in a sentence, as in the following example:

fn print_words(sentence: &str) {
    let words: Vec<_> = sentence.split_whitespace().collect();

    match words.as_slice() {
        [] => println!("There were no words"),
        [word] => println!("Found 1 word: {}", word),
        _ => println!("Found {} words: {:? }", words.len(), words),
    }
}

fn main() {
    print_words("");
    print_words("Hello");
    print_words("Hello World!");
}
Copy the code

Code link: playground

The above code will print the following:

There were no words
Found 1 word: Hello
Found 2 words: ["Hello", "World!"]
Copy the code

Matches the beginning of a slice

. Known as the “rest” pattern, it lets you match the rest of the slice.

According to [] the ELF format (en.wikipedia.org/wiki/Execut… ELF, from which we can implement our IS_ELF () ‘function using rest mode.

use std::error::Error;

fn is_elf(binary: &[u8]) -> bool {
    match binary {
        [0x7f.b'E'.b'L'.b'F'. ] = >true, _ = >false,}}fn main() - >Result< (),Box<dyn Error>> {
    letcurrent_exe = std::env::current_exe()? ;letbinary = std::fs::read(&current_exe)? ;if is_elf(&binary) {
        print!("{} is an ELF binary", current_exe.display());
    } else {
        print!("{} is NOT an ELF binary", current_exe.display());
    }

    Ok(())}Copy the code

Code link: playground

Checking for Palindromes

A common problem in getting started with programming is detecting palindromes. We know that the @ symbol will bind a new variable to all its matches, so we can match at the head and tail of the slice to implement a more elegant is_palindrome() function.

fn is_palindrome(items: &[char]) -> bool {
    match items {
        [first, middle @ .., last] => first == last && is_palindrome(middle),
        [] | [_] => true,}}Copy the code

Code link: playground

A crude parameter parser

You may also want to “peeling off” the desired prefixes and suffixes in peeling mode.

Although more powerful Crate models like Clap and structopt already exist, we use slice mode to implement our own parameter parser.

fn parse_args(mut args: &[&str]) -> Args {
    let mut input = String::from("input.txt");
    let mut count = 0;

    loop {
        match args {
            ["-h" | "--help". ]  => { eprintln! ("Usage: main [--input <filename>] [--count <count>] <args>...");
                std::process::exit(1);
            }
            ["-i" | "--input", filename, rest @ .. ]  => { input = filename.to_string(); args = rest; } ["-c" | "--count", c, rest @ .. ]  => { count = c.parse().unwrap(); args = rest; } [..] = >break,}}let positional_args = args.iter().map(|s| s.to_string()).collect();

    Args {
        input,
        count,
        positional_args,
    }
}

struct Args {
    input: String,
    count: usize,
    positional_args: Vec<String>,}Copy the code

Code link: playground

Irrefutable pattern matching

Although this is not a technical part of the slicing pattern feature, you can still use pattern matching to deconstruct fixed-length arrays in addition to match and iflet states. This is a great way to avoid fussiness by indexing sequences that are always valid.

fn format_coordinates([x, y]: [f32; 2]) -> String {
    format!("{} | {}", x, y)
}

fn main() {
    let point = [3.14, -42.0];

    println!("{}", format_coordinates(point));

    let [x, y] = point;
    println!("x: {}, y: {}", x, y);
    // Much more ergonomic than writing this!
    // let x = point[0];
    // let y = point[1];
}
Copy the code

Code link: playground

conclusion

For many of the features in Rust, slicing patterns are not very complex when used properly, and they can significantly improve the expressiveness of code.