有没有办法(生锈)将一个可变的借来的自我发送到一个没有我在以下MWE中使用的mem::replace
黑客的回调?我正在使用锈稳定(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);
}
答案 0 :(得分:2)
否,您的代码无法做到这一点。如果可能的话,您可以轻松地构建一个破坏内存安全性的示例,例如访问释放的内存(这是留给读者的练习)。
您可以考虑SelfCaller
是否真的需要Inner
的所有字段。如果没有,您可以将(希望很少)单个字段作为参数传递。如果没有,您可以创建另一个类型(让我们称之为$self.find('a').attr('href','new value');
),其中包含对回调重要的所有字段并将其传递给函数。
答案 1 :(得分:2)
不,没有办法,因为这样做是不安全的。这是一个演示原因的例子(需要夜间编译器)。
#![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
好的,那是怎么回事?首先,我使用act
为SelfCaller
编写了mem::replace
的实现方式,以便我可以调用回调而不使用mem::transmute
让编译器生成与self
断开连接的新生命周期。
然后,我编写了一个回调函数(使用struct Callback
,因为我需要一个实现FnMut
和Drop
的类型来演示问题),它会改变SelfCaller
1}}通过更改其callback
成员。这具有删除前一个回调的效果,这是当前正在执行的回调!如果Callback
包含数据成员,则尝试读取它们会导致未定义的行为,因为它们现在处于释放的内存中(我们放弃了整个Box
)。
顺便说一下,在使用mem::replace
的代码中,回调无法更改回调,因为在回调调用结束后恢复回调。
答案 2 :(得分:2)
如果您不需要借用环境的回调,您可以使用函数而不是闭包:
trait Actable {
fn act(&mut self);
}
struct SelfCaller {
message: String,
callback: fn(&mut SelfCaller),
}
impl Actable for SelfCaller {
fn act(&mut self) {
(self.callback)(self);
}
}
fn main() {
fn change(messenger: &mut SelfCaller) {
messenger.message = "replaced message".to_owned();
}
let mut messenger = SelfCaller {
message: "initial message".to_owned(),
callback: change,
};
messenger.act();
println!("{}", &messenger.message);
}