我试图在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(..)
.我在这里缺少什么?也许是一些显式的寿命注解?
创建一个新的区块,以确保不可更改的借款(source
)之前丢掉。capture
是变异的。
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)。然而,NLL对于实现了 掉落 特质,因为 Drop
总是在一个值的词法作用域的末尾被调用,以便向后兼容。
因为 SyncSource
包含一个特质对象(dyn Fn() -> T
),它有可能实现 Drop
在这种情况下,防止了NLL。在这种情况下,NLL被阻止。这个游乐场 你可以看到,由于NLL的存在,删除trait对象可以解决这个错误。
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(),
}
}
}