4 ms·
Isn’t a Rust “move” implemented as a bit wise copy (e.g. memcpy call)? I see people claiming move has no cost but I’m not sure that is true.
by indemnity 6y ago
Isn’t a Rust “move” implemented as a bit wise copy (e.g. memcpy call)? I see people claiming move has no cost but I’m not sure that is true.
- justinpombrio 6y agoEDIT: See kevincox's reply. Rust will bitwise copy the containing type, which is typically very cheap. For example, it you move a String, it will copy the String struct, which contains a couple pointers and a length (or something along those lines). Importantly, it will not copy the underlying char array. I was thinking of the following code, where I believe the assignment to y is actually free. Though apparently this isn't called a "move". let x = <<large owned type like [char; 1000]>>; let y = x; More info: https://doc.rust-lang.org/rust-by-example/scope/move.html https://doc.rust-lang.org/rust-by-example/scope/move.html
- kevincox 6y agoSemantically it is a bitwise copy. In the example from the article it is probably actually a copy because the value was originally on the parent thread's stack, which will be reused after the function returns, so the value will need to be copied to the new thread's stack. However it is important to not that it isn't a deep/recursive bitwise copy. It just needs to copy the HashMap itself (which is probably a handful of words). So yes, it is doing a bitwise copy, but this is also very cheap. It will be much, much cheaper than spawning the thread.
- ahupp 6y agoThat's not exactly true. In C++ terms, the example code is moving a value: std::map<...> foo; someFunction(std::move(foo)); And not moving a pointer like: std::unique_ptr<std::map<...>> foo = ...; someFunction(std::move(foo)); So it copies sizeof(std::map<...>), not a pointer.
- pjmlp 6y agoNot necessary, std::map::map(&&) is free to move just the internal pointers and bookkeeping data, only dumb implementations would move the memory blocks where the map data is actually stored, as per ISO C++ requirements.
- ahupp 6y agoThe comment originally said it was the cost of a pointer copy, and I was pointing out it's not a pointer. eg., sizeof(std::map<std::string,std::string>) is 48, which is the "pointers and bookeeping" you mention.
- pjmlp 6y agoAh sorry for the misinterpretation. Should have payed more attention.
- steveklabnik 6y agoSemantically, it is a bit wise copy, yes. However, these copies can often be elided by optimizations.
- dathinab 6y agoWhat is bit-wise copied is the pointer to the memory. I.e. a `HashMap` struct, or `Vec` struct don't directly contain the data. For example the `Vec` is defined internally as something similar to: `struct Vec<T> { data: *mut [T], capacity: usize, len: usize, marker: PhantomData<T> }` (Slightly simplified, not actual Vec type). So a move of a Vec copies at most 3 usize (24 bytes on 64bit systems), similar thinks apply for a HashMap. Additionally the copy can often be elided through compiler optimizations. As a interesting side note a new empty Vec/HashMap will not actually allocate any memory, only once elements get added it will start doing so. This is why it crates vec's of vecs of length 1. Or else it wouldn't need to do "number of element" free calls.
- tsimionescu 6y agoSo the Vec type is not storing data on the stack, like C++'s std::vector can?
- cesarb 6y agoExactly, neither Rust's Vec nor Rust's String (which is actually a Vec in disguise) store data within the struct itself, it's always stored in an external allocation. This is intentional, to avoid extra branches on whether the data is inline or not. If you have a lot of small vectors, and using less memory is more important for your use case than avoiding the extra branches, there are alternatives like https://crates.io/crates/smallvec https://crates.io/crates/smallvec that you can use.