锈迹斑斑的零拷贝寿命处理

问题描述 投票:1回答:1

我试图在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) 已被调用。这样就可以释放借来的 Capturecapture.add(1) 调用会成功,并获得一个适当的可变引用。

我的零拷贝要求要求不要把片断复制到Vec中,然后把新的缓冲区交给 push(..).我在这里缺少什么?也许是一些显式的寿命注解?

rust lifetime borrow-checker
1个回答
0
投票

如何解决

创建一个新的区块,以确保不可更改的借款(source)之前丢掉。capture 是变异的。

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没有帮助

这个问题应该用 非逻辑性寿命 (NLL)。然而,NLL对于实现了 掉落 特质,因为 Drop 总是在一个值的词法作用域的末尾被调用,以便向后兼容。

因为 SyncSource 包含一个特质对象(dyn Fn() -> T),它有可能实现 Drop在这种情况下,防止了NLL。在这种情况下,NLL被阻止。这个游乐场 你可以看到,由于NLL的存在,删除trait对象可以解决这个错误。

但我想同时访问 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(),
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.