我试图在使用回调的一段代码中实现CLONE_INTO_BOX模式,但我遇到了一个难以理解的错误.

基本上,我是在克隆lambda的参数,但borrow 判断器仍然抱怨我泄露了对所述参数的引用.

我的问题是:这是borrow 判断器过于保守的情况(如果是这样的话,我如何才能以一种与borrow 判断器很好地配合的方式重写这篇文章),或者是我在这里遗漏了什么,尽管有克隆,但引用确实被泄露了吗?

代码:

pub trait CloneIntoBox{
    fn clone_into_box<'a>(&self) -> Box<dyn CloneIntoBox + 'a>;
}

impl<'a> Clone for Box<dyn CloneIntoBox + 'a> {
    fn clone(&self) -> Self {
        self.as_ref().clone_into_box()
    }
}

#[derive(Clone)]
pub struct StructWithBox<'a> {
    pub my_box: Box<dyn CloneIntoBox + 'a>,
}

#[derive(Clone)]
struct StructThatCanBeClonedIntoBox {
    pub data: u32,
}

impl CloneIntoBox for StructThatCanBeClonedIntoBox {
    fn clone_into_box<'a>(&self) -> Box<dyn CloneIntoBox + 'a> {
        Box::new(self.clone())
    }
}

pub type WalkCallback<'a> = dyn FnMut(&StructWithBox) + 'a;

pub fn walk(data: Vec<u32>, cb: &mut WalkCallback) {
    for d in data{
        let instance = StructWithBox{my_box: Box::new(StructThatCanBeClonedIntoBox{data:d})};
        cb(&instance);
    }
}

fn main() {
    let data = vec![1, 2];
    let mut result = vec![];
    walk(data, &mut|param| result.push((*param).clone()));
}

[Playground link]

提供:

error[E0521]: borrowed data escapes outside of closure
  --> src/main.rs:39:28
   |
38 |     let mut result = vec![];
   |         ---------- `result` declared here, outside of the closure body
39 |     walk(data, &mut|param| result.push((*param).clone()));
   |                     -----  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `param` escapes the closure body here
   |                     |
   |                     `param` is a reference that is only valid in the closure body

推荐答案

问题是StructWithBox中的'a.考虑一下walk()的以下实施:

impl<'b> CloneIntoBox for &'b u32 {
    fn clone_into_box<'a>(&self) -> Box<dyn CloneIntoBox + 'a> {
        Box::new(&42)
    }
}

pub fn walk(data: Vec<u32>, cb: &mut WalkCallback) {
    let data = 123u32;
    let instance = StructWithBox {
        my_box: Box::new(&data),
    };
    cb(&instance);
}

我还没有找到一种方法来使它变得不可靠:从impl CloneIntoBox for &u32返回的唯一东西是'static引用,因为'a是由调用者决定的.但borrow 判断器不知道这一点:它假设'a可以是回调中的任何生存期,然后我们将其推入回调外部的向量中,但在回调完成后可以释放它.因此,你可以认为这过于保守了,或者不是.

要解决此问题,您可以在回调中将'a设置为Always 'static:

pub type WalkCallback<'a> = dyn FnMut(&StructWithBox<'static>) + 'a;

或者干脆把一辈子都甩了:

pub trait CloneIntoBox {
    fn clone_into_box(&self) -> Box<dyn CloneIntoBox>;
}

impl Clone for Box<dyn CloneIntoBox> {
    fn clone(&self) -> Self {
        self.as_ref().clone_into_box()
    }
}

#[derive(Clone)]
pub struct StructWithBox {
    pub my_box: Box<dyn CloneIntoBox>,
}

#[derive(Clone)]
struct StructThatCanBeClonedIntoBox {
    pub data: u32,
}

impl CloneIntoBox for StructThatCanBeClonedIntoBox {
    fn clone_into_box(&self) -> Box<dyn CloneIntoBox> {
        Box::new(self.clone())
    }
}

Rust相关问答推荐

为什么函数不接受选项T参数的所有权?

如何在Rust中获得不可辩驳的'if let'模式警告Mutex锁定?""

如何在tauri—leptos应用程序中监听后端值的变化?""

收集RangeInclusive T到Vec T<><>

如何最好地并行化修改同一Rust向量的多个切片的代码?

如何导出 rust 色二进制文件中的符号

使用pyo3::Types::PyIterator的无限内存使用量

无法实现整型类型的泛型FN

不能在Rust中使用OpenGL绘制三角形

在复制类型中使用std::ptr::WRITE_VILAR进行内部可变性的安全性(即没有UnSafeCell)

是否可以在不直接重复的情况下为许多特定类型实现一个函数?

tokio::sync::broadcast::Receiver 不是克隆

Rust:为什么 Pin 必须持有指针?

Rust 中的通用 From 实现

当特征函数依赖于为 Self 实现的通用标记特征时实现通用包装器

将数据序列化为 struct 模型,其中两个字段的数据是根据 struct 中的其他字段计算的

在使用大型表达式时(8k 行需要一小时编译),是否可以避免 Rust 中的二次编译时间?

HashMap entry() 方法使borrow 的时间比预期的长

如何将 while 循环内的用户输入添加到 Rust 中的向量?

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