Help needed with match

I have a piece of code that reads a file using read_to_end like:

        match File::open(file_name) {
            Err(err) => "boo hoo".to_string(),
            Ok(mut f) =>  {
                let mut bytes:Vec<u8> = Vec::new();
                match f.read_to_end(&mut bytes) {
                    Err(err) => "boo hoo".to_string(),
                    Ok(_) => "Ha ha".to_string(),
                },
            }, 

At the end of the match statement match f.read_to_end(&mut bytes)
the compiler error:

                 },
   |                  ^ expected expression

But both legs of the match are returning a String.

Below is a complete programme that illustrates the error followed by th eerror followed by a programme that is subtly different but runs as expected

use std::fs::File;
use std::io::Read;
fn main() {
    let file_name = "/tmp/test.txt";
    let test_value = 
        match File::open(file_name) {
            Err(err) => "boo hoo".to_string(),
            Ok(mut f) =>  {
                let mut bytes:Vec<u8> = Vec::new();
                match f.read_to_end(&mut bytes) {
                    Err(err) => "boo hoo".to_string(),
                    Ok(_) => "Ha ha".to_string(),
                },
            }, 
        };
    println!("Got string: {}", test_value);
}

Error:

error: expected expression, found `,`
  --> src/main.rs:13:18
   |
13 |                 },
   |                  ^ expected expression

error[E0308]: mismatched types
  --> src/main.rs:10:17
   |
10 | /                 match f.read_to_end(&mut bytes) {
11 | |                     Err(err) => "boo hoo".to_string(),
12 | |                     Ok(_) => "Ha ha".to_string(),
13 | |                 },
   | |_________________^ expected (), found struct `std::string::String`
   |
   = note: expected type `()`
              found type `std::string::String`

Programme that runs:

use std::fs::File;
use std::io::Read;
fn main() {
    let file_name = "/tmp/test.txt";
    let mut bytes:Vec<u8> = Vec::new();
    let test_value = 
        match File::open(file_name) {
            Err(_) => "boo hoo".to_string(),
            Ok(mut f) =>  
                match f.read_to_end(&mut bytes) {
                    Err(_) => "boo hoo".to_string(),
                    Ok(_) => "Ha ha".to_string(),
                },
            
        };
    println!("Got string: {}", test_value);
}

I am missing something here… (More than the unused err variables!)

The comma is used after the pattern => expression, but not within general expressions.

In the failing case you have => { match { ... }, },
Try instead => { match { ... } },

The working case has only the outer comma => match { ... },

1 Like

More generally, this can be summarized to the following:

match x {
    Ok(_) => expr1,
    Err(_) => expr2,
}

Where, for example, expr2 is in your example:

{
    match y {
        Ok(_) => expr3,
        Err(_) => expr4,
    }, //<-- Note here
}

But, lets say we decide to minimize the match block, we can see the following:

{
    match y {/**/},//<-- Note here
}

and therefore if we substitute it in:

match x {
    Ok(_) => expr1,
    Err(_) => { match y { /**/ }, /*<-- Note here*/ }
}

But this comma is erroneous because the second match is the implicit return from the code block that is the Err(_) branch:

match x {
    Ok(_) => expr1,
    Err(_) => { /*This is a block too!*/ }
}

And this follows the same rules for any other implicit return, and therefore it would be analogous to trying to say the following:

fn foo() -> usize {
    let x = 0usize;
    x, //<-- Note here
}

But that, once again would be erroneous.
In your second example, your inner match y statement becomes the block that used to contain it, therefore making the comma belong to the match x's syntax symbolizing a trailing comma after a pattern.

Thank you.

I took out the comma. Obvious really!!

1 Like