I have a type that implements writing data in a particular format to a contained impl std::io::Write
:
// bounds on struct required due to Drop, see below
pub struct MyWriter<W: Write + Seek> { inner: W }
impl<W: Write + Seek> MyWriter<W> {
pub fn write_foo(&mut self, foo: Foo) -> Result<(), WriterError> {
// write some bytes to `self.inner` based on `foo`
}
}
At the end of a MyWriter
's lifetime, it needs to "fix up" the data it's written by seeking backward and overwriting a previous field; let's say this is implemented by a method fixup
:
impl<W: Write + Seek> MyWriter<W> {
fn fixup(&mut self) -> Result<(), WriterError> {
// ...
}
}
One approach to making sure this fixup happens in (almost) all circumstances is to put it in a Drop
impl, but that precludes doing any real handling of errors from fixup
: either we panic in drop
on an error, which will likely cause aborts during unwinding, or we ignore any errors, which seems bad. So I'd like to provide a way to perform the fixup explicitly that doesn't hide the error, like this:
impl<W: Write + Seek> MyWriter<W> {
pub fn finish(self) -> Result<(), WriterError> {
self.fixup()?;
// TODO what now?
}
}
The idea is that callers can use finish
to get access to any errors that occur during fixup
, but if they forget to do this or don't want to then the Drop
impl will still take care of it (by panicking or ignoring any error).
So here's my question: how should I complete finish
? It needs to skip the Drop
impl for MyWriter
(because we should only fixup
once), while still running the destructors for the object's fields—in particular inner
, which is probably a File
or something. I thought I could use std::mem::forget
for this, but apparently that won't run the fields' destructors.