Match arms have incompatible type

I wrote this code while learning error handling:

#![allow(unused)]

use std::io;
use std::fs::File;
use std::io::Read;



fn main() {
    openfile4();
}


fn openfile4() -> Result<String, std::io::Error> {
    let result = File::open("hello.txt");
    match result {
        Ok(mut fileh) => {
            let mut s = String::new();
            match fileh.read_to_string(&mut s) {
                Ok(_) => Ok(s),
                Err(ef) => Err(ef),
            }
        },
        eff => eff
    }
}

this gives error that 'match arms have incompatible types' :

error[E0308]: `match` arms have incompatible types
  --> src/main.rs:24:16
   |
16 |       match result {
   |       ------------ `match` arms have incompatible types
...
19 | /             match fileh.read_to_string(&mut s) {
20 | |                 Ok(_) => Ok(s),
21 | |                 Err(ef) => Err(ef),
22 | |             }
   | |_____________- this is found to be of type `Result<String, std::io::Error>`
23 |           },
24 |           eff => eff
   |                  ^^^ expected struct `String`, found struct `File`
   |
   = note: expected enum `Result<String, _>`
              found enum `Result<File, _>`

error: aborting due to previous error

It doesn't make sense to me though - the eff => eff is simply returning Err(std::io::Error) right? I don't get the expected struct String, found struct File message either.

I know that the error goes away by using Err(eff) => Err(eff), but the way I see it, the above should work as well.

Can someone please help me make sense of the error message above?

Even though, logically, hitting that branch must mean that you have an Err(std::io::Error), this is just a variant of an enum -- and variants aren't types on their own. The type of a variant is the type of it's parent enum. So the type of eff is still Result<File, std::io::Error>. There is no automatic coercion here to turn it into a Result<String, std::io::Error>.

The try operator, ?, will infer the correct conversion, however. Here's how I would write that function:

fn openfile4() -> Result<String, std::io::Error> {
    let mut fileh = File::open("hello.txt")?;

    let mut s = String::new();
    let _ = fileh.read_to_string(&mut s)?;
    Ok(s)
}
2 Likes

In this case the easiest solution is to create a new Err like this:

fn openfile4() -> Result<String, std::io::Error> {
    let result = File::open("hello.txt");
    match result {
        Ok(mut fileh) => {
            let mut s = String::new();
            match fileh.read_to_string(&mut s) {
                Ok(_) => Ok(s),
                Err(ef) => Err(ef),
            }
        },
        Err(eff) => Err(eff),
    }
}
3 Likes