我该如何写一个盒装的闭包来改变对结构的引用?

时间:2019-07-06 23:48:41

标签: rust

我有一些看起来像这样的代码:

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;
        })
    }
}

此代码无法编译,因为闭包需要静态生存期。但是,我不确定这是否可以解决问题,并且由于没有明确的生命周期,因此尚不清楚我需要做什么来解决该问题。

以下是编译错误的示例:

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)>

如何编写能够使结构的name属性发生变异的闭包?

1 个答案:

答案 0 :(得分:2)

因为借用了&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);
}

还请注意,您无需使用方框:

#[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);
}