'How can i pass a struct method as callback in Rust?

In C++, I work with this.

class A{
    public:
        void read();
}

class B{
    public:
    void setCallback(std::function<void()> f){
        ...
    }
}

int main(){
    A a;
    B b;
    b.setCallback(std::bind(&A::read,&a));
}

Could I implement the same function in Rust?



Solution 1:[1]

@isaactfa answer is correct, but std::function uses dynamic dispatch and is more like Box<dyn Fn()> than using generics.

struct B<'a> {
    callback: Option<Box<dyn FnMut() + 'a>>,
}

impl<'a> B<'a> {
    fn set_callback(&mut self, callback: Box<dyn FnMut() + 'a>) {
        self.callback = Some(callback);
    }

    fn call_callback(&mut self) {
        self.callback.as_mut().map(|cb| cb());
    }
}

fn main() {
    let mut a = A { x: 0 };
    let mut b = B { callback: None };
    b.set_callback(Box::new(|| a.do_something()));
    b.call_callback();
    drop(b); // It is necessary for the code to not use `a` while it's borrowed by the closure
    assert_eq!(a.x, 1);
}

Playground.

You can have a little more convenience by creating the Box within the struct methods, so the caller doesn't have to create it:

impl<'a> B<'a> {
    fn set_callback(&mut self, callback: impl FnMut() + 'a) {
        self.callback = Some(Box::new(callback));
    }
}

Playground.

Solution 2:[2]

Does this work for you?

struct A {
    x: u32,
}

impl A {
    fn do_something(&mut self) {
        self.x += 1;
    }
}

struct B<F> {
    callback: Option<F>,
}

impl<F: FnMut()> B<F> {
    fn set_callback(&mut self, callback: F) {
        self.callback = Some(callback);
    }
    
    fn call_callback(&mut self) {
        self.callback.as_mut().map(|cb| cb());
    }
}

fn main() {
    let mut a = A { x: 0, };
    let mut b = B { callback: None, };
    b.set_callback(|| a.do_something());
    b.call_callback();
    assert_eq!(a.x, 1);
}

I took a conservative guess at what the mutability requirements would be.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Chayim Friedman
Solution 2 isaactfa