Rust 零复制生命周期处理

标签 rust lifetime borrow-checker

我正在尝试在 Rust 中实现用于实时数据处理的零复制机制。 为了说明我的问题,我准备了以下示例:

use std::io;

pub trait Producer<T> {
    fn produce(&self) -> Result<T, ()>;
}

pub trait Consumer<T> {
    fn consume(&self, t: T);
}

pub trait Source<T> : Producer<T> {
    fn push(&self, t: T) -> io::Result<()>;
}

pub trait Sink<T> : Consumer<T> {
    fn pull(&self) -> io::Result<T>;
}

pub struct SyncSource<T> {
    pub producer: Option<Box<dyn Fn() -> T>>,
}

impl<T> SyncSource<T> {
    pub fn new() -> SyncSource<T> {
        SyncSource {
            producer: None,
        }
    }
}

impl<T> Producer<T> for SyncSource<T> {
    fn produce(&self) -> Result<T, ()> {
        match &self.producer {
            Some(func) => Ok((*(func))()),
            None => Err(()),
        }
    }
}

impl<T> Source<T> for SyncSource<T> {
    fn push(&self, t: T) -> io::Result<()> {
        // do something useful
        Ok(())
    }
}

pub struct Frame<'a> {
    pub buf: &'a [u8],
}

pub struct Capture {
    buf: Vec<u8>,
}

impl Capture {
    pub fn add(&mut self, val: u8) {
        self.buf.push(val);
    }

    pub fn read(&self) -> Frame {
        Frame {
            buf: &self.buf[..],
        }
    }
}

fn main() {
    let mut capture = Capture {
        buf: Vec::new(),
    };

    let source: SyncSource<Frame> = SyncSource::new();

    // immutable borrow of 'capture'
    let frame = capture.read();

    source.push(frame);

    // mutable borrow of 'capture'
    capture.add(1); // ERROR
}

..这当然会产生借用检查器错误:

error[E0502]: cannot borrow `capture` as mutable because it is also borrowed as immutable
   --> src/bin/so.rs:212:5
    |
208 |     let frame = capture.read();
    |                 ------- immutable borrow occurs here
...
212 |     capture.add(1);
    |     ^^^^^^^^^^^^^^ mutable borrow occurs here
213 | }
    | - immutable borrow might be used here, when `source` is dropped and runs the destructor for type `SyncSource<'_, Frame<'_>>`

我知道 push(frame) 不能在同一范围内拥有不可变引用,而 capture.add(1) 稍后需要可变引用。

我想要实现的是 push(frame) 能够对切片做一些有用的事情(如果需要的话,也许可以将其复制到 Vec 中),但有可能不用它做任何事情。

基本上,我需要确保 frame 的生命周期在调用 push(frame) 后结束。然后,这将释放对 Capture 借用的引用,并且 capture.add(1) 调用将成功并获取正确的可变引用。

我的零复制要求要求不要将切片复制到 Vec 中,然后将新缓冲区交给 push(..)。 我在这里缺少什么?也许有一些显式的生命周期注释?

最佳答案

如何修复

创建一个新 block 以确保在 capture 发生变异之前删除不可变借用 (source):

Playground

let mut capture = Capture {
    buf: Vec::new(),
};

{
    let source: SyncSource<Frame> = SyncSource::new();

    // immutable borrow of 'capture'
    let frame = capture.read();

    // borrow moved into `source`
    source.push(frame);

    // `source` dropped here
}

// mutable borrow of 'capture'
capture.add(1);

为什么 NLL 没有帮助

这个问题应该用 non-lexical lifetimes 来解决(NLL)。但是,NLL 不适用于实现 Drop 的类型。特征,因为 Drop 总是在值的词法范围末尾调用以实现向后兼容性。

由于 SyncSource 包含一个特征对象 (dyn Fn() -> T),它可能会实现 Drop,因此可以防止 NLL这个案例。在 this playground您可以看到,由于 NLL,删除特征对象修复了错误。

但我想在循环中访问sourcecapture!

那么可变借用和不可变借用是交错的,这意味着 Rust 无法在编译时验证所有权规则。

您可以使用 RefCell 来解决此问题,这确保所有权规则在运行时得到维护。这可以这样实现:

use std::cell::{RefCell, Ref};

pub struct Frame<'a> {
    pub buf: Ref<'a, Vec<u8>>,
}

pub struct Capture {
    buf: RefCell<Vec<u8>>,
}

impl Capture {
    pub fn add(&self, val: u8) {
        self.buf.borrow_mut().push(val);
    }

    pub fn read(&self) -> Frame {
        Frame {
            buf: self.buf.borrow(),
        }
    }
}

关于Rust 零复制生命周期处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61371710/

相关文章:

rust - 如何在rust中返回带有引用的结构?

rust - “cannot borrow as immutable because it is also borrowed as mutable”在嵌套数组索引中是什么意思?

rust - 如何分配给匹配分支内的匹配表达式中使用的变量?

rust - 如何修改 HashSet 中不属于哈希计算的属性?

error-handling - 如何从主结果处理程序中删除默认的 “Error”

rust - "parameter ` 'a` 从不使用 "error when ' a 在类型参数绑定(bind)中使用

rust - 不能借用为不可变的,因为它在函数参数中也被借用为可变的

macos - OS X 上的 Rust 和加载程序路径(@rpath、@loader_path)

rust - 如何使用 dyn 类型别名将生命周期参数添加到 Box<>

rust - 在字符串上创建闭包返回迭代器