Panicking, unsafe, and you
In Jon Gjengset’s Demystifying Unsafe Code talk at Rust NYC, he gives a very interesting example of unsafe code. Here’s the link– please go and watch it– but I’ve transcribed it here along with my paraphrased explanation.
impl<T> Vec<T> {
/// apply `f` to every element of `us`, and extend `self`
/// with the result. for example:
///
/// names.extend_map([user1, user2], |u| u.name())
///
fn extend_map<U, F>(&mut self, us: &[U], mut f: F)
where F: FnMut(&U) -> T {
// reserve capacity in the Vec all at once, for perf (?)
self.reserve(us.len());
// set the length() manually.
let cur_len = self.len();
unsafe { self.set_len(cur_len + us.len()) };
// insert the items by writing to the memory location
let into = unsafe { self.as_mut_ptr().add(cur_len) };
for u in us {
unsafe { std::ptr::write(into, f(u)) }; into += 1;
}
}
}
If f
panics, then Rust will unwind the stack and drop every item in Vec
before dropping the Vec
itself. However, since you’ve unsafely set its length
with set_len()
, it will try to call drop on array indices that you haven’t
written to yet! In other words, it will just call garbage memory. Apparently,
this is why it can be challenging to write Vec::drain()
implementations.