Как абстрагироваться от данных и действий с замыканиями - PullRequest
0 голосов
/ 22 марта 2020

Я пытаюсь создать код, который читает данные из какого-либо источника в обоих направлениях без дублирования кода.

use std::iter;

struct Reader {
    data: Vec<usize>,
    data_rev: Vec<usize>,
    data_it: Box<dyn Iterator<Item = usize>>,
    data_it_rev: Box<dyn Iterator<Item = usize>>,
}

enum Direction { Forward, Backward }

impl Reader {

    fn read(&mut self, n: usize, direction: Direction) {
        let (it, adder) = match direction {
            Direction::Forward => {
                let adder: Box<dyn FnMut(usize) -> ()> = Box::new(|idx| self.data.push(idx));
                (&self.data_it, adder)
            }
            Direction::Backward => {
                let adder: Box<dyn FnMut(usize) -> ()> = Box::new(|idx| self.data_rev.insert(0, idx));
                (&self.data_it_rev, adder)
            }
        };
        for idx in it.by_ref().take(n) {
            adder(idx);
        }
    }
}

Полный код здесь

Что я В этом примере я пытаюсь представить функцию read, которая имеет общий код и некоторый код, который меняется в зависимости от направления, в котором должно произойти read. Очевидно, как написать это с несколькими совпадениями шаблонов по направлению, но я хочу сделать это, не повторяя себя.

Если возможно, я бы хотел сделать направление неким обобщенным параметром c и получить что-то вроде

struct Reader<D: Directon> {
    data: Vec<usize>,
    data_it: Box<Iterator<Item = usize>>
}
trait Reader<Direction> {
    fn read(&mut self)
}
// somewhere in code
let reader = match direction {
    Direction::Forward => self.forward_reader;
    Direction::Backward => self.backward_reader;
}
reader.read()

1 Ответ

1 голос
/ 23 марта 2020

Обратите внимание, что указание направления в общем параметре c, как в конечном коде, означает, что направление статически определяется для каждого экземпляра Reader и не может быть изменено. Если это то, что вы хотите, вы можете сделать это, имея Direction быть признаком и Forward и Backward быть типами, которые реализуют эту черту:

trait Direction{}
struct Forward{}
impl Direction for Forward {}
struct Backward{}
impl Direction for Backward {}

struct Reader<D: Direction> {
    data: Vec<usize>,
    data_it: Box<dyn Iterator<Item = usize>>,
    direction: D,
}

impl Reader<Forward> {
    fn read (&mut self) { unimplemented!(); }
}

impl Reader<Backward> {
    fn read (&mut self) { unimplemented!(); }
}

Если ваша структура Reader должна также включают некоторые общие функции, которые не зависят от направления, вы можете добавить эти функции в дополнительный общий блок c impl:

impl<T: Direction> Reader<T> {
    fn common_function (&self) { unimplemented!(); }
}

Поскольку вы хотите иметь возможность иметь экземпляры каждого и переключения между ними во время выполнения, вам нужно будет определить интерфейс в признаке, а затем использовать ссылки на этот признак. Кроме того, чтобы иметь возможность доступа к общим функциям из реализации черты, они также должны быть определены в супертрассе:

pub trait Direction{}
pub struct Forward{}
impl Direction for Forward {}
pub struct Backward{}
impl Direction for Backward {}

pub trait CommonReader {
    fn common_function (&self);
}

pub trait ReaderItf: CommonReader {
    fn read (&mut self);
}

pub struct Reader<D: Direction> {
    pub data: Vec<usize>,
    pub data_it: Box<dyn Iterator<Item = usize>>,
    pub direction: D,
}

impl ReaderItf for Reader<Forward> {
    fn read (&mut self) { self.common_function(); }
}

impl ReaderItf for Reader<Backward> {
    fn read (&mut self) { unimplemented!(); }
}

impl<T: Direction> CommonReader for Reader<T> {
    fn common_function (&self) { unimplemented!(); }
}

// And you use it like this wherever you want to switch at runtime:
pub fn use_reader (r: &mut dyn ReaderItf) {
    r.read();
}

Playground

...