0

According to multiple sources, I believe this is the correct way to read a string from a file:

use std::error::Error;

fn main() {
    let path = std::path::Path::new("input.txt");

    let file = match std::fs::File::open(&path) {
        Err(e) => {
            panic!("Failed to read file {}: {}",
                   path.display(),
                   e.description())
        }
    };

    let mut s = String::new();
    let mut v = Vec::new();
    match file.read_to_string(&mut s) {
        Err(e) => panic!("Failed to read file contents: {}", e.description()),
    }

    println!("{}", s);
}

But this code produces an error using Rust 1.17.0 so I must be missing something:

error: the type of this value must be known in this context
  --> src/main.rs:16:11
   |
16 |     match file.read_to_string(&mut s) {
   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Reuben
  • 133
  • 2
  • 9
  • 3
    You're missing the `Ok` branch of your match. So the match block expression can only evaluate to the panic!() which isn't a 'known type' per the error. – turbulencetoo May 09 '17 at 19:57
  • That was it, thank you! :) I guess I should be more literal when copying examples. If you submit as an answer I'll accept it. – Reuben May 09 '17 at 20:40
  • 1
    @turbulencetoo Isn't the compiler supposed to complain when a match arm is missing? I suspect the compiler is, in an attempt to temporarily ignore an error and provide more useful diagnostic, in fact reporting a *less* useful error. When you remove the second `match` (and the definition of `v`), the compiler correctly complains about non-exhaustive patterns with `Ok(_)` not being covered. – user4815162342 May 09 '17 at 21:14
  • right!? I'm not really proficient enough to explain exactly what's going on which is why I left my explanation as a comment. – turbulencetoo May 09 '17 at 21:17
  • @user4815162342 I don't think the compiler is holding onto the error, it's just "normal" type inference. Because there's no `Ok` case, the type of `file` can't be inferred, so that error is reported first. – Shepmaster May 09 '17 at 21:22

1 Answers1

4

You have multiple overlapping issues. Whenever debugging a programming problem, it helps to create a Minimal, Complete Verifiable Example.

Start by commenting out match file.read_to_string(&mut s) { /* ... */ }. Then you will get another error:

error[E0282]: type annotations needed
  --> src/main.rs:15:17
   |
15 |     let mut v = Vec::new();
   |         -----   ^^^^^^^^ cannot infer type for `T`
   |         |
   |         consider giving `v` a type

Comment out that line too, giving:

error[E0004]: non-exhaustive patterns: `Ok(_)` not covered
 --> src/main.rs:6:22
  |
6 |     let file = match std::fs::File::open(&path) {
  |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Ok(_)` not covered

This is your real issue. Result is an enum with two values, Ok and Err. You have to handle all variants in a match.

In this case, it's easiest to use unwrap_or_else:

let file = std::fs::File::open("input.txt").unwrap_or_else(|e| {
    panic!(
        "Failed to read file {}: {}",
        path.display(),
        e.description()
    )
});

You can remove the unused vector and apply the same unwrap_or_else to the other failure case. You then need to:

  1. Import std::io::Read.
  2. Declare file as mutable.

You can also:

  1. Print an error directly using {}.
  2. Pass a string slice to File::open.
use std::io::Read;

fn main() {
    let path = "input.txt";
    let mut file = std::fs::File::open(path).unwrap_or_else(|e| {
        panic!("Failed to read file {}: {}", path, e);
    });

    let mut s = String::new();
    file.read_to_string(&mut s).unwrap_or_else(|e| {
        panic!("Failed to read file contents: {}", e);
    });

    println!("{}", s);
}

Compare your code against What's the de-facto way of reading and writing files in Rust 1.x? as well.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Sorry for including the `let mut v = Vec::new();`, I was experimenting with another approach to see why the error was happening and forgot to remove that before uploading. Thanks for the very complete response and link. :) – Reuben May 10 '17 at 00:12
  • @Shepmaster just wondering why you chose `*.unwrap_or_else()` instead of `*.expect(). ` Seems like the inclusion of the specific error message `|e|` does not contribute much since the text of the message tells the story. Just was wondering if `.expect()` would be more economical. Or could you use `?` as in `file.read_to_string(&mut s)?;` – krishnab May 13 '17 at 03:23
  • @krishnab No, you [cannot use `?` in a function that doesn't return `Result`](http://stackoverflow.com/q/30555477/155423). The important thing here was the use of `path` in the first failure case; the error message doesn't include that. The second case was done just for consistency. – Shepmaster May 13 '17 at 04:15