Skip to content

docs: Explain why/when .lines() returns an error #37744

Closed
@anholt

Description

@anholt

context: I'm doing the newbie Rust thing of writing some program that reads lines of a file. From stackoverflow I find myself at BufReader.lines() and then BufRead.lines() docs.

What I'm finding confusing in the docs is that lines() returns a Result, but the example in the docs blindly unwrap()s that result, and so does every caller of lines() I find in demo code. It would be nice if BufRead (or, if it's more appropriate there, then maybe in BufReader's impl) would explain under what circumstances you'd get an error instead of Ok when reading a file, so I know if unwrap() is appropriate to use or not.

Activity

added
E-easyCall for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue.
on Nov 13, 2016
bluss

bluss commented on Nov 13, 2016

@bluss
Member

As a general rule for IO: it's interacting with an environment that's outside of the program's control, so errors can never really be ruled out. When BufReader’s operations return an error depends on which Read implementation it is wrapping.

A good reason to not use .unwrap() is composability. You're writing a larger program, and you don't want unwraps or error handling spread throughout the smaller helper functions. Instead each of them bubble up errors so that you can handle errors at a higher level (and in fewer places). The macro try!() and equivalently the operator ? are made for that.

Below there's a new example. The both read all functions do the same thing. Using collect() felt a bit cryptic for an example, but that's what you would use for that particular task (just gathering the results from an iterator). In general, lots of functions will be using ? (or equivalently try!()) instead, and the second read all function shows that.

use std::io;
use std::io::prelude::*;

fn main() {
    let stdin = io::stdin();
    let lines = match read_all_lines(stdin.lock()) {
        Err(err) => {
            println!("Failed to read input: {}", err);
            ::std::process::exit(1);
        }
        Ok(result) => result,
    };

    //  continue the program with lines: Vec<String>
}

fn read_all_lines<R: BufRead>(reader: R) -> Result<Vec<String>, io::Error> {
    // magic: .collect() can transform an iterator of Result<T, E> into a Result<Vec<T>, E>!
    reader.lines().collect()
}

fn read_all_lines_2<R: BufRead>(reader: R) -> Result<Vec<String>, io::Error> {
    let mut lines = Vec::new();
    for line_result in reader.lines() {
        // magic? return with an error if there is an error,
        // otherwise push the String to the vector.
        lines.push(line_result?);
    }
    Ok(lines)
}
anholt

anholt commented on Nov 15, 2016

@anholt
Author

Yeah, that seems a bit overcomplicated for the example in that section, and I like that the current example keeps the lines as an iterator instead of collecting.

What I'm looking for is a note like "Note that stdin.lines() may return an ErrorKind::Interrupted when ^C is hit, which would panic on the unwrap()" or something. What got me here was trying to figure out what errors could happen when reading from a std::fs::File wrapped in a BufReader, or whether the unwrap() I had seen in every single example was actually safe.

bluss

bluss commented on Nov 15, 2016

@bluss
Member

Good request. The docs should indicate what kind of reading is done and if it would return the Interrupted error.

added a commit that references this issue on Dec 24, 2016

Rollup merge of rust-lang#38505 - estebank:why-lines, r=frewsxcv

3c26ff4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    E-easyCall for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @bluss@anholt

        Issue actions

          docs: Explain why/when .lines() returns an error · Issue #37744 · rust-lang/rust