捕获 FnMut 闭包的寿命不够长

问题描述 投票:0回答:1

问题

简而言之:我正在尝试实现一个

struct
,其中包含对闭包的回调,它可变地捕获状态。想法是用户提供回调(关闭),并且可以在特定事件发生时收到通知。

我有一个有效的概念验证版本(代码示例#1)。

这个 PoC 有效(代码示例 #1):

fn fun<F>(f: F) -> F
    where
        F: FnMut() -> (),
{
    f
}

fn main() {
    let mut abc = "abc".to_string();
    let mut f = fun(|| {
        abc.push_str(".");
        println!("closure: {:?}", abc);
    });
    f();
    f();
    f();
    println!("end: {:?}", abc);
}

输出:

closure: "abc."
closure: "abc.."
closure: "abc..."
end: "abc..."

失败了(代码示例#2):

与之前的想法相同(略有不同),但试图在内部包含闭包

Foo

struct Foo<'a, T> {
    pub cb: Box<dyn FnMut(&T) + 'a>,
}

impl<'a, T> Foo<'a, T> {
    fn new(f: impl FnMut(&T) + 'a) -> Self {
        Self { cb: Box::new(f) }
    }
    fn on_change(&mut self, f: impl FnMut(&T) + 'a)
    {
        self.cb = Box::new(f);
    }
}

impl<'a, T> Default for Foo<'a, T>
{
    fn default() -> Self {
        Self::new(|_| {})
    }
}

fn main() {
    let mut abc = "abc".to_string();
    let mut f = Foo::default();
    f.on_change(|a| {
        abc.push_str("."); // PROBLEM HERE; uncomment and it works!
        println!("- param: {:?}", a);
    });
    let param = "a".to_string();
    (f.cb)(&param);
    (f.cb)(&param);
    (f.cb)(&param);
    println!("end: {:?}", abc);
}

预期输出:

- param: "a"
- param: "a"
- param: "a"
end: "abc..."

实际输出(编译器错误):

error[E0502]: cannot borrow `abc` as immutable because it is also borrowed as mutable
  --> src/main.rs:33:27
   |
25 |     f.on_change(|a| {
   |                 --- mutable borrow occurs here
26 |         abc.push_str("."); // PROBLEM HERE; uncomment and it works!
   |         --- first borrow occurs due to use of `abc` in closure
...
33 |     println!("end: {:?}", abc);
   |                           ^^^ immutable borrow occurs here
34 | }
   | - mutable borrow might be used here, when `f` is dropped and runs the destructor for type `Foo<'_, String>`

编译器错误非常明显,它肯定与生命周期有关。我认为我的问题是我需要告诉编译器有关闭包及其参数生命周期的信息 - 但问题是,如何?

我应该如何修改代码示例#2才能像代码示例#1那样获得回调注册工作?

回复:

rust callback closures lifetime fn
1个回答
0
投票

abc
仍然绑定在
f

变量在其所在作用域结束时被销毁。

f
位于
main
中,因此
f
在main结束时被销毁。

要更早地销毁变量,您必须有以下可能性:

  • 拨打
    drop
    就可以了
  • 将其放入嵌套范围中

这里与

drop

struct Foo<'a, T> {
    pub cb: Box<dyn FnMut(&T) + 'a>,
}

impl<'a, T> Foo<'a, T> {
    fn new(f: impl FnMut(&T) + 'a) -> Self {
        Self { cb: Box::new(f) }
    }
    fn on_change(&mut self, f: impl FnMut(&T) + 'a) {
        self.cb = Box::new(f);
    }
}

impl<'a, T> Default for Foo<'a, T> {
    fn default() -> Self {
        Self::new(|_| {})
    }
}

fn main() {
    let mut abc = "abc".to_string();
    let mut f = Foo::default();
    f.on_change(|a| {
        abc.push_str("."); // PROBLEM HERE; uncomment and it works!
        println!("- param: {:?}", a);
    });
    let param = "a".to_string();
    (f.cb)(&param);
    (f.cb)(&param);
    (f.cb)(&param);

    drop(f);

    println!("end: {:?}", abc);
}

这里有一个嵌套范围:

struct Foo<'a, T> {
    pub cb: Box<dyn FnMut(&T) + 'a>,
}

impl<'a, T> Foo<'a, T> {
    fn new(f: impl FnMut(&T) + 'a) -> Self {
        Self { cb: Box::new(f) }
    }
    fn on_change(&mut self, f: impl FnMut(&T) + 'a) {
        self.cb = Box::new(f);
    }
}

impl<'a, T> Default for Foo<'a, T> {
    fn default() -> Self {
        Self::new(|_| {})
    }
}

fn main() {
    let mut abc = "abc".to_string();

    {
        let mut f = Foo::default();
        f.on_change(|a| {
            abc.push_str("."); // PROBLEM HERE; uncomment and it works!
            println!("- param: {:?}", a);
        });
        let param = "a".to_string();
        (f.cb)(&param);
        (f.cb)(&param);
        (f.cb)(&param);
    }

    println!("end: {:?}", abc);
}

两个代码片段都打印:

- param: "a"
- param: "a"
- param: "a"
end: "abc..."
© www.soinside.com 2019 - 2024. All rights reserved.