Skip to content

Commit 1309352

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

File tree

1 file changed

+180
-0
lines changed

1 file changed

+180
-0
lines changed

text/3196-inputln.md

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

0 commit comments

Comments
 (0)