C++'s equivalent of
foo is now reachable but interacting with it is UB. Importantly, this does not call the deleting dtor, and
delete foo is now UB. Since C++ does not have a meaningful version of
forget, such an operation is in general UB, but not for the reason you think:
A* p = &a;
p->~A(); // First call to ~A().
} // Second, implicit call to ~A(). This UB.
Equivalent Rust, which is also UB, is as follows:
let mut a: A = ...;
ptr::drop_in_place(&mut a); // First call to A::drop().
} // Second, implicit call to A::drop(). This is UB.
This Rust code can be made correct by adding
mem::forget(a); at the end.
There is also the fact that C++ does not have an equivalent of
[T] that a destructor can be called on. The closest analogue,
std::span, is trivially destructible. What Rust is doing is not equivalent to
ptr::drop_in_place::<[T]> can be thought of as just iterating over the elements and calling
drop_in_place on each; a raw
[T] owns its values but not its allocated space (just like any other non-reference type). Since
Vec never actually holds references to the underlying memory, and only creates them dynamically after checking that the pointed-to memory is valid, there is no UB. This is the exact same process by which
std::vector<T> is implemented in all major STLs, except C++ can take advantage of
operator new to make the code a bit more straightforward.