No, there is no way, because it is unsafe to do so. Here's an example that demonstrates why (requires a nightly compiler).
#![feature(fn_traits)]
#![feature(unboxed_closures)]
use std::mem;
trait Actable {
fn act(&mut self);
}
struct SelfCaller {
message: String,
callback: Box<FnMut(&mut SelfCaller)>,
}
impl Actable for SelfCaller {
fn act(&mut self) {
let mut callback: &mut Box<FnMut(&mut SelfCaller)> = unsafe { mem::transmute(&mut self.callback) };
println!("calling callback");
callback(self);
println!("called callback");
}
}
struct Callback;
impl Drop for Callback {
fn drop(&mut self) {
println!("Callback dropped!");
}
}
impl<'a> FnOnce<(&'a mut SelfCaller,)> for Callback {
type Output = ();
extern "rust-call" fn call_once(mut self, args: (&mut SelfCaller,)) {
self.call_mut(args)
}
}
impl<'a> FnMut<(&'a mut SelfCaller,)> for Callback {
extern "rust-call" fn call_mut(&mut self, (messenger,): (&mut SelfCaller,)) {
println!("changing callback");
messenger.callback = Box::new(|messenger| {});
println!("changed callback");
messenger.message = "replaced message".to_owned();
}
}
fn main() {
let change = Callback;
let mut messenger = SelfCaller {
message: "initial message".to_owned(),
callback: Box::new(change),
};
messenger.act();
println!("{}", &messenger.message);
}
The output of this program is:
calling callback
changing callback
Callback dropped!
changed callback
called callback
replaced message
OK, so what's going on? First, I've written the implementation of act
for SelfCaller
in such a way that I can call the callback without mem::replace
, using mem::transmute
to get the compiler to generate a new lifetime disconnected from self
.
Then, I've written a callback (using the struct Callback
, since I needed a type that implements both FnMut
and Drop
to demonstrate the problem) that mutates the SelfCaller
by changing its callback
member. This has the effect of dropping the previous callback, which is the callback that is currently executing! If Callback
contained data members, attempting to read them would cause undefined behavior, since they are now in deallocated memory (we dropped the whole Box
).
By the way, in your code using mem::replace
, callbacks cannot change the callback, since you restore the callback after the callback call ends.