Обработка нулевого времени копирования - PullRequest
1 голос
/ 22 апреля 2020

Я пытаюсь реализовать механизм нулевого копирования для обработки данных в реальном времени в 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) смог сделать что-то полезное со слайсом (и, возможно, скопировать это в Ve c при необходимости), но с возможностью ничего с ним не делать.

По сути, мне нужно убедиться, что время жизни frame заканчивается после вызова push(frame). Это затем освободит заимствованную ссылку на Capture, и вызов capture.add(1) завершится успешно, когда будет получена правильная изменяемая ссылка.

Мое требование нулевой копии предписывает не копировать фрагмент в Ve c и затем передать этот новый буфер в push(..). Что мне здесь не хватает? Возможно, какие-то явные аннотации времени жизни?

1 Ответ

0 голосов
/ 23 апреля 2020

Как это исправить

Создать новый блок, чтобы убедиться, что неизменный заем (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). Однако NLL не работает для типов, которые реализуют черту Drop , потому что Drop всегда вызывается в конце лексической области значения для обратной совместимости.

Начиная с SyncSource содержит объект признака (dyn Fn() -> T), который потенциально может реализовать Drop, в этом случае предотвращается NLL. В этой игровой площадке вы можете видеть, что удаление объекта черты исправляет ошибку благодаря NLL.

Но я хочу получить доступ к source и capture в любом oop!

Тогда изменяемые и неизменяемые заимствования чередуются , что означает, что 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(),
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...