It is true that choosing to make this recoverable is a big part of it; in Rust we decided to make standard library data structures treat allocation as unrecoverable, and this does simplify the API of using them significantly. However, I would argue that the complexity in C++ comes more from using exceptions for this purpose than from that choice. Rust’s panics being used for unrecoverable errors only helps simplify the overall model. In other words, the ease of use goes unrecoverable > recoverable through result > recoverable through exceptions, and the model complexity comes from the choice of using exceptions over return values. We do have a plan for introducing a way to make the data structures recoverable, but it’s waiting on several things.
This is, of course, only my opinion.
a) wrap underlying errors into something like VecNewError, which is tedious due to the prevalence of errors if every construction/allocation can fail, kindly similar to the CheckedException dilemma
b) type erasing it by Box it or dyn Error, and use ? operator to transparently pass errors to upper level, in which case the user need to downcast with match to extract information needed, and exception is essentially the syntax sugar for this case because exception acts like a ? operator after all failable function calls
a) is usually used in a language where errors are rare, and b) is preferred when the errors are common.
Vec::try_reserve, on the other hand, simply uses a common `CollectionAllocError`, which is not really (a) or (b).
The most useful aspect of having customisable move constructors like in C++ seems to be things like updating pointers in self-referential types, which hopefully can be written to never throw.
The reason C++'s model is so much more complicated is because of backwards compatibility and nondestructive moves, not as a mechanism to support recoverable allocation failure.