The compiler tells me wrong type, so I add vec.to_vec(), but why can't I simply do *vec ?
Then: help: consider changing this to be a mutable reference: &mut Vec<i32>
At this point I'm lost, I made vec0 mutable and suddenly it works.
But why does it need to be mutable?
why if it is immutable, I need to clone it inside fill_vec?
what would be the best solution in this case, if I want to keep it maintainable and at the same time performant? that also mean, I don't like to duplicate memory usage but I also don't like stuff being mutable.
Since inside of the function, vec.push(...) is called, which is a (&mut self)-method for Vec, there needs to be a vector that you can actually mutate. This can either be the original vector vec0 that's been moved into fill_vec, as would be the case if you keep the argument (vec: Vec<i32>) (but of course, this approach runs into problems when the main function tries to use vec0 another time later; or it could be the original vector vec0, which is still owned by the main function, and only borrowed by fill_vec; however it would need to be borrowed mutably, because the push call still needs mutable access. Or it could be an owned copy of the original vec0; this could be achieved in two ways: either you clone the Vec in the main function, and pass the clone, or you pass a reference (this time it can be a shared reference) and the first thing that fill_vec does is clone the vector.
Note that .to_vec on a &Vec<T> or a &mut Vec<T> will essentially just clone the whole vector. Cloning turns a borrowed &Vec<T> into a new owned copy (of type Vec<T>). (To explain why this method exists at all, it's actually a method on slices, so that you can turn &[T] into a Vec<T> by cloning all the elements.)
I'm not 100% certain what clone you have in mind, i.e. where in the code exactly would you clone? Assuming it's the same thing as I suggested above, i.e. the first thing that fill_vec does is cloning the vector, then it's necessary for the reason I've explained above: push needs mutable access to some vector, which means you need fill_vec to own one itself (and cloning an (immutably) borrowed vector is a way to obtain ownership (of the clone)), or to have mutable access to a vector that's still owned by the main function.
The "best" solution depends a bit on the actual use case; the code in this example is more of a toy example. In general, cloning the whole vector is kind-of expensive (though in this concrete case again, cloning an empty vector is really cheap); so in practical Rust code, you should clone vectors when you need multiple independent owned values that can me mutated independently and such; whereas when you don't need those, you can try to avoid cloning.
Trying to avoid the to_vec here is not too hard. Since the fill_vec(vec: &mut Vec<i32>) with a &mut ... argument makes it return value basically already accessible in the function argument (by mutating it), you don't really need to return a copy of the whole thing anymore. Hence, if you skip the -> Vec<i32> return value, you can skip the vec.to_vec() accordingly; you won't get a vec1 back then of course, so also skip the let mut vec1 = part of the call, and use vec0 throughout the remaining main function.
This last suggestion is in line with the last suggestion that rustlings hint move_semantics2 should be giving you:
So vec0 is being moved into the function fill_vec when we call it on line 10, which means it gets dropped at the end of fill_vec, which means we can't use vec0 again on line 13 (or anywhere else in main after the fill_vec call for that matter). We could fix this in a few ways, try them all!
Make another, separate version of the data that's in vec0 and pass that to fill_vec instead.
Make fill_vec borrow its argument instead of taking ownership of it, and then copy the data within the function in order to return an owned Vec<i32>
Make fill_vecmutably borrow its argument (which will need to be mutable), modify it directly, then not return anything. Then you can get rid of vec1 entirely -- note that this will change what gets printed by the first println!
As far as I can tell, reading these hints - even after solving the exercise without needing a hint - can be a good idea in general, because they can sometimes provide further references or extra tasks, as the one above that suggests "a few ways" and encourages you to "try them all".