给定:对象 Foo
和 FooBuilder
。 FooBuilder
必须创建对象并使用正在创建的对象的方法注册回调。在这种情况下,构建器充当对象 Foo
的高级定制,需要根据事件的接收进行更改。静态方法不是强制性的,但仍然是可取的。 main()的最后是事件循环
#[macro_use]
extern crate lazy_static;
use std::sync::Mutex;
struct Global {
callbacks: Vec<Box<Fn(i32) + 'static + Send + Sync>>
}
lazy_static! {
static ref GLOBAL: Mutex<Global> = {
Mutex::new(Global {
callbacks: Vec::new()
})
};
}
struct FooBuilder {
c: i32
}
impl FooBuilder {
fn new() -> FooBuilder {
FooBuilder {
c: 0
}
}
fn build(&self) -> Foo {
let foo = Foo {
f: self.c
};
// foo.set_callback(|x| foo.edit());
// ^ ERROR
foo
}
}
struct Foo {
f: i32
}
impl Foo {
fn edit(&mut self) {
self.f += 1;
println!("{:?}", self.f);
}
fn set_callback<F>(&self, f: F)
where F: Fn(i32) + 'static + Send + Sync {
GLOBAL.lock().unwrap().callbacks.push(Box::new(f));
}
}
fn main() {
let foo = FooBuilder::new().build();
/* event loop */
}
最佳答案
不适用于您的情况。
考虑这部分代码:
fn set_callback<F>(&self, f: F)
where F: Fn(i32) + 'static + Send + Sync
这表示提供给该方法的任何闭包只能包含与 'static
生命周期相匹配的引用。 foo
实例的地址只保证在 build
方法体内有效,因此根据定义,生命周期不能是 'static
.
如果这是可以避免的,那么您将闭包限制为 Fn
类型,但您希望调用需要可变接收器的方法。
您可以做的是向闭包提供self
,然后在那里使用它:
fn set_callback<F>(&self, f: F)
where F: Fn(&mut Self, i32) + 'static + Send + Sync
{}
// Elsewhere
foo.set_callback(|foo, x| foo.edit());
来自您编辑的评论:
then this approach is not very suitable
问题在于您对问题建模的方式充满了危险,而 Rust 会阻止您搬起石头砸自己的脚。您只能对一个项目有一个可变引用。除此之外,您还试图在函数调用之间共享对堆栈分配项的引用。
也许您会更喜欢一种更接近垃圾收集语言可能创建的解决方案?
在这里,我们使用 Mutex
允许多个线程共享一个可变项。我们使用 Arc
以便多个线程可以共享项目的所有权。
我还将 set_callback
移到了一个自由函数中,因为它与您之前使用它的结构无关。
当调用 set_callback
时,我们克隆增加引用计数的 Arc
并将该克隆提供给全局队列。由于 Mutex
具有内部可变性,该函数可以保留为 Fn
。
#[macro_use]
extern crate lazy_static;
use std::sync::{Mutex,Arc};
struct Global {
callbacks: Vec<Box<Fn(i32) + 'static + Send + Sync>>
}
fn set_callback<F>(f: F)
where F: Fn(i32) + 'static + Send + Sync
{
GLOBAL.lock().unwrap().callbacks.push(Box::new(f));
}
lazy_static! {
static ref GLOBAL: Mutex<Global> = {
Mutex::new(Global {
callbacks: Vec::new()
})
};
}
struct FooBuilder;
impl FooBuilder {
fn build(&self) -> Arc<Mutex<Foo>> {
let foo = Arc::new(Mutex::new(Foo));
let inner_foo = foo.clone();
set_callback(move |_| inner_foo.lock().unwrap().edit());
foo
}
}
struct Foo;
impl Foo {
fn edit(&mut self) {}
}
fn main() {
let _foo = FooBuilder.build();
}
关于methods - 有什么方法可以让构建器使用创建对象的方法注册回调?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33676804/