You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Stdin::read_line is very commonly used in programs written by beginners in rust. (For example, see the guessing game program in the Book.) Therefore, I think that it should be as easy to use as possible.
Code using read_line tends to be more verbose than similar code in other languages (because you need to manually create an empty string yourself before using it). Furthermore, it can be error-prone because read_line returns the string including the trailing newline, tripping up many beginners. (This issue comes up pretty frequently in the rust community discord.) And if you call read_line in a loop, you need to manually clear the string yourself in order to get the correct strings. This is a lot of complexity to avoid allocations, even though interactive CLI programs are bottlenecked by the human typing the input, so allocations often don't matter.
There exists the Stdin::lines method. Calling .next() on the resultant iterator produces Option<io::Result<String>>. This requires some finangling to properly handle errors. Additionally, it is not very discoverable, and it is not intuitive that doing user input would be done with iterators.
The input is read into a buffer until a newline (a 0xA byte) is reached or EOF is reached, whichever is first.
If EOF is reached without any bytes being read, return an Err with ErrorKind::UnexpectedEof.
If the buffer ends with 0xD,0xA, or ends with 0xA, trim those bytes.
Turn the buffer into a String, returning an Err with ErrorKind::InvalidData if it's not valid UTF-8.
Return the resulting String.
This behavior is the same as .lines().next(), except that instead of returning None to indicate EOF, an error is returned instead.
Alternatives
The status quo, which would encourage users to avoid allocations at the cost of potentially unnecessary complexity.
The return value could be of type io::Result<Option<String>>, with the exact same behavior as .lines().next().transpose(). I think that handling the error conditions properly is too cumbersome if this return type is used.
This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
We think this problem seems worth solving, and the standard library might be the right place to solve it.
We think that this probably doesn't belong in the standard library.
Second, if there's a concrete solution:
We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.
The text was updated successfully, but these errors were encountered:
Proposal
Problem statement
Stdin::read_line
is very commonly used in programs written by beginners in rust. (For example, see the guessing game program in the Book.) Therefore, I think that it should be as easy to use as possible.Code using
read_line
tends to be more verbose than similar code in other languages (because you need to manually create an empty string yourself before using it). Furthermore, it can be error-prone becauseread_line
returns the string including the trailing newline, tripping up many beginners. (This issue comes up pretty frequently in the rust community discord.) And if you callread_line
in a loop, you need to manually clear the string yourself in order to get the correct strings. This is a lot of complexity to avoid allocations, even though interactive CLI programs are bottlenecked by the human typing the input, so allocations often don't matter.There exists the
Stdin::lines
method. Calling.next()
on the resultant iterator producesOption<io::Result<String>>
. This requires some finangling to properly handle errors. Additionally, it is not very discoverable, and it is not intuitive that doing user input would be done with iterators.Similar comments also apply to
BufRead
.Solution sketch
I propose adding the following methods:
The methods would behave as follows:
0xA
byte) is reached or EOF is reached, whichever is first.Err
withErrorKind::UnexpectedEof
.0xD,0xA
, or ends with0xA
, trim those bytes.String
, returning anErr
withErrorKind::InvalidData
if it's not valid UTF-8.String
.This behavior is the same as
.lines().next()
, except that instead of returningNone
to indicate EOF, an error is returned instead.Alternatives
io::Result<Option<String>>
, with the exact same behavior as.lines().next().transpose()
. I think that handling the error conditions properly is too cumbersome if this return type is used.Links and related work
Prior discussion: https://internals.rust-lang.org/t/read-line-is-a-beginner-footgun-how-to-fix-it/20663
What happens now?
This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
Second, if there's a concrete solution:
The text was updated successfully, but these errors were encountered: