Move Semantics
These exercises are adapted from pnkfelix’s Rust Tutorial – Thank you Felix!!!
Further information
For this section, the book links are especially important.
Rustlings
借用
move_semantics1
// move_semantics1.rs // Execute `rustlings hint move_semantics1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { let vec0 = Vec::new(); let vec1 = fill_vec(vec0); // let vec1 = fill_vec(&vec0); // vec0.push(24); // Try accessing `vec0` after having called `fill_vec()`. See what happens! println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); vec1.push(88); println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); } fn fill_vec(vec: Vec<i32>) -> Vec<i32> { let mut vec = vec; vec.push(22); vec.push(44); vec.push(66); vec }
Hint
So you’ve got the “cannot borrow immutable local variable vec1 as mutable” error on line 13,
right? The fix for this is going to be adding one keyword, and the addition is NOT on line 13
where the error is.
Also: Try accessing vec0 after having called fill_vec(). See what happens!
可变借用
move_semantics2
// move_semantics2.rs // Make me compile without changing line 13 or moving line 10! // Execute `rustlings hint move_semantics2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { let vec0 = Vec::new(); let mut vec1 = fill_vec(vec0); // Do not change the following line! println!("{} has length {} content `{:?}`", "vec0", vec0.len(), vec0); vec1.push(88); println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); } fn fill_vec(vec: Vec<i32>) -> Vec<i32> { let mut vec = vec; vec.push(22); vec.push(44); vec.push(66); vec }
Hint
So, vec0 is passed into the fill_vec function as an argument. In Rust,
when an argument is passed to a function and it’s not explicitly returned,
you can’t use the original variable anymore. We call this “moving” a variable.
Variables that are moved into a function (or block scope) and aren’t explicitly
returned get “dropped” at the end of that function. This is also what happens here.
There’s a few ways to fix this, try them all if you want:
- Make another, separate version of the data that’s in
vec0and pass that tofill_vecinstead. - Make
fill_vecborrow its argument instead of taking ownership of it, and then copy the data within the function in order to return an ownedVec<i32> - Make
fill_vecmutably borrow a reference to its argument (which will need to be mutable), modify it directly, then not return anything. Then you can get rid ofvec1entirely – note that this will change what gets printed by the firstprintln!
move_semantics3
// move_semantics3.rs // Make me compile without adding new lines-- just changing existing lines! // (no lines with multiple semicolons necessary!) // Execute `rustlings hint move_semantics3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { let vec0 = Vec::new(); let mut vec1 = fill_vec(vec0); println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); vec1.push(88); println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); } fn fill_vec(vec: Vec<i32>) -> Vec<i32> { vec.push(22); vec.push(44); vec.push(66); vec }
Hint
The difference between this one and the previous ones is that the first line
of fn fill_vec that had let mut vec = vec; is no longer there. You can,
instead of adding that line back, add mut in one place that will change
an existing binding to be a mutable binding instead of an immutable one :)
move_semantics4
// move_semantics4.rs // Refactor this code so that instead of passing `vec0` into the `fill_vec` function, // the Vector gets created in the function itself and passed back to the main // function. // Execute `rustlings hint move_semantics4` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { let vec0 = Vec::new(); let mut vec1 = fill_vec(vec0); println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); vec1.push(88); println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1); } // `fill_vec()` no longer takes `vec: Vec<i32>` as argument fn fill_vec() -> Vec<i32> { let mut vec = vec; vec.push(22); vec.push(44); vec.push(66); vec }
Hint
Stop reading whenever you feel like you have enough direction :) Or try doing one step and then fixing the compiler errors that result! So the end goal is to:
- get rid of the first line in main that creates the new vector
- so then
vec0doesn’t exist, so we can’t pass it tofill_vec - we don’t want to pass anything to
fill_vec, so its signature should reflect that it does not take any arguments - since we’re not creating a new vec in
mainanymore, we need to create a new vec infill_vec, similarly to the way we did inmain
move_semantics5
// move_semantics5.rs // Make me compile only by reordering the lines in `main()`, but without // adding, changing or removing any of them. // Execute `rustlings hint move_semantics5` or use the `hint` watch subcommand for a hint. // I AM NOT DONE fn main() { let mut x = 100; let y = &mut x; let z = &mut x; *y += 100; *z += 1000; assert_eq!(x, 1200); }
Hint
Carefully reason about the range in which each mutable reference is in scope. Does it help to update the value of referent (x) immediately after the mutable reference is taken? Read more about ‘Mutable References’:
References and Borrowing > Mutable References - The Rust Programming Language
move_semantics6
// move_semantics6.rs // Execute `rustlings hint move_semantics6` or use the `hint` watch subcommand for a hint. // You can't change anything except adding or removing references. // I AM NOT DONE fn main() { let data = "Rust is great!".to_string(); get_char(data); string_uppercase(&data); } // Should not take ownership fn get_char(data: String) -> char { data.chars().last().unwrap() } // Should take ownership fn string_uppercase(mut data: &String) { data = &data.to_uppercase(); println!("{}", data); }
Hint
To find the answer, you can consult the book section “References and Borrowing”
-
The first problem is that
get_charis taking ownership of the string. Sodatais moved and can’t be used forstring_uppercasedatais moved toget_charfirst, meaning thatstring_uppercasecannot manipulate the data. Once you’ve fixed that,string_uppercase’s function signature will also need to be adjusted. Can you figure out how? -
Another hint: it has to do with the
&character.