What should I return in this Ok(Input::Exit) match arm?

In the following handle_input() function, if Input is the Text or None variant, an Output returns.

Now, when the user presses Esc, Input will be the Exit variant. When that happens, the program/process will exit without running any command.

#[derive(Debug)]
pub enum Input {
    Text(String),
    None,
    Exit,
}

fn handle_input(
    input: Result<Input, std::io::Error>,
) -> Result<std::process::Output, std::io::Error> {
    match input {
        Ok(Input::Text(text)) => {
            let output = std::process::Command::new("echo")
                .arg(format!("{}", &text))
                .output()?;
            Ok(output)
        }
        Ok(Input::None) => {
            let output = std::process::Command::new("echo")
                .arg("Default text")
                .output()?;
            Ok(output)
        }
        Ok(Input::Exit) => {
            // What should I return here?
        }
        Err(e) => Err(e),
    }
}

fn get_input() -> Result<Input, std::io::Error> {
    let input = "Argument text".to_owned();

    // In the actual application, this may return other variants of `Input`.
    Ok(Input::Text(input))
}

fn main() {
    let input = get_input();
    let output = handle_input(input).unwrap();

    println!("{}", String::from_utf8_lossy(&output.stdout));
}

Rust Playground

What should I return in the Ok(Input::Exit) of the match arm? Right now, I get this error:

mismatched types
expected enum `Result<Output, std::io::Error>`
expected enum `Result<Output, std::io::Error>`

Because I'm not returning anything.

If I understand what you want to achieve correctly, you can use std::process::exit to exit your program. It returns ! (the never type), making it compatible with your other match arms.

2 Likes

If you don't want to exit the entire process immediately, either have a more complicated Ok variant than std::process::Output, or maybe a dedicated Err variant (potentially still using io::Error), and (either way) check for the clean exit request in main (or wherever such processing actually occurs).

4 Likes

Slight non-sequitur but unless you have something you want to do specifically with the error variant, you can simplify handle_input by using and_then:

#[derive(Debug)]
pub enum Input {
    Text(String),
    None,
    Exit,
}

fn handle_input(
    input: Input,
) -> Result<std::process::Output, std::io::Error> {
    match input {
        Input::Text(text) => {
            let output = std::process::Command::new("echo")
                .arg(format!("{}", &text))
                .output()?;
            Ok(output)
        }
        Input::None => {
            let output = std::process::Command::new("echo")
                .arg("Default text")
                .output()?;
            Ok(output)
        }
        Input::Exit => {
            // What should I return here?
            todo!("fix this")
        }
    }
}

fn get_input() -> Result<Input, std::io::Error> {
    let input = "Argument text".to_owned();

    // In the actual application, this may return other variants of `Input`.
    Ok(Input::Text(input))
}

fn main() {
    let input = get_input();
    let output = input.and_then(handle_input).unwrap();

    println!("{}", String::from_utf8_lossy(&output.stdout));
}
1 Like

That's a good solution, thanks. But since the program will be exiting in handle_input(), I won't be able the test that function, right?

Right, if you were to test handle_input(Input::Exit), your test suite would exit. If that's a problem for you, please refer to quinedot's suggested solution of making either the Ok or Err variants of your return type more complicated by adding a variant that indicates that the user wishes to exit the program.

1 Like

Thanks for the suggestion. I think it'll work.

#[derive(Debug)]
pub enum Input {
    Text(String),
    None,
    Exit,
}

#[derive(Debug)]
pub enum Output {
    Success(std::process::Output),
    Exit,
}

fn handle_input(input: Result<Input, std::io::Error>) -> Result<Output, std::io::Error> {
    match input {
        Ok(Input::Text(text)) => {
            let output = std::process::Command::new("echo")
                .arg(format!("{}", &text))
                .output()?;
            Ok(Output::Success(output))
        }
        Ok(Input::None) => {
            let output = std::process::Command::new("echo")
                .arg("Default text")
                .output()?;
            Ok(Output::Success(output))
        }
        Ok(Input::Exit) => {
            Ok(Output::Exit)
        }
        Err(e) => Err(e),
    }
}

fn get_input() -> Result<Input, std::io::Error> {
    let input = "Argument text".to_owned();
    Ok(Input::Exit)
}

fn main() {
    let input = get_input();
    let output = handle_input(input).unwrap();

    println!("Output: {:?}", output);
}

Rust Playground

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.