Read user input from stdin in Rust

Pascal Precht
InstructorPascal Precht
Share this video with your friends

Social Share Links

Send Tweet
Published 4 years ago
Updated 3 years ago

In this lesson we'll learn how to read user input from stdin, using Rust's std::io module and its Stdin object.

Instructor: [00:00] Reading user input from stdin can be done by importing the io module from Rust standard library. We then create an instance of stdin using the stdin() function. This comes with a method read_line. Read_line takes a mutable reference to a string buffer.

[00:20] Let's create a mutable variable name which is a string and we pass a reference to that to read_line. The reason read_line takes a mutable reference to a string buffer is because it will use this buffer to fill in the data that is entered by the user.

[00:43] After the user is done entering its data, we can output the data using println! We save the file and run the code. We'll notice that the compiler warns us about the fact that read_line returns something of type result, which can possibly be an error. It also tells us that the error should be handled.

[01:05] However, our program still functions. Let's type in our name, and we'll see the program outputs our name.

J. Matthew
J. Matthew
~ 4 years ago

I know I'm getting caught up in the technical details here, but I'm curious a) why the ampersand precedes the mut keyword, as opposed to mut &name, and b) why the keyword is necessary at all when passing the argument to read_line. We've already defined name as mutable, so shouldn't a reference to it be sufficient? But when I try read_line(&name), the compiler tells me:

note: expected mutable reference `&mut std::string::String`  
found reference `&std::string::String`

So I get the feeling I'm not quite grasping how exactly mut is working.

Pascal Precht
Pascal Prechtinstructor
~ 4 years ago

Hi Matt,

why the ampersand precedes the mut keyword, as opposed to mut &name As mentioned in your other comments, I'm no compiler expert, so this is just a guess:

The ampersand expresses a reference to something so &name is a reference to the data name in memory. Pretty much everything in rust is an expression like 1+1, some_variable + 1 etc. I believe that, having an expression like mut &name conveys very different semantics from what's the intention here. mut won't have a meaning but could maybe be a variable name and &name is, already, clearly an immutable reference to name.

I could imagine that going with &mut name makes the expression less ambiguous. But again, just a guess. Great question!

why the keyword is necessary at all when passing the argument to read_line. We've already defined name as mutable, so shouldn't a reference to it be sufficient?

As the compiler errors says, passing a &reference to an API that expects a &mut Type cannot work. This is because also reference are by default immutable.

let mut name = "Pascal"; // `name` is mutable
let r = &name; // `r` is immutable`

Now in the case of the code in this lesson it might not be as obvious because we're not creating a variable for the &mut name but instead pass it straight to the API. You could write it like this:

let mut name = String::new();
let r = &mut name;
io::stdin().read_line(r).unwrap();

Does that make sense?

J. Matthew
J. Matthew
~ 4 years ago

Does that make sense?

Thanks, after reading a bunch of articles and thinking about it more, I think I understand this now. We start by declaring name as mutable so that it can be changed by the later code. However, we immediately then pass a reference to name in the next line. As you said, references are immutable by default, and this one is pointing to the state of name at the time the reference was created, when it is an empty string. So that means that the reference is a pointer to an empty buffer on the heap. When the input modifies the string, it could (and in this case certainly would) create a new buffer on the heap to fit the new contents, which means the reference pointing to the old buffer is no longer valid, or at least useful. Therefore the reference itself must mutate, to point to the new buffer, in order for the original variable name to own the new buffer (i.e. the correct data). Yes? Or something like that; I'm still absorbing a lot of new information.

Pascal Precht
Pascal Prechtinstructor
~ 4 years ago

When the input modifies the string, it could (and in this case certainly would) create a new buffer on the heap to fit the new contents, which means the reference pointing to the old buffer is no longer valid, or at least useful. Therefore the reference itself must mutate, to point to the new buffer, in order for the original variable name to own the new buffer (i.e. the correct data). Yes? Or something like that; I'm still absorbing a lot of new information.

That pretty much nailed it.

Markdown supported.
Become a member to join the discussionEnroll Today