methods - 有什么方法可以让构建器使用创建对象的方法注册回调?

标签 methods callback closures rust builder

给定:对象 FooFooBuilderFooBuilder 必须创建对象并使用正在创建的对象的方法注册回调。在这种情况下,构建器充当对象 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/

相关文章:

javascript - 来自 GetLocations() 的回调(来自 Google Maps API)可以调用 API 函数以外的任何东西吗?

Java静态/非静态方法

javascript - 如何使用 Node.js 在外部作用域中定义回调函数?

c++ - 使用 `std::function` 从另一个对象调用一个对象的成员

javascript - 无法访问javascript中函数内的函数

循环内的 JavaScript 闭包 – 简单的实际示例

c# - 奇怪的 lambda 行为

java - 设计一个类来判断一个数是否为素数

c# - 方法重载 C#

java - 我应该如何将此对象添加到数组中...?