Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Is there a way to return multiple values from a Rust match statement?

I’m very new to Rust (coming from Python) and am working through past Advent of Codes as an avenue to learn the language more fully.

As part of one of the problems, I created a function to validate inputs read from a file, returning an enum to tell how to proceed:

enum ValidatorReturns {
    BRK,
    CNT,
    VALUES(i32, i32)
}

fn validate_inputs(contents: &mut String) -> ValidatorReturns {...}

fn pt2() -> Result<(), std::io::Error> {
    let file = File::open("input.input")?;
    let mut reader = BufReader::new(file);
    ...
    loop {
        let mut contents = String::new();
        let _ = reader.read_line(&mut contents)?;
        let mut val1: i32 = 0;  // <-- warning on never-read
        let mut val2: i32 = 0;  // <-- ditto
        let _ = match validate_inputs(&mut contents) {
            ValidatorReturns::BRK => break,
            ValidatorReturns::CNT => continue,
            ValidatorReturns::VALUES(tval1, tval2) => {
                val1 = tval1; // Ugly use of temp var names
                val2 = tval2;
            }
        };
        ...
    }
    ...
}

This works, but as I’ve commented, it leads to never-read warnings, has fairly ugly use of temp var names, and feels overall messy.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

  • Coming from Python, I was inclined to try returning val1, val2 as a tuple from the match statement, then unpack (rather than let _ = match ..., however that didn’t work and I was unable to find similar examples anywhere.
  • I also tried to declare both variables in the match-response block (i.e., let val1 = tval1;), and that led to an undeclared-variable compile error (which I can understand why, as it’s in a subscope).
  • I considered also just returning multiple bools (the first representing BRK, the second CNT, and then the two validated values) however this would be messy and seems un-Rust-like.

After some reading of the docs, I refactored it to use a struct as the enum member rather than storing the values directly in the enum:

struct ValidatedValues {
    val1: i32,
    val2: i32
}

enum ValidatorReturns {
    BRK,
    CNT,
    VALUES(ValidatedValues)
}

fn validate_inputs(contents: &mut String) -> ValidatorReturns {...}

fn pt2_enum() -> Result<(), std::io::Error> {
    let file = File::open("input.input")?;
    let mut reader = BufReader::new(file);
    ...
    loop {
        let mut contents = String::new();
        let _ = reader.read_line(&mut contents)?;
        let valvalues = match validate_inputs(&mut contents) {
            ValidatorReturns::BRK => break,
            ValidatorReturns::CNT => continue,
            ValidatorReturns::VALUES(val) => {val}  // <-- Still feels messy
        };
        ...
    }
    ...
}

It seems like this would be a common pattern with elegant syntax, but having to specify that I want the enum seems rather messy and unconsidered, in contrast to most other Rust syntax; that leads me to wonder if there’s a better way to handle this. This answer to another question has the phrase "An enum is one type only; its variants are purely that—variants, not types." That makes sense to me, though the solution I’ve outlined above still strikes me as a somewhat messy solution for what should be a common scenario – especially if it needs to be done per function. For example, the Option enum has ? or .unwrap() to extract the "main" value.

Is using a struct the best/correct way to handle returning multiple values which must be part of an enum? Or, alternatively, is there a better approach altogether which I’m missing?

>Solution :

Use a tuple. Here’s how:

let (val1, val2) = match validate_inputs(&mut contents) {
    ValidatorReturns::BRK => break,
    ValidatorReturns::CNT => continue,
    ValidatorReturns::VALUES(tval1, tval2) => {
        (tval1, tval2)
    }
};
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading