回调可变自身 [英] Callback to mutable self
问题描述
有没有一种方法(生锈)可以在没有 mem::replace
hack 的情况下将可变借用的 self 发送到回调我在以下 MWE 中使用?我正在使用 Rust stable (1.11.0).
Is there a way (in rust) to send a mutable borrowed self to a callback without the mem::replace
hack I am using in the following MWE? I am using rust stable (1.11.0).
use std::mem;
trait Actable {
fn act(&mut self);
}
// Not Cloneable
struct SelfCaller {
message: String,
callback: Box<FnMut(&mut SelfCaller)>,
// other stuff
}
impl Actable for SelfCaller {
fn act(&mut self) {
fn noop(_: &mut SelfCaller) {}
let mut callback = mem::replace(&mut self.callback, Box::new(noop));
callback(self);
mem::replace(&mut self.callback, callback);
}
}
impl Drop for SelfCaller {
fn drop(&mut self) {/* unimiportant to the story */}
}
fn main() {
fn change(messenger: &mut SelfCaller) {
messenger.message = "replaced message".to_owned();
}
let mut messenger = SelfCaller {
message: "initial message".to_owned(),
callback: Box::new(change),
};
messenger.act();
println!("{}", &messenger.message);
}
推荐答案
不,没有办法,因为这样做不安全.这是一个演示原因的示例(需要每晚编译器).
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);
}
这个程序的输出是:
calling callback
changing callback
Callback dropped!
changed callback
called callback
replaced message
好的,怎么回事?首先,我为 SelfCaller
编写了 act
的实现,这样我可以在没有 mem:: 的情况下调用回调替换
,使用mem::transmute
让编译器生成一个与self
断开连接的新生命周期.
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
.
然后,我编写了一个回调(使用结构 Callback
,因为我需要一个同时实现 FnMut
和 Drop
的类型到演示问题)通过更改 callback
成员来改变 SelfCaller
.这具有删除前一个回调的效果,也就是当前正在执行的回调!如果 Callback
包含数据成员,尝试读取它们会导致未定义的行为,因为它们现在处于已释放的内存中(我们丢弃了整个 Box
).
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
).
顺便说一下,在你使用 mem::replace
的代码中,回调不能改变回调,因为你在回调调用结束后恢复回调.
By the way, in your code using mem::replace
, callbacks cannot change the callback, since you restore the callback after the callback call ends.
这篇关于回调可变自身的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!