jeudi 5 août 2021

Callback pattern in Rust

Looking to implement a callback interface of two flavors. Immutable (cannot mutate the struct the callback is assigned to) and mutable (can mutate the struct the callback is assigned to).

type Callback<'a> = dyn FnMut(&'a MyStruct<'a>);
type CallbackMut<'a> = dyn FnMut(&'a mut MyStruct<'a>);

struct MyStruct<'a> {
    callback: &'a Callback<'a>,
    callback_mut: &'a CallbackMut<'a>
}

impl<'a> MyStruct<'a> {
    pub fn new(callback: &'a Callback<'a>, callback_mut: &'a CallbackMut<'a>) -> MyStruct<'a> {
        MyStruct {
            callback,
            callback_mut,
        }
    }

    pub fn trigger_callback(&'a self) {
        (self.callback)(self);
    }

    pub fn trigger_callback_mut(&'a mut self) {
        (self.callback_mut)(self);
    }
}

#[cfg(test)]
mod tests {
    use crate::minimal::*;

    #[test]
    fn it_works() {

        let mut triggered1 = false;
        let callback = |_my_struct: &MyStruct| {
            triggered1 = true;
        };

        let mut triggered2 = false;
        let callback_mut = |_my_struct: &mut MyStruct| {
            triggered2 = true;
        };

        let mut my_struct = MyStruct::new(&callback, &callback_mut);
        my_struct.trigger_callback();
        my_struct.trigger_callback_mut();
        assert!(triggered1, "Should call immutable callback");
        assert!(triggered2, "Should call mutable callback");
    }
}

So I am trying to understand how to make this pattern work in Rust and am confused about how to resolve the following few compiler errors.

  1. How do I use the assigned callback and pass the struct into it? What other patterns are available for doing this that are more Rust-friendly?
error[E0596]: cannot borrow `*self.callback_mut` as mutable, as it is behind a `&` reference
  --> src/minimal.rs:22:9
   |
6  |     callback_mut: &'a CallbackMut<'a>
   |                   ------------------- help: consider changing this to be mutable: `&'a mut CallbackMut<'a>`
...
22 |         (self.callback_mut)(self);
   |         ^^^^^^^^^^^^^^^^^^^ cannot borrow as mutable
  1. How can I mutate the variable captured in the callback? What other patterns are available for doing this that are more Rust-friendly?
error[E0597]: `triggered1` does not live long enough
  --> src/minimal.rs:35:13
   |
34 |         let callback = |_my_struct: &MyStruct| {
   |                        ----------------------- value captured here
35 |             triggered1 = true;
   |             ^^^^^^^^^^ borrowed value does not live long enough
...
43 |         let mut my_struct = MyStruct::new(&callback, &callback_mut);
   |                                           --------- cast requires that `triggered1` is borrowed for `'static`
...
48 |     }
   |     - `triggered1` dropped here while still borrowed
  1. It seems like I should be able to do an operation that is an immutable borrow strictly followed by an operation that is a mutable borrow when the immutable borrow does not live until the mutable borrow.
error[E0502]: cannot borrow `my_struct` as mutable because it is also borrowed as immutable
  --> src/minimal.rs:45:9
   |
44 |         my_struct.trigger_callback();
   |         --------- immutable borrow occurs here
45 |         my_struct.trigger_callback_mut();
   |         ^^^^^^^^^^--------------------^^
   |         |         |
   |         |         immutable borrow later used by call
   |         mutable borrow occurs here

  1. How would I then reference the variables that were augmented in the callbacks?
error[E0503]: cannot use `triggered1` because it was mutably borrowed
  --> src/minimal.rs:46:17
   |
34 |         let callback = |_my_struct: &MyStruct| {
   |                        ----------------------- borrow of `triggered1` occurs here
35 |             triggered1 = true;
   |             ---------- borrow occurs due to use of `triggered1` in closure
...
43 |         let mut my_struct = MyStruct::new(&callback, &callback_mut);
   |                                           --------- cast requires that `triggered1` is borrowed for `'static`
...
46 |         assert!(triggered1, "Should call immutable callback");
   |                 ^^^^^^^^^^ use of borrowed `triggered1`

Aucun commentaire:

Enregistrer un commentaire