There's a type Foo that takes a self parameter to do something, like this:
struct Foo {}
impl Drop for Foo {
fn drop(&mut self) {
todo!()
}
}
impl Foo {
fn consume_and_regenerate(self) -> Foo {
Foo {} // it is complicated in the real case
}
}
}
And a Bar type designed by me that hold a Foo:
struct Bar {
foo: Foo,
}
impl Bar {
fn update_foo(&mut self) {
self.foo = self.foo.consume_and_regenerate();
}
}
The code will not compile, because I can own the foo from a &mut foo, and here's my solution:
fn update_foo(&mut self) {
let foo = replace(&mut self.foo, unsafe { MaybeUninit::uninit().assume_init() });
let foo = foo.consume_and_regenerate();
let foo = replace(&mut self.foo, foo);
std::mem::forget(foo);
}
Is there a better (safe) way to do this?
P.S. I don't use an foo: Option<Foo> because there's no case that the option can be none, using an Option<Foo> makes other codes bloated。
Your solution may runs Foo's destructor with dangling reference when consume_and_regenerate panic and unwind. take_mut crate provides safe workaround by immediately kill the process on such unwinding.
panic=abort can be useful to reduce code size when that is a critical factor (because then the compiler doesn't need to generate code to clean up during unwinding), and there are some platforms (embedded and Wasm) where panic=unwind is not supported.
Regarding supporting panic=abort:
If you are writing a library for others to use, then you should, if at all possible, make your code panic=abort compatible by:
Not panicking (use Result etc. instead) if there is any chance that the error condition is something the caller wants to recover from and it isn't evidence of a bug in your library's code.
Not using catch_unwind or relying on thread JoinHandles being able to catch panics.
On panic (or to be more precise, unwinding) of the closure f, default will be called to provide a > replacement value. default should not panic – doing so will constitute a double panic and will most likely abort the process.
Afaik. rust doesn't guarantee that panicing while unwinding aborts, so the following example has double free.
let mut string = String::new();
replace_with(
&mut string,
|| panic!(), // if this panic doesn't cause abort `string` will be freed twice
|s| {
drop(s); // `s´ dropped here
panic!()
}
);