Read contents of file with appropriate error handling (Rust) I am trying to learn basic Rust. Is this a good way to get the contents of a file? Am I handling errors the best way? Is there any way this could be more performant?

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

fn main() {
    let path = "bar/foo.txt";

    match read(path) {
        Ok(contents) =&gt; println!("{}", contents),
        Err(err) =&gt; println!("Unable to read '{}': {}", path, err),
    }
}

fn read(file: &amp;str) -&gt; Result&lt;String, std::io::Error&gt; {
    let mut file = match File::open(file) {
        Ok(mut f) =&gt; f,
        Err(err) =&gt; return Err(err),
    };

    let mut data = String::new();
    match file.read_to_string(&amp;mut data) {
        Ok(_) =&gt; return Ok(data),
        Err(err) =&gt; return Err(err),
    }
}
Your code gives a warning:

warning: variable does not need to be mutable
  --&gt; src/main.rs:15:12
   |
15 |         Ok(mut f) =&gt; f,
   |            ^^^^^
   |


When you move a value from one binding to another, the two bindings don't have to agree on mutability. It may sound strange, but it's valid to move from an immutable binding to a mutable binding. It's perfectly safe because in order to be able to move a value, there must not be any pointers to it, which ensures that you have exclusive access to the value. Therefore, you could write Ok(f) here instead of Ok(mut f).
The match / return Err pattern is so frequent that Rust has a shorthand for it: the ? operator (and its predecessor, the try! macro). We could rewrite your program using the ? operator like this:

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

fn main() {
    let path = "bar/foo.txt";

    match read(path) {
        Ok(contents) =&gt; println!("{}", contents),
        Err(err) =&gt; println!("Unable to read '{}': {}", path, err),
    }
}

fn read(file: &amp;str) -&gt; Result&lt;String, std::io::Error&gt; {
    let mut file = File::open(file)?;

    let mut data = String::new();
    file.read_to_string(&amp;mut data)?;
    Ok(data)
}


The second match in read ends the function, so we don't need the ? operator. Instead, we could use map to replace the data in the Ok variant while keeping the Err the same, and then just return the result of map.

fn read(file: &amp;str) -&gt; Result&lt;String, std::io::Error&gt; {
    let mut file = File::open(file)?;

    let mut data = String::new();
    file.read_to_string(&amp;mut data).map(|_| data)
}


Which one you use is up to you. :)
File::open accepts more than just &amp;str. Its signature is:

fn open&lt;P: AsRef&lt;Path&gt;&gt;(path: P) -&gt; Result&lt;File&gt;


You could make your own read function more generic by introducing a type parameter.

use std::path::Path;

fn read&lt;P: AsRef&lt;Path&gt;&gt;(file: P) -&gt; Result&lt;String, std::io::Error&gt; {
    let mut file = File::open(file)?;

    let mut data = String::new();
    file.read_to_string(&amp;mut data)?;
    Ok(data)
}


Look at the list of implementations for AsRef&lt;Path&gt; to see what types you can now pass to your function.