This is one case where Rust benefited from C++’s experience — move by default with opt-in clone/copy is IMO the better setup.
And the most important idea: destructive moves. Since C++ doesn't track lifetimes it has to leave the object in a "valid state" after a move and the destructor still runs which has to have a check if it should do something or not.
It did for sure, but the problem with C++ is its heritage, specifically that structures can be self-referential. For instance, the Rust's url::Url type has to use usize offsets for tracking the location of each of its components. Conversely, in C++, someone could have already created a similar Url type that would use std::string for the buffer and char pointers for the component locations. As such, you cannot simply memcpy from one struct into another and forget the former as std::string could have its own in-place storage and that would invalidate all pointers - you'll need to define a move constructor instead.
This is an easy mistake to make but it's not what happened
Programmers already knew (~20 years ago) when the C++ move feature was designed that what people want is the destructive move assignment semantic, the thing Rust has today. Other languages did have that. But C++ 98 already existed and WG21 already did not want to make it difficult to take your crusty 10+ year old C++ codebase, slap a sticker on it and say this is "Modern C++"
The C++ "move" proposal is slightly sneaky, it admits that what they're proposing is not the destructive move (again, people know they want this) but it gives the impression that if they really want destructive move they can add it later, without revealing what's really going on underneath.
In fact C++ move is roughly what Rust would call core::mem::take, we move something in the usual way (a destructive move) but then we replace it with some value of the same type, in the case of core::mem::take it's Default::default()
To enable this, C++ is full of types which look superficially familiar to a Rust programmer but have a weird "empty" state to provide that default value where none would make sense. For example std::unique_ptr<T> looks like it's Box<T> but it's not, it's actually Option<Box<T>>, even newer types often do this but they might be more embarrassed about it.
[Edited to clarify timeline]