5 ms·
C++ can never change the way move vs copy semantics work, which is precisely the opposite of Rust’s. It’s the basis for rust’s ownership model, and it’s no smal
by ninkendo 1y ago
C++ can never change the way move vs copy semantics work, which is precisely the opposite of Rust’s. It’s the basis for rust’s ownership model, and it’s no small difference. Without this I don’t see how C++ can ever get a workable ownership model like Rust’s.
- fooker 1y agoC++ just gives you the option to have it both ways. You are free to design your library in a way that your users only see one of these.
- tialaramex 1y agoWhat developers actually wanted, even years back when C++ didn't have anything named "move" was the destructive move semantic. It's an elegant, easy to understand feature for a language. I like it very much in Rust which has it. C++ didn't get that. The proposal paper at the time says it's impossible (for C++). But what they did propose was the feature you've seen in C++ today, which they call "move", but it has slightly odd (though convenient to implement, Worse Is Better after all) behaviour. Now you can make this C++ "move" behaviour out of destructive move, that behaviour is roughly what Rust calls std::mem::take and it's sometimes useful, which is why that function is provided. But, often it's not really what you wanted, and if you actually wanted destructive move but only have this C++ imposter then you need to perform the entire take, then throw away the newly created object. You will find lots of C++ code doing exactly that. So, no, you can't "design your library" to deliver the desirable property in C++. It's just another of the dozens of nagging pains because of design mistakes C++ won't fix.
- fooker 1y agoI am still confused about what you are claiming to be not possible in C++ when you are designing your library. Yes, I understand that there’s a lot of bad code out there and C++ happily enables that. But that was not my point.
- tialaramex 1y agoMaybe it will be clearer if you read and understand my favourite function from Rust's standard library (its full name is core::mem::drop) pub fn drop<T>(_x: T) {}
- fooker 1y agoGreat. template <typename T> void drop(std::unique_ptr<T> &&) {} Also, you don’t really need this because of RAII. You can make it simpler, and here’s how you’d do this in C++26. template <std::movable T> void drop(T &&) {} It can get even simpler! void drop(std::movable auto){} Do you see my point about C++ incorporating the good ideas at a glacial pace?
- aw1621107 1y ago> template <typename T> void drop(std::unique_ptr<T> &&) {} So what happens for objects that aren't behind a unique_ptr? > Also, you don’t really need this because of RAII. drop() indeed isn't used much because of RAII, but it is handy for those instances where you do actually want to dispose of something "early".
- fooker 1y agoSee the other versions I added. > dispose of something "early". This is a bit of an anti pattern in C++, but doable of course.
- ninkendo 1y agoIt’s not just for disposing things, it’s also used to decompose things into their constituent parts… like if I had something representing an HTTP response and it contains headers and a body, I could write a `fn into_parts(self) -> (Headers, Body)` that returns the parts you care about while destroying the response object. This is useful in the grpc library I use, which has a response type with some metadata about the response, and an “inner” value that represents the thing the grpc method returned. If you just want the inner thing and don’t care about the metadata, there’s a `fn into_inner(self)` that destroys the response and leaves you the inner value. You can write similar methods in C++ but they require leaving the original value in an “empty” state so that you can avoid expensive copying of things while still letting the moved-from value be “safe” to use. But that affects your API design… you now have to make the methods that fetch the “inner” thing be nullable/optional so that you can represent “oh this was moved from so there’s no body any more”. You don’t have to do that in Rust: moves are just a memcpy and the compiler will reject programs that use the moved-from value.