问题

简而言之:我正在try 实现一个struct,其中包含对闭包的回调,该闭包以静默方式捕获状态.其 idea 是,用户提供回调(闭包),并且可以在以后发生特定事件时得到通知.

我有一个可用的概念验证版本(code sample #1).

This PoC works (code sample #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..."

This fails (code sample #2个):

与前面的 idea 相同(略有不同),但试图将闭包包含在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>`

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

我应该如何修改code sample #2以获得像code sample #1中那样的回调注册工作?

代表:

推荐答案

abc仍然是f的结合点.

变量在它们所在的作用域结束时被销毁.f生活在main中,所以f在Main的末尾被销毁,这意味着在println期间,f仍然活着,并绑定abc.

为了更早地销毁一个变量,你必须有以下可能性:

  • 在它上面打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..."

Rust相关问答推荐

Rust kill std::processs::child

是否可以为`T:Copy`执行`T. clone`的测试

Arrow RecordBatch as Polars DataFrame

这种获取-释放关系是如何运作的?

JSON5中的变量类型(serde)

如何将实现多个特征的 struct 传递给接受这些特征为&;mut?

对于rustc编译的RISC-V32IM二进制文件,llvm objdump没有输出

对reqwest提供的这种嵌套JSON struct 进行反序列化

使用 Option 来分配?

tokio::spawn 有和没有异步块

从Rust 的临时文件中创建引用是什么意思?

为什么 Rust 的临时值有时有参考性有时没有?

特征中定义的类型与一般定义的类型之间的区别

如何获取函数中borrow 的切片的第一部分?

无法把握借来的价值不够长寿,请解释

Rust - 在线程之间不安全地共享没有互斥量的可变数据

基于名称是否存在的条件编译

list 中没有指定目标 - 必须存在 src/lib.rs、src/main.rs、[lib] 部分或 [[bin]] 部分

为什么这里需要类型注解?

类型参数不受 impl 特征、自身类型或谓词的约束