Rustlings: move problem

Hello

I have to solve this problem but have no clue how. Can someone give me some pointers

// move_semantics2.rs
// Make me compile without changing line 13!
// Execute `rustlings hint move_semantics2` for hints :)

// I AM NOT DONE

//  Make another, separate version of the data that's in `vec0` and pass that
//   to `fill_vec` instead.

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
}

The first step to figuring out a compile error is closely reading the error message and trying to understand what it says. What error do you get, and what do you think it means?

You will find the solution when you read the chapter 4.1 and 4.2 in the book and understand the difference between moving and borrowing a value.

nope, I read and reread that chapter and I thought this was the solution

fn main() {
    let vec0 = Vec::new();

    let mut vec1 = fill_vec(vec0).clone();

    // 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
}
! Compiling of exercises/move_semantics/move_semantics2.rs failed! Please try again. Here's the output:
error[E0382]: borrow of moved value: `vec0`
  --> exercises/move_semantics/move_semantics2.rs:16:57
   |
11 |     let vec0 = Vec::new();
   |         ---- move occurs because `vec0` has type `std::vec::Vec<i32>`, which does not implement the `Copy` trait
12 | 
13 |     let mut vec1 = fill_vec(vec0).clone();
   |                             ---- value moved here
...
16 |     println!("{} has length {} content `{:?}`", "vec0", vec0.len(), vec0);
   |                                                         ^^^^ value borrowed here after move

error: aborting due to previous error

I'd suggest to think, where do the vector returned from fill_vec going afterwards. You're cloning it and storing the clone, but the return value itself isn't stored anywhere. So why do you clone it?

1 Like

because I got this hint : // Make another, separate version of the data that's invec0and pass that // tofill_vecinstead.

So I thought after reading the chapter I need to use clone. but it seems I still do not get this confusing topic. Which return value are we now talking about.

Your thought to use clone is correct, but you’re cloning the wrong value. The compiler is complaining about vec0 and your code right now is roughly the same as this:


fn main() {
    let vec0 = Vec::new();

    let tmp = fill_vec(vec0);
    let mut vec1 = tmp.clone();

    // 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);
}

Breaking apart expressions and giving names to the intermediate results like this can sometimes help clarify what’s going on. If nothing else, when the compiler complains it’ll be pointing more directly at the problem.

hmm, when I do let vec0 = Vec::new().clone gives the same error

Because of the ownership rules, fill_vec destroys its argument; you need to give it something other than vec0 so that it still exists for println! to use.

now im complete confused. I think I understand the problem but still do not see the answer.
When I give it something else then vec0 then I think I have also to change the println and that is not allowed.

or do I have to use borrow somehow?

Why do you think so? If you give some other vector to fill_vec, vec_0 will still be here to print.

hmm

this looks like a wierd way to solve it

let vec0 = Vec::new();
    let vec2 = Vec::new(); 

    let mut vec1 = fill_vec(vec2);

and it does not work :

 Compiling of exercises/move_semantics/move_semantics2.rs failed! Please try again. Here's the output:
error[E0282]: type annotations needed for `std::vec::Vec<T>`
  --> exercises/move_semantics/move_semantics2.rs:11:16
   |
11 |     let vec0 = Vec::new();
   |         ----   ^^^^^^^^ cannot infer type for type parameter `T`
   |         |
   |         consider giving `vec0` the explicit type `std::vec::Vec<T>`, where the type parameter `T` is specified

error: aborting due to previous error

Well, now you've run into another problem: you create vec0, but it has no connection to vec1 at all, even at type level. Initially, they were connected through the fill_vec signature, which forced input and output to both be Vec<i32>, but now you've removed this constraint - you connect vec1 to vec2 instead.
This means that you need some connection between vec0 and vec2, so that vec0 is connected to vec1 transitively.
What will this connection between vec0 and vec2 be, logically?

1 Like

oke, maybe here i can use the clone so vec2 = vec0.Clone();

Yes, this compiles :

let vec0 = Vec::new();
    let vec2 =  vec0.clone();  

    let mut vec1 = fill_vec(vec2);

Yes, that's what we're trying to lead you to.

In fact, this whole problem boils down to the following:

  • You have some item (in this case, vector).
  • You need to pass this item to the function which takes ownership of its argument.
  • You need to use this item afterwards.

The usual solution in all such cases is to clone the item you have and to pass this clone into the function.

oke, and that cannot be done with borrowing. I try now to figure out when to use cloning and when to borrow something

In general, borrowing should be used in three cases.

First is when some value should be passed in and then out. For example, your fill_vec function could be written as such:

fn fill_vec(vec: &mut Vec<i32>) {
    vec.push(22);
    vec.push(44);
    vec.push(66);
}

From the low-level point of view, this is equivalent to the original function, the difference is only with ownership semantics.

Second is when some value can't be cloned, but should be read from multiple places. In this case, you pass references to it around, while storing the value itself somewhere in the parent function (for example, in the Box).

And the third case is when you find out that your code is too slow and want to optimize it. For example, large vector may be, while clonable, fairly costly to clone on every action. In this case, you can borrow the vector and pass around this borrow - which, thanks to so-called "deref coercions", will be transparently converted into reference to slice whenever it is necessary.

I know you have solved the problem now, but I thought I'd add my comments.

If you want to move a value into a function, and also use it in the current function, then the easiest choice is to create a copy of the object using the clone function. This is done automatically for you for types that are Copy, but for types that are only Clone you have to call clone() explicitally: let mut vec1 = fill_vec(vec0.clone());.

In the vast majority of cases if you want to pass a value to a function and then use it later, you want to use borrowing -> passing some kind of reference to the function. When the function returns, the borrow ends and you can continue using the value. This would look like fill_vec(&mut vec0) if the fill_vec function took a &mut Vec<i32>.

There are occasions however where you need to retain ownership in multiple places. An example might be the configuration for a more complex app, where different parts of the app need to have access to the config, and it's not possible to keep track of borrowing. In this case you can use the multiple owner types (Rc for single thread and Arc for multiple threads). These types keep track of the number of owners and drop the object once all owners no longer exist. To do this safely, they prevent mutation of the value - otherwise the value of the object might change unexpectedly and unpredictably. If you need mutation, you have to use some more types that track mutation at runtime (Cell and RefCell for single-threaded, and Mutex for multithreaded).