In rust Vec::pop() has the following signature:
pub fn pop(&mut self) -> Option<T>;
In c++ the same operation has to be done with two methods:
reference back();
void pop_back();
When reading up on the subject I encountered multiple explanations the most compelling one to me seemed to be exception safety. The following answer talks about front()
and pop()
but the discussion should be identical for back()
and pop_back()
:
Consider this scenario (with a naive/made up pop implementation, to ilustrate my point):
template<class T> class queue { T* elements; std::size_t top_position; // stuff here T pop() { auto x = elements[top_position]; // TODO: call destructor for elements[top_position] here --top_position; // alter queue state here return x; // calls T(const T&) which may throw }
If the copy constructor of T throws on return, you have already altered the state of the queue (
top_position
in my naive implementation) and the element is removed from the queue (and not returned). For all intents and purposes (no matter how you catch the exception in client code) the element at the top of the queue is lost.This implementation is also inefficient in the case when you do not need the popped value (i.e. it creates a copy of the element that nobody will use).
This can be implemented safely and efficiently, with two separate operations (
void pop
andconst T& front()
).
So now my questions is the following. Does rusts std::vec::Vec::pop()
have this exception safety problem? I know that there isn't an exception mechanism in rust, but panics basically work the same way. But of course rust doesn't have a copy constructor concept. Vec::pop()
simply reads the pointer value
pub fn pop(&mut self) -> Option<T> {
if self.len == 0 {
None
} else {
unsafe {
self.len -= 1;
Some(ptr::read(self.as_ptr().add(self.len())))
}
}
}
So is it just that none of the operations in Vec::pop()
panic and therefore the function is exception safe while c++ cannot assume anything about the copy constructor and therefore has to assume that it may panic?