How to read user input in Rust?

前端 未结 10 1689
情歌与酒
情歌与酒 2021-01-31 07:50

Editor\'s note: This question refers to parts of Rust that predate Rust 1.0, but the general concept is still valid in Rust 1.0.

I intend to

10条回答
  •  天涯浪人
    2021-01-31 08:23

    In Rust 1.0 and later, you can use the lines method on anything that implements the std::io::BufRead trait to obtain an iterator over lines in the input. You could also use read_line , but using the iterator is more likely what you'd want. Here is a version of the function in the question using iterators; see below for a more detailed explanation. (playground link)

    use std::io;
    use std::io::prelude::*;
    
    pub fn read_lines() -> Vec {
        let stdin = io::stdin();
        let stdin_lock = stdin.lock();
        let vec = stdin_lock.lines().filter_map(|l| l.ok()).collect();
    
        vec
    }
    

    And here's a version that is more like the C++ version in the question, but is not really the idiomatic way to do this in Rust (playground):

    use std::io;
    use std::io::prelude::*;
    
    pub fn read_lines() -> Vec {
        let mut vec = Vec::new();
        let mut string = String::new();
    
        let stdin = io::stdin();
        let mut stdin_lock = stdin.lock();
    
        while let Ok(len) = stdin_lock.read_line(&mut string) {
            if len > 0 {
               vec.push(string);
               string = String::new();
            } else {
                break
            }
        }
    
        vec
    }
    

    To obtain something that implements BufRead, which is needed to call lines() or read_line(), you call std::io::stdin() to obtain a handle to standard input, and then call lock() on the result of that to obtain exclusive control of the standard input stream (you must have exclusive control to obtain a BufRead, because otherwise the buffering could produce arbitrary results if two threads were reading from stdin at once).

    To collect the result into a Vec, you can use the collect method on an iterator. lines() returns an iterator over Result, so we need to handle error cases in which a line could not be read; for this example, we just ignore errors with a filter_map that just skips any errors.

    The C++ like version uses read_line, which appends the read line to a given string, and we then push the string into our Vec. Because we transfer ownership of the string to the Vec when we do that, and because read_line would otherwise keep appending to the string, we need to allocate a new string for each loop (this appears to be a bug in the original C++ version in the question, in which the same string is shared and so will keep accumulating every line). We use while let to continue to read until we hit an error, and we break if we ever read zero bytes which indicates the end of the input.

提交回复
热议问题