2

I have some code that looks like this:

type Callback<T> = Box<Fn(&T) -> ()>;

struct Foo {
    name: String,
}

impl Foo {
    fn name_updater(&mut self) -> Callback<String> {
        Box::new(|new_name| {
            self.name = *new_name;
        })
    }
}

This code doesn't compile because the closure requires the static lifetime. However I'm unsure if this will resolve the question and because there aren't explicit lifetimes, it's not immediately clear to me what I'd need to do to resolve that.

Here is an example of the compilation error:

error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
  --> src/lib.rs:9:9
   |
9  | /         Box::new(|new_name| {
10 | |             self.name = *new_name;
11 | |         })
   | |__________^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 8:5...
  --> src/lib.rs:8:5
   |
8  | /     fn name_updater(&mut self) -> Callback<String> {
9  | |         Box::new(|new_name| {
10 | |             self.name = *new_name;
11 | |         })
12 | |     }
   | |_____^
note: ...so that the type `[closure@src/lib.rs:9:18: 11:10 self:&mut &mut Foo]` will meet its required lifetime bounds
  --> src/lib.rs:9:9
   |
9  | /         Box::new(|new_name| {
10 | |             self.name = *new_name;
11 | |         })
   | |__________^
   = note: but, the lifetime must be valid for the static lifetime...
   = note: ...so that the expression is assignable:
           expected std::boxed::Box<(dyn for<'r> std::ops::Fn(&'r std::string::String) + 'static)>
              found std::boxed::Box<dyn for<'r> std::ops::Fn(&'r std::string::String)>

How can I write a closure that is capable of mutating the struct's name property?

maxcountryman
  • 1,562
  • 1
  • 24
  • 51

1 Answers1

2

You need to bound a lifetime to your closure because you borrow something aka &mut self:

type Callback<'a, T> = Box<dyn FnMut(&T) -> () + 'a>;

#[derive(Debug)]
struct Foo {
    name: String,
}

impl Foo {
    fn name_updater(&mut self) -> Callback<str> {
        Box::new(move |new_name| {
            self.name.replace_range(.., new_name);
        })
    }
}

fn main() {
    let mut foo = Foo {
        name: String::from("foo"),
    };

    foo.name_updater()("bar");

    println!("{:?}", foo);
}

Also note that you don't need to use a box:

#[derive(Debug)]
struct Foo {
    name: String,
}

impl Foo {
    fn name_updater<'a>(&'a mut self) -> impl FnMut(&str) -> () + 'a {
        move |new_name| {
            self.name.replace_range(.., new_name);
        }
    }
}

fn main() {
    let mut foo = Foo {
        name: String::from("foo"),
    };

    foo.name_updater()("bar");

    println!("{:?}", foo);
}
Stargateur
  • 24,473
  • 8
  • 65
  • 91
  • [The compiler suggests I add a 'static lifetime because the parameter type may not live long enough, but I don't think that's what I want](https://stackoverflow.com/q/40053550/155423) – Shepmaster Jul 07 '19 at 12:20