我正在尝试在 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
):
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,删除特征对象修复了错误。
但我想在循环中访问source
和capture
!
那么可变借用和不可变借用是交错的,这意味着 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/