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.
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?
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.
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.
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 tomut &name
, and b) why the keyword is necessary at all when passing the argument toread_line
. We've already definedname
as mutable, so shouldn't a reference to it be sufficient? But when I tryread_line(&name)
, the compiler tells me:So I get the feeling I'm not quite grasping how exactly
mut
is working.