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 make a tokenizer. I need to read every line the user types and stop reading once the user presses ctrl-D.
I searched around and only found one example on Rust IO which does not even compile. I looked at the io
module's documentation and found that the read_line()
function is part of the ReaderUtil
interface, but stdin()
returns a Reader
instead.
The code that I would like would essentially look like the following in C++
vector<string> readLines () {
vector<string> allLines;
string line;
while (cin >> line) {
allLines.push_back(line);
}
return allLines;
}
In Rust 1.0 and later, you can use the
lines
method on anything that implements thestd::io::BufRead
trait to obtain an iterator over lines in the input. You could also useread_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)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):
To obtain something that implements
BufRead
, which is needed to calllines()
orread_line()
, you callstd::io::stdin()
to obtain a handle to standard input, and then calllock()
on the result of that to obtain exclusive control of the standard input stream (you must have exclusive control to obtain aBufRead
, because otherwise the buffering could produce arbitrary results if two threads were reading from stdin at once).To collect the result into a
Vec<String>
, you can use thecollect
method on an iterator.lines()
returns an iterator overResult<String>
, so we need to handle error cases in which a line could not be read; for this example, we just ignore errors with afilter_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 ourVec
. Because we transfer ownership of the string to theVec
when we do that, and becauseread_line
would otherwise keep appending to thestring
, 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 usewhile 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.The question is to read lines from stdin and return a vector. On Rust 1.7:
Uh... After many trials and errors, I've found a solution.
I'd still like to see a better solution so I'm not going to accept my own solution.
The code below prints exactly what the user inputs.
Rust 1.x (see documentation):
Rust 0.10–0.12 (see documentation):
Rust 0.9 (see 0.9 documentation):
Rust 0.8:
Rust 0.7:
This is what I have come up with (with some help from the friendly people in the #rust IRC channel on irc.mozilla.org):
And proof that it works :)
There are few ways I can think of.
Read all the input into single
String
Read lines into
Vector
. This one doesn'tpanic
when reading a line fails, instead it skips that failed line.Furthermore if you want to parse it:
After reading it into string do this. You can parse it to other collections that implements
FromIterator
. Contained elements in the collection also must implementFromStr
. As long as the trait constraint satisfies you can change Vec to anyCollection:FromIterator
,Collection<T: FromStr>
Also you can use it on the
StdinLock