Match enum based on user input

Hi, I am trying to learn about enums in Rust, so decided to write a sample code as exercise. It takes the input from user based on 4 conditions(Up, Down, Left, Right), then match the input with the enum. But I met a problem.

The code:

#![allow(dead_code)]
use std::io;

enum Direction {
    Up,
    Down,
    Left,
    Right
}

fn enum_match(direction: Direction) {
    match direction {
        Direction::Up => println!("We are heading up!"),
        Direction::Down => println!("We are going all the wayy down!"),
        Direction::Left => println!("Left side, left side!"),
        Direction::Right => println!("Right, going right!"),
    }
}

fn main() {
    let mut input = String::new();
    println!("Hey mate, which direction you want to go? (Up, Down, Left, Right): ");
    
    enum_match(Direction::input);
}

The compiler returned error:

error[E0599]: no variant or associated item named `input` found for enum `Direction` in the current scope
  --> src/main.rs:24:27

I knew it complained about no such item input during defining the enum. So, how can I do to pass the user input string to the enum so it'll make it like Direction::Up in the end?

Or worse, maybe I misunderstood the use of enum, which it is not intended to do the things like this?

There is no auto conversion between user input (&str or String) and your enum.
If you want to convert a user input to your enum you have to write such functionality and think about what happens if the user input is invalid

e.g.

use std::io::BufRead;

#[derive(Debug)]
enum Direction {
    Up,
    Down,
    Left,
    Right
}

#[derive(Debug)]
struct InputError;

impl Direction {
    fn from_str(s: &str) -> Result<Direction, InputError> {
        match s {
            "Up" => Ok(Direction::Up),
            "Down" => Ok(Direction::Down),
            "Left" => Ok(Direction::Left),
            "Right" => Ok(Direction::Right),
            _ => Err(InputError)
        }
    }
}

fn enum_match(direction: Direction) {
    match direction {
        Direction::Up => println!("We are heading up!"),
        Direction::Down => println!("We are going all the wayy down!"),
        Direction::Left => println!("Left side, left side!"),
        Direction::Right => println!("Right, going right!"),
    }
}

fn main() {
    let mut input = String::new();
    println!("Hey mate, which direction you want to go? (Up, Down, Left, Right): ");
    std::io::BufReader::new(std::io::stdin()).read_line(&mut input).unwrap();
    let dir = Direction::from_str(input.trim()).unwrap();
    enum_match(dir);
}


3 Likes

Note also that this code, as written, will always produce an error, since read_line includes the trailing \n.

1 Like

Rust is a statically-typed language, and as such, scopes and name resolution is static, too. This means that scopes and names of items (functions, types, etc.) are resolved at compile time.

There is no implicit runtime splicing of arbitrary strings into the source code (it's impossible, since the source code is long gone by the time a Rust program runs anyway). You can't write something like eval("Direction::" + input), either. Generally, that's just not a thing in statically-typed, compiled languages.

The expression Direction::input literally means "get me the variant named input of the enum named Direction". Which does not exist.

If you want to yield one of the variants of an enum corresponding to a dynamic string, you have to perform the conversion. One approach to that has been elaborated in previous posts; if you want to automate away the tedium of spelling out every possible variant, you can #[derive(EnumString)] using the Strum crate, which gives you an automatic implementation of FromStr.

3 Likes

Thanks for the answer, your answer made me know that Rust requires me to think everything thoroughly, which I never cared that much from a dynamically-typed language background.

Add a trim()