Skip to content

Commit d1b96ae

Browse files
Add RFC for std::inputln()
1 parent cfa5d53 commit d1b96ae

File tree

1 file changed

+181
-0
lines changed

1 file changed

+181
-0
lines changed

text/0000-inputln.md

+181
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
- Feature Name: `inputln`
2+
- Start Date: 2021-11-16
3+
- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000)
4+
- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000)
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
Add an `inputln` function to `std` to read a line from standard input and return
10+
a `std::io::Result<String>`.
11+
12+
# Motivation
13+
[motivation]: #motivation
14+
15+
Building a small interactive program that reads input from standard input and
16+
writes output to standard output is well-established as a simple and fun way of
17+
learning and teaching a new programming language. Case in point the chapter 2
18+
of the official Rust book is [Programming a Guessing Game], which suggests the
19+
following code:
20+
21+
[Programming a Guessing Game]: https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html
22+
23+
```rs
24+
let mut guess = String::new();
25+
26+
io::stdin()
27+
.read_line(&mut guess)
28+
.expect("Failed to read line");
29+
```
30+
31+
While the above code is perfectly clear to everybody who already knows Rust, it
32+
can be quite overwhelming for a beginner. What is `mut`? What is `&mut`? The
33+
2nd chapter gives only basic explanations and assures that mutability and
34+
borrowing will be explained in detail in later chapters. Don't worry about that
35+
for now, everything will make sense later. But the beginner might worry about
36+
something else: Why is something so simple so complicated with Rust? For example
37+
in Python you can just do `guess = input()`. Is Rust always this cumbersome?
38+
Maybe they should rather stick with their current favorite programming language
39+
instead.
40+
41+
This RFC therefore proposes the introduction of a `std::inputln` function so
42+
that the above example could be simplified to just:
43+
44+
```rs
45+
let guess = inputln().expect("Failed to read line");
46+
```
47+
48+
This would allow for a more graceful introduction to Rust. Letting beginners
49+
experience the exciting thrill of running their own first interactive Rust
50+
program, without being confronted with mutability and borrowing straight away.
51+
While mutability and borrowing are very powerful concepts, Rust does not force
52+
you to use them when you don't need them. The examples we use to teach Rust to
53+
complete beginners should reflect that.
54+
55+
# Guide-level explanation
56+
[guide-level-explanation]: #guide-level-explanation
57+
58+
`std::inputln()` is a convenience wrapper around `std::io::Stdin::read_line`,
59+
introduced so that Rust beginners can create interactive programs without having
60+
to worry about mutability or borrowing. The function allocates a new String
61+
buffer for you, and reads a line from standard input. The result is returned as
62+
a `std::io::Result<String>`.
63+
64+
If you are repeatedly reading lines from standard input and don't need to
65+
allocate a new String for each of them you should be using
66+
`std::io::Stdin::read_line` directly instead, so that you can reuse an existing
67+
buffer.
68+
69+
# Reference-level explanation
70+
[reference-level-explanation]: #reference-level-explanation
71+
72+
```rs
73+
pub fn inputln() -> std::io::Result<String> {
74+
let mut input = String::new();
75+
std::io::stdin().read_line(&mut input)?;
76+
Ok(input)
77+
}
78+
```
79+
80+
# Drawbacks
81+
[drawbacks]: #drawbacks
82+
83+
* Can lead to unnecessary buffer allocations in Rust programs when developers
84+
don't realize that they could reuse a buffer instead. This could potentially
85+
be remedied by a new Clippy lint.
86+
87+
* There is no precedent for a function residing directly in the `std` module
88+
(currently it only contains macros). So Rust programmers might out of habit
89+
try to call `inputln!()`. This should however not pose a big hurdle because
90+
`rustc` already provides a helpful error message:
91+
92+
```
93+
error: cannot find macro `inputln` in this scope
94+
--> src/main.rs:13:5
95+
|
96+
13 | inputln!();
97+
| ^^^^^^^
98+
|
99+
= note: `inputln` is in scope, but it is a function, not a macro
100+
```
101+
102+
# Rationale and alternatives
103+
[rationale-and-alternatives]: #rationale-and-alternatives
104+
105+
> Why is this design the best in the space of possible designs?
106+
107+
It is the simplest solution to the explained problem.
108+
109+
> What other designs have been considered and what is the rationale for not
110+
> choosing them?
111+
112+
The function could also be implemented as a macro but there is really no need for that.
113+
114+
> What is the impact of not doing this?
115+
116+
A higher chance of Rust beginners getting overwhelmed by mutability and borrowing.
117+
118+
# Prior art
119+
[prior-art]: #prior-art
120+
121+
Python has [input()], Ruby has [gets], C# has `Console.ReadLine()`
122+
... all of these return a string read from standard input.
123+
124+
[input()]: https://docs.python.org/3/library/functions.html#input
125+
[gets]: https://ruby-doc.org/docs/ruby-doc-bundle/Tutorial/part_02/user_input.html
126+
127+
Other standard libraries additionally:
128+
129+
* accept a prompt to display to the user before reading from standard input
130+
(e.g. Python and Node.js)
131+
132+
* provide some functions to parse multiple values of specific data types
133+
into ovariables (e.g. C's `scanf`, C++, Java's `Scanner`)
134+
135+
Python's `input()` function accepts a `prompt` argument because Python's output
136+
is line buffered by default, meaning a `print()` without a newline would only be
137+
output after a manual flush. Node.js accepts a prompt because its
138+
[readline](https://nodejs.org/api/readline.html) interface is very high level.
139+
Both reasonings don't apply to Rust. With Rust a simple `print!()` call before
140+
invoking `inputln()` suffices to display an input prompt and more high-level
141+
interfaces are better provided by crates.
142+
143+
While scanning utilities could also be added to the Rust standard library, how
144+
these should be designed is less clear, as well as whether or not they should be
145+
in the standard library in the first place. There exist many well established
146+
input parsing libraries for Rust that are only a `cargo install` away. The same
147+
argument does not apply to `inputln()` ... beginners should be able to get
148+
started with an interactive Rust program without having to worry about
149+
mutability, borrowing or having to install a third-party library.
150+
151+
# Unresolved questions
152+
[unresolved-questions]: #unresolved-questions
153+
154+
> What parts of the design do you expect to resolve through the RFC process
155+
> before this gets merged?
156+
157+
The name of the function is up to debate. `read_line()` would also be a
158+
reasonable choice, that does however potentially beg the question: Read from
159+
where? `inputln()` hints that the line comes from standard input.
160+
161+
The location of the function is also up to debate. It could also reside in
162+
`std::io`, which would however come with the drawback that beginners need to
163+
either import it or prefix `std::io`, both of which seem like unnecessary
164+
hurdles.
165+
166+
> What related issues do you consider out of scope for this RFC that could be
167+
> addressed in the future independently of the solution that comes out of this RFC?
168+
169+
I consider the question whether or not scanning utilities should be added to the
170+
standard library to be out of the scope of this RFC.
171+
172+
# Future possibilities
173+
[future-possibilities]: #future-possibilities
174+
175+
Once this RFC is implemented the Chapter 2 of the Rust book could be simplified
176+
to introduce mutability and borrowing in a more gentle manner. Clippy could gain
177+
a lint to tell users to avoid unnecessary allocations due to repeated
178+
`inputln()` calls and suggest `std::io::Stdin::read_line` instead.
179+
180+
With this addition Rust might lend itself more towards being the first
181+
programming language for students.

0 commit comments

Comments
 (0)