Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to match on data type in Rust?

Tags:

rust

I'm trying to match on the datatype of a generic field of a struct and react accordingly. My general idea was like this (code doesn't compile):

struct Foo<T> {
    bar: T,
}

fn main() {
    let x = Foo::<String> {
        bar: "world".to_string(),
    };

    match x.bar {
        String => println!("It's a string!"),
        u32 => println!("It's a u32!"),
        _ => println!("Something else"),
    };

    println!("end of program!");
}

The error message from the compiler:

warning: unreachable pattern
  --> src/main.rs:12:9
   |
11 |         String => println!("It's a string!"),
   |         ------ matches any value
12 |         u32 => println!("It's a u32!"),
   |         ^^^ unreachable pattern
   |
   = note: `#[warn(unreachable_patterns)]` on by default

warning: unreachable pattern
  --> src/main.rs:13:9
   |
11 |         String => println!("It's a string!"),
   |         ------ matches any value
12 |         u32 => println!("It's a u32!"),
13 |         _ => println!("Something else"),
   |         ^ unreachable pattern

warning: unused variable: `String`
  --> src/main.rs:11:9
   |
11 |         String => println!("It's a string!"),
   |         ^^^^^^ help: consider prefixing with an underscore: `_String`
   |
   = note: `#[warn(unused_variables)]` on by default

warning: unused variable: `u32`
  --> src/main.rs:12:9
   |
12 |         u32 => println!("It's a u32!"),
   |         ^^^ help: consider prefixing with an underscore: `_u32`

warning: variable `String` should have a snake case name
  --> src/main.rs:11:9
   |
11 |         String => println!("It's a string!"),
   |         ^^^^^^ help: convert the identifier to snake case: `string`
   |
   = note: `#[warn(non_snake_case)]` on by default

What I wanted was for x to match the first one. I'm actually not sure what I want to do can be done, but what would achieve the desired effect?

like image 647
Dash83 Avatar asked Jan 11 '17 17:01

Dash83


1 Answers

Idiomatic Solution

Create a trait which constrains the parameter T in Foo, implement any specific behavior as an associated function of this trait.

Example:

trait PrintMe {
    fn print_me(&self);
}

impl PrintMe for String {
    fn print_me(&self) { println!("I am a string"); }
}

struct Foo<T: PrintMe> {
    bar: T
}

fn main() {
    // ...
    x.bar.print_me();
}

This is principled generic programming, where you declare exactly the difference of behavior of the possible generic parameters, so that there is no surprise.

See also:

  • Why is this match pattern unreachable when using non-literal patterns?

Exact Solution

Rust can indeed query types: each type has a unique TypeId associated, and you can match on TypeId with a series of if checks. It's clunky.

fn print_me<T>(x: &Foo<T>) {
    if TypeId::of::<T>() == TypeId::of::<String>() {
        println!("I am a string");
    } else // ...
}

But please... don't do that :)

like image 145
Matthieu M. Avatar answered Oct 12 '22 14:10

Matthieu M.