It's actually not that painful.
C++ has a bit of a different safety model that somewhat relies on move constructors to work. Rust builds this in, with some crucial changes.
For one, all C++ values are forced to have a zero state; e.g. when you move out of a unique_ptr (in certain ways, not all of them) the old one must be zeroed, because the destructor will still be run on it. Use after move leads to unspecified but not necessarily undefined. Rust OTOH does not have null as a valid value for its pointers. In case of conditional moves Rust will use an extra bit on the stack called a "drop flag" to track whether or not the destructor needs to be run, but only when needed, and usually it isn't so this complexity is abstracted away and also doesn't impact the runtime for the majority of times it isn't necessary.
99% of the move constructors in C++ are focused on making this work correctly. It's not necessary in Rust.
There are the 1% that do stuff like the small string optimization. Yes, you can't do this as neatly in Rust. But it's worth noting that move constructors have a significant cost, both in runtime (things like vectors need to deal with this correctly -- in particular vector<smart_object<_>> can't memcpy on resize anymore), and in compile time -- if you want to build good generic abstractions you have to guard against the fact that moves (and copies) can do anything. This would completely destroy Rust's ability to easily compartmentalize safety because now you have to worry about arbitrary code running every other line of your generic code. In C++ you don't care about this as much; since compartmentalizing safety is more of a best-effort thing and blame is harder to assign, whereas in Rust your generic datastructure must be safe when you throw any (safely implemented) type at it.
So yeah, sounds painful, but not really, and from a Rust POV what C++ has in this space is really painful.