|
Gotcha. I thought the video would have explained this, but since it didn't get through, let me try :) fn take(vec: Vec<i32>)
In Rust, the default way that things operate is to _move_. So when you call take(), we'd say that the vector moves into the function.Moving is always a memcpy. There's no way to change this behavior, so you can always know that it's true. But with a Vec, there's a subtlety: the Vec itself is three words: a pointer to the heap, a length, and a capacity. So when I say that the Vec is memcpy'd, I mean literally those three things. It doesn't try to follow the pointer and copy the data that it points to. Incidentally, that pointer is why we say that it moves: because two copies of the Vec structure itself would mean an aliased pointer to the heap, the compiler doesn't allow the use of the older binding after take() is called. For simpler types, like integers: fn take(i: i32)
they implement a trait called Copy. This means that when you call take, the i32 is copied, in the same fashion that the Vec was copied. The only difference is that there's no problem with having these two copies, as an i32 is, well, just an i32. So the compiler won't prevent the use of the binding outside the function like it would with a non-Copy type.References are like pointers, but with the extra static checking that Rust does. fn take(vec: &Vec<i32>)
References also implement Copy, and so when you pass a reference to a Vec to this function, the same thing happens as in the i32 case. The reference itself gets copied.This is sort of a long-winded way of saying that Rust is "pass by value", not "pass by reference." Some people like to call this "pass reference by value", since while it's always pass by value, references are themselves values. What if we _did_ want to make a full copy of the Vec, including its elements? For that, we have to use the .clone() method. let v = ... // some Vec<i32>
let v1 = v.clone();
Now, v and v1 will be full, independent copies of the same thing. In other words, if you don't see .clone(), it will never be a deep copy. let v = ... // some Vec<i32>
let v1 = v; // a move, always a shallow memcpy
let v2 = v1.clone(); // a deep copy
You can always see, syntactically, when you're making a possibly expensive copy of something.Does that help? I'm happy to elaborate further. |
"This is sort of a long-winded way of saying that Rust is "pass by value", not "pass by reference." Some people like to call this "pass reference by value", since while it's always pass by value, references are themselves values."
...and instantly have to focus hard to make sure I'm not slipping on the basic concepts. The wording of many Rust descriptions seems unnecessarily confusing to the point that I just googled value vs reference semantics to make sure my memory wasn't screwed up [more]. In a normal 3GL, pass by reference basically stores a pointer in a variable and passes that value somewhere. That value/reference can be used to modify original data outside its original function. Is that what Rust does? If so, it's just pass by reference "with (caveats/rules here)." End of story. Otherwise, I'll see if I can guess a wording that doesn't merge opposite concepts.