5 ms·
In this video[0] on Rust, in this example I was unable to directly tell whether the Vec structure is being copied to the take() function. If ownership is being
by fredmorcos 11y ago
In this video[0] on Rust, in this example I was unable to directly tell whether the Vec structure is being copied to the take() function. If ownership is being handed over, why is a copy being made? And if a copy really isn't being made, then why aren't the function signature and the calling site reflecting that?
I hope I am making any sense... Please tell me if I should try to rephrase, or if I am in need of clarification on the subject.
(BTW, this is also something I really dislike about C++, two function calls that look exactly the same, foo(x) and bar(x), could either be pass by value or pass by reference: you have to go dig up the function signatures to figure that out).
[0] https://youtu.be/O5vzLKg7y-k?t=1110 https://youtu.be/O5vzLKg7y-k?t=1110
- steveklabnik 11y agoGotcha. 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.
- nickpsecurity 11y agoIt was a great detailed explanation that also illustrates why I've held off on Round 2 of reviewing the docs. I mean, I read this... "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.
- steveklabnik 11y agoThis concept isn't Rust specific, it's a general PLT thing. Rust is the same as C in this regard. Almost every language is pass by value these days. IIRC Fortran is pass by reference, an exception.
- nickpsecurity 11y agoThen just say it's pass by reference instead of stuff I quoted. Counter anyone else doing the same in your usual, gentle style reminding how unnecessary confusion hurts adoption. Ill get back on helping find more of this stuff in the docs once im done moving. Btw, I tried to get on your draft but link didnt work by time I got back to it. Was it merged into official docs or where so I read/review the right thing?
- steveklabnik 11y agoI still failed, it's pass by value :( just like C. http://github.com/rust-lang/book http://github.com/rust-lang/book is the repo, and has a link to the rendered version.