Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I store a pattern in a variable in Rust?

Tags:

rust

I'm implementing a parser in Rust and whitespace is a common pattern that I want to reuse in match patterns.

This code works:

let ch = ' ';

match ch {
    ' ' | '\n' | '\t' | '\r' => println!("whitespace"),
     _ => println!("token"),
}

This would get really repetitive if I need to keep on specifying the whitespace pattern each time. I would like to define that once and reuse it. I want to do something like:

let whitespace = ' ' | '\n' | '\t' | '\r';

let ch = ' ';

match ch {
    whitespace => println!("whitespace"),
    _          => println!("token"),
}

The compiler does not like the ws assignment. It interprets the | as a binary operation instead of alternation.

Can patterns be stored in variables somehow? Is there a better or more idiomatic way to do this?

like image 720
Brennan Cheung Avatar asked Mar 14 '17 18:03

Brennan Cheung


People also ask

Does Rust have pattern matching?

Patterns are a special syntax in Rust for matching against the structure of types, both complex and simple. Using patterns in conjunction with match expressions and other constructs gives you more control over a program's control flow.

How do you match types in Rust?

TLDR: in Rust, to match over type, we create a trait, implement a function for each type and call it on the element to match. Surround it with backticks to mark it as code. Single backticks for inline code, triple backticks for code blocks.

What does match do in Rust?

Rust has an extremely powerful control flow construct called match that allows you to compare a value against a series of patterns and then execute code based on which pattern matches.


1 Answers

Can patterns be stored in variables somehow?

No. Patterns are a compile-time construct, and variables hold run-time concepts.

Is there a better or more idiomatic way to do this?

Creating a function or method is always a good solution to avoid repeating code. You can then use this as a guard clause:

fn is_whitespace(c: char) -> bool {
    match c {
        ' ' | '\n' | '\t' | '\r' => true,
        _ => false,
    }
}

fn main() {
    let ch = ' ';

    match ch {
        x if is_whitespace(x) => println!("whitespace"),
        _ => println!("token"),
    }
}

I'd also strongly recommend using an existing parser, of which there are a multitude, but everyone wants their Rust "hello world" to be parsing, for whatever reason.

A parsing library I use allows writing code akin to this, where whitespace is a function that knows how to parse the valid types of whitespace:

sequence!(pm, pt, {
    _          = literal("if");
    ws         = whitespace;
    _          = literal("let");
    ws         = append_whitespace(ws);
    pattern    = pattern;
    ws         = optional_whitespace(ws);
    _          = literal("=");
    ws         = optional_whitespace(ws);
    expression = expression;
}, |_, _| /* do something with pieces */);

Each of the things on the right-hand side are still individual functions that know how to parse something specific.

like image 75
Shepmaster Avatar answered Sep 30 '22 20:09

Shepmaster