1

I have a struct in a module with a field with a Fn type and a setter method, trying to register a callback function

struct MyStruct {
    name: String,
    f: Box<dyn Fn(String) -> ()>,
}

impl MyStruct {
    pub fn set_f(&mut self, f: Box<dyn Fn(String) -> ()>) {
        self.f = f
    }

    pub fn set_handler(&mut self, f: Box<dyn Fn(String) -> ()>) {
        let h = |s: String| {
            f(format!("{} {}", self.name, s));
        };

        self.set_f(Box::new(h));
    }
}

fn main() {
    let my_struct = MyStruct {
        name: String::from("hello"),
        f: Box::new(|_: String| ()),
    };
    my_struct.set_handler(Box::new(|s: String| println!("{}", s)))
}

playground

getting the following error:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/main.rs:12:17
   |
12 |           let h = |s: String| {
   |  _________________^
13 | |             f(format!("{} {}", self.name, s));
14 | |         };
   | |_________^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 11:5...
  --> src/main.rs:11:5
   |
11 | /     pub fn set_handler(&mut self, f: Box<dyn Fn(String) -> ()>) {
12 | |         let h = |s: String| {
13 | |             f(format!("{} {}", self.name, s));
14 | |         };
15 | |
16 | |         self.set_f(Box::new(h));
17 | |     }
   | |_____^
   = note: ...so that the types are compatible:
           expected &&mut MyStruct
              found &&mut MyStruct
   = note: but, the lifetime must be valid for the static lifetime...
   = note: ...so that the expression is assignable:
           expected std::boxed::Box<(dyn std::ops::Fn(std::string::String) + 'static)>
              found std::boxed::Box<dyn std::ops::Fn(std::string::String)>
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
harumphfrog
  • 187
  • 1
  • 1
  • 7
  • `Fn(T) -> ()` is a trait, not a type, at least, [not the way you want it to be](https://stackoverflow.com/questions/50650070/what-does-dyn-mean-in-a-type). You might find it helpful to read [How do I store a closure in a struct in Rust?](https://stackoverflow.com/q/27831944/3650362) Beyond that, please take some time to create a [mre]. There are no silver bullets when it comes to lifetime errors (or any errors, really); we need to know what code caused the errors you're having trouble with. Ideally, create something on the [playground](https://play.rust-lang.org/) or in a new Cargo project. – trent Nov 08 '19 at 03:17
  • Updated the description and added a link to the playground. – harumphfrog Nov 08 '19 at 03:48
  • The [duplicates applied to your case](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=245398b55a088215710f34d135fa4ec9) – Shepmaster Nov 08 '19 at 13:55

1 Answers1

1

Core problem is that, h uses self.name and f inside it's implementation. Rust closures by default capture by reference (borrow), so if you store your callback inside MyStruct, captured f will not live enough, because it will be destroyed (dropped), after execution leaves set_handler block.

Another problem is that by default values stored in Box<_> should live as long as 'static.

Compiler automaticaly tries to assign apropriate lifetime for &mut self, it defaults to assume, that self should live as long as set_handler function executes.

You're essentially trying to create self-referential struct. MyStruct references itself inside callback. Simplest solution will be to just clone name and remove self-reference.

Here we force closure to take ownership of variables used inside it and clone self.name, so closure doesn't borrow self

    let name = self.name.clone();
    let h = move |s: String| {
        f(format!("{} {}", name, s));
    };

More complicated solution will be, to tell compiler that MyStruct can't move, because self-refential types can be safe only when they don't move around changing their reference (borrow) address and tell compiler that data in closure should live as long as MyStruct. This is more complicated.

Refactored code using Pin<_> and some carefully written unsafe code.

use std::marker::PhantomPinned;
use std::pin::Pin;
use std::ptr::NonNull;

// Alias supertrait
trait Callback: Fn(String) -> ()  {}
impl<T> Callback for T where T: Fn(String) -> ()  {}

struct MyStruct {
    name: String,
    handler: Box<dyn Callback>,
    _pin: PhantomPinned,
}

impl MyStruct {
    pub fn new() -> Pin<Box<MyStruct>> {
        Box::pin(MyStruct {
            name: String::from("hello"),
            handler: Box::new(|_| { Default::default() }),
            _pin: PhantomPinned,
        })
    }

    // Extracting callback is essentially safe, because if you save it, it can't live longer than pinned MyStruct. Lifetimes are elided.
    pub  fn get_handler_mut(self: Pin<&mut MyStruct>) -> &mut Box<dyn Callback> {
        unsafe {
            &mut self.get_unchecked_mut().handler
        }
    }

    pub fn set_handler(self: Pin<&mut MyStruct>, f: impl Callback + 'static) {
        // Create non null, raw pointer. Type is pinned and lifetimes are set by compiler for get_handler_mut(), everything is safe.
        let name = NonNull::from(&self.name);

        let wrapper = move |s: String| {
            // This is safe, because self is pinned, so name can't point to dangling pointer.
            let name = unsafe { name.as_ref() };

            f(format!("{} {}", name, s));
        };

        unsafe {
            // We know that assigning to `handler` will not move self, so it's safe.
            self.get_unchecked_mut().handler = Box::new(wrapper);
        }
    }
}


fn main() {
    let mut my_struct = MyStruct::new();

    my_struct.as_mut().set_handler(|s: String| {
        println!("{}", s)
    });


    let handler = my_struct.as_mut().get_handler_mut();
    (handler)("test".to_owned())

}

Another safe, but less effective solution will be to store self.name in reference counted smart pointer. Rc will manage lifetime of name automatically.

use std::rc::Rc;

// Alias supertrait
trait Callback: Fn(String) -> ()  {}
impl<T> Callback for T where T: Fn(String) -> ()  {}

struct MyStruct {
    name: Rc<String>,
    handler: Box<dyn Callback>,
}

impl MyStruct {
    pub fn new() -> MyStruct {
        MyStruct {
            name: Rc::new("hello".to_owned()),
            handler: Box::new(|_| { Default::default() }),
        }
    }

    pub fn get_handler(&self) -> &Box<dyn Callback> {
        &self.handler
    }

    pub fn set_handler(&mut self, f: impl Callback + 'static) {
        let name = self.name.clone();
        let wrapper = move |s: String| {
            f(format!("{} {}", name, s));
        };
        self.handler = Box::new(wrapper);
    }
}


fn main() {
    let mut my_struct = MyStruct::new();

    my_struct.set_handler(|s: String| {
        println!("{}", s)
    });

    let handler = my_struct.get_handler();
    (handler)("test".to_owned())
}

Conclusion. Self-referential structs in Rust can be quite complicated, because language tries to deal with unsafety and relies on RAII and manual memory management. There is no GC taking care of things. In production for this example I would use Rc or cloning options (depending on circumstances), because it's the safest approach of all.

Inline
  • 2,566
  • 1
  • 16
  • 32
  • Thanks for the detailed answer. The clone solution won't work because the handler closure should use the current value of `name` at the time that it is called, rather than at the time `set_handler` is called. – harumphfrog Nov 08 '19 at 12:38