【问题标题】:How to pass a method as callback如何将方法作为回调传递
【发布时间】:2019-07-30 20:37:55
【问题描述】:

在 Python 或 C++ 中,类say A 可以将一些工作委托给类 Say B 的另一个实例,并在 B 中设置 A 的回调方法。 我尝试在 Rust 中做到这一点,但到目前为止我一无所获,被 Rust 编译器击败。

这是我尝试过的代码,其余代码在本文末尾。

在 A::test 中,我尝试使用闭包来获取 Fn() 特征对象作为回调。

// let b = B::new(self.finish)); // ideally but would not compile

// let test :Box<Fn(String)> = Box::new(move |msg| {self.finish(msg);}); // cannot infer an appropriate lifetime due to conflicting requirements
// let b = B::new(&test);

// let b = B::new(&Box::new( |msg| {A::finish(&self, msg);} )); // expected trait std::ops::Fn, found closure
// let b = B::new(&Box::new( |msg| {self.finish(msg);} )); // expected trait std::ops::Fn, found closure

还没有任何工作。有没有办法做到这一点?

任何帮助将不胜感激!

还是我根本上错了? Rust 是否要求另一种方式来实现这个想法?

这是我的测试代码

Play Ground Link

struct A {}

impl A {
    fn finish(&self, msg: String) {
        println!("{}", msg);
    }

    fn test(&self) {

        //let b = B::new(self.finish)); // would not compile

        // let test :Box<Fn(String)> = Box::new(move |msg| {self.finish(msg);}); // cannot infer an appropriate lifetime due to conflicting requirements
        // let b = B::new(&test);

        // let b = B::new(&Box::new( |msg| {A::finish(&self, msg);} )); // expected trait std::ops::Fn, found closure
        let b = B::new(&Box::new( |msg| {self.finish(msg);} )); // expected trait std::ops::Fn, found closure

        b.start("hi".to_string().clone());
    }
}

struct B<'b> {
    // cb:fn(msg:String),
    cb: &'b Box<Fn(String)>,
}

impl<'b> B<'b> {
    fn new(cb: &'b Box<Fn(String)>) -> B<'b> {
        B { cb: cb }
    }

    fn start(&self, msg: String) {
        (self.cb)(msg);
    }
}

fn main() {
    let a = A {};
    a.test();
}

【问题讨论】:

  • 为什么所有的代码都被注释掉了?请不要那样做!另外,请在操场右上角使用rustfmt,或在您的机器上使用cargo fmt
  • @hellow 感谢您的建议。我是 Rust 的新手,不知道。游乐场是个绝妙的主意。

标签: callback rust


【解决方案1】:

是的,您可以将方法作为回调传递给您的结构,并从该结构的方法中调用它。而且你不需要在传递引用时对闭包进行装箱:

struct A {}

impl A {
    fn finish(&self, msg: String) {
        println!("{}", msg);
    }

    fn test(&self) {
        let fun = |msg: String| self.finish(msg);
        let b = B::new(&fun);
        b.start("hi".to_string().clone());
    }
}

struct B<'b> {
    cb: &'b Fn(String),
}

impl<'b> B<'b> {
    fn new(cb: &'b Fn(String)) -> B<'b> {
        B { cb }
    }

    fn start(&self, msg: String) {
        (self.cb)(msg);
    }
}

fn main() {
    let a = A {};
    a.test();
}

playground

当您将函数移动到新结构时,该框很有用,但您的情况并非如此。

注意:由于您的函数称为start,我怀疑在您的实际用例中您想要启动一个线程,在这种情况下您可能应该查看channels 而不是回调。

【讨论】:

  • 谢谢,能否请您提供更多有关 Box 用法的信息。
  • @Ian.Zhang 一个 Box 对于存储非 Sized 值很有用。 Fn(String) struct 参数在编译时没有已知的大小,这就是您需要将其装箱的原因。如果您存储引用,则不需要它:引用具有恒定大小。
  • 是的,最初我启动了一个线程并尝试使用回调收集结果,但失败了。然后使用渠道解决这个问题。如果回调也可以工作,我会徘徊。
  • 简单而干净的解决方案是使用通道而不是尝试将回调传递给其他线程。
  • 我明白了,再次感谢
猜你喜欢
  • 2020-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-04
  • 2013-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多