使用自我回调

callback using self

我正在尝试使用一个使用对象的 self 参数的回调。阻止编译的部分是 self.make_sandwhich() 位。我怎样才能做这样的事情并让它真正编译成 Rust? objective 是为了让 ThingUser 能够指定事物的功能。在这种情况下,当你把土豆扔掉时,用户做了一个三明治。

pub trait Thing {
  fn stuff(&self);
  fn junk(&mut self);
}

pub struct Potato {
    thing: Box<dyn FnMut()+Send>,
}

impl Potato {
  fn new() -> Self {
    Self{
        thing: Box::new(||{println!("Original callback");}),
    }
  }
  
  fn give_callback(&mut self, thing: Box<dyn FnMut()+Send>) {
      self.thing = thing;
  }
}

impl Thing for Potato {
  fn stuff(&self) {
  }
  fn junk(&mut self) {
    (self.thing)();
  }
}

fn make_thing() -> Box<dyn Thing> {
    Box::new(Potato::new())
}

pub trait ThingUser {
    fn get_thing(&mut self) -> Option<Box<dyn Thing>>;
}

pub struct Person {
    
}

impl Person {
    fn make_sandwhich(&self) {
        println!("I made a sandwhich");
    }
}

impl ThingUser for Person {
    fn get_thing(&mut self) -> Option<Box<dyn Thing>> {
        let mut p = Potato::new();
        p.give_callback(Box::new(||{self.make_sandwhich()}));
        Some(Box::new(p))
    }
}

fn main() {
    println!("Hello, world!");
  let mut p  = Potato::new();
  p.stuff();
  
  p.give_callback(Box::new(||{println!("Callback");}));
  (p.thing)();
  p.junk();
  
  let mut q = make_thing();
  q.junk();
  
  let mut tu = Person{};
  let used_thing = tu.get_thing();
  used_thing.unwrap().junk();
}

您发布的设计无法使用。在 ThingUser::get_thing 中需要 &mut self,这意味着 self&mut dyn ThingUser 类型的借用。闭包|| { self.make_sandwhich() }也必须借用self(虽然它只需要一个非mut&dyn ThingUser来调用make_sandwhich)。 self 的借用必须与闭包一样长。

但是,您想将闭包存储在 Potato 中。这意味着在存储它之后 Potato 也在借用 &ThingUser,但是 Potato 根本没有定义为借用任何东西(在 potato 的定义中没有生命周期参数)。因此,您将能够传递给 give_callback 的任何闭包都必须 具有 'static 生命周期(这意味着它不会借用任何东西,除了可能存在的东西节目的整个长度)。

这就是您的编译器错误所表达的内容 (playground)

error: lifetime may not live long enough
  --> src/main.rs:51:25
   |
49 |     fn get_thing(&mut self) -> Option<Box<dyn Thing>> {
   |                  - let's call the lifetime of this reference `'1`
50 |         let mut p = Potato::new();
51 |         p.give_callback(Box::new(||{self.make_sandwhich()}));
   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static`

编译器阻止你这样做:

let person = Person {};
let mut potato = Potato::new();
p.give_callback(Box::new(|| { person.make_sandwhich() } ));
drop(person);
potato.junk(); // ! This would be trying to call `person.make_sandwhich()`, but `person` has been dropped! This is a horrible memory safety error!

大体上你有两个选择。

首先是用生命周期参数定义 Potato,但这很快就会变得混乱,因为生命周期参数将与马铃薯类型相关联,并且几乎每次使用 Potato 都会变得复杂曾经。

第二个(可能更可取)选项是让您的闭包具有 'static 生命周期。例如,这可以通过将您的人存储在 Arc<Person> 中来完成。然后使用 move 闭包创建一个拥有 Arc<Person> 的闭包,这样 Person 在关闭闭包之前不能被删除。例如

let person = Arc::new(Person {});
let mut potato = Potato::new();
let person_clone = person.clone();  // Not strictly necessary for this simple example, but I'm assuming you might want access to `person` after creating the callback in which case this is necessary
p.give_callback(Box::new(move || { person_clone.make_sandwhich() } ));
drop(person);
potato.junk(); // This is now fine! The first `Arc<Person>` was dropped, but the closure is still holding one so the internal `Person` object has not been dropped. Hooray!

P.S。我强烈建议您使用 rustfmt 来格式化您的代码(最好是在每次保存时)。它将使您更快地编写它并帮助人们阅读它。