Hi All! Welcome to the Reading Club for Rust’s “The Book” (“The Rust Programming Language”). This is week 1 (the beginning!!).
Have a shot at going through “the reading” and post any thoughts, confusions or insights here
“The Reading”
-
Finish up to Chapter 2: “Programming a Guessing Game”
-
The Book: https://rust-book.cs.brown.edu/title-page.html (the special Brown University version with quizzes etc)
The Twitch Stream
- @sorrybookbroke@sh.itjust.works ran a twitch stream on these chapters which is now on YouTube: https://www.youtube.com/watch?v=ou2c5J6FmsM
- You might prefer watching and listening to that rather than reading the book.
- Be sure to catch future streams (will/should be weekly: https://www.twitch.tv/deerfromsmoke)
What’s Next Week?
- Chapters 3 and 4
- Start thinking about challenges or puzzles to try as we go in order to get some applied practice!
- EG, Advent of Code
- Maybe some basic/toy web apps such as a “todo”
I think this is an occasion where a vague familiarity with other languages ended up confusing me with Rust. The ‘&’ sign doesn’t mean ‘pass by reference’ in the same way as it does in C. Anything with a size that’s fixed at compile time is typically passed by value, whereas variables who’s size might change are passed by reference. The ‘&’ in Rust isn’t about that. For variables that are passed by reference, the ‘&’ is about whether the ownership of that memory address is transferred or not.
To illustrate:
fn abc(v: String) { println!("v is {}", v); } fn main() { let mut v=String::from("ab"); v.push('c'); abc(v); // println!("v is {}", v); }
works fine as it is, but will error if you uncomment the second println! The ‘v’ variable was passed by reference, but it’s ownership was transferred, so it can’t be referred to again.
Thanks!
Seems I gotta dig into the borrow checker before thinking too much about this!
Otherwise, compiling your code snippet is a nice illustration of how helpful the compiler tries to be … lots of tips in the output there! To anyone else, just try running
rustc
on this, with the secondprintln!
uncommented and see the output, which is half error half linting.Yea. So if the second
println!
were uncommented, how could we compile this? From what you’ve said, I’d guess that&
means “borrow” (ie, not “move” ownership).So if we alter
abc
to take a&String
type and notString
, and therefore only “borrow” the variable, and then pass in&v
and notv
to pass in a “borrowed” variable, it should compile.fn abc(v: &String) { println!("v is {}", v); } fn main() { let mut v=String::from("ab"); v.push('c'); abc(&v); println!("v is {}", v); }
It seems to!
Of course, as the compiler suggests, we could instead just pass in
v.clone()
which presumably creates a new variable and effectively “passes by value”.Digging in a bit more, what happens if
abc
(tries to) mutate the variable?We can add
v.push('X')
toabc
and see if we get different printouts. As the compiler would tell us, we would need to make the argumentv
mutable for this to work.fn abc(mut v: String) { v.push('X'); println!("v is {}", v); } fn main() { let mut v=String::from("ab"); v.push('c'); abc(v.clone()); println!("v is {}", v); } // OUTPUT: // v is abcX // v is abc
I’m not clear on why I don’t have to declare that the
v.clone()
is mutable in anyway though.What about trying the same with a "borrowed’ variable?
Well we need mutable borrowed variables, so necessary adjustments to the types of
abc
and its call inmain
. And adding an additional mutation ofv
inmain
afterabc
is called, and we get two different println outputs, with each mutation applying to the same variable.fn abc(v: &mut String) { v.push('X'); println!("v is {}", v); } fn main() { let mut v=String::from("ab"); v.push('c'); abc(&mut v); v.push('Y'); println!("v is {}", v); } // OUTPUT // v is abcX // v is abcXY
It’s covered in detail in chapter 4.