Как захватить самопотребляющую переменную в структуре?

У меня есть экземпляр Reader с методом, который использует Reader и возвращает Writer, и Writer может аналогичным образом снова вернуться к Reader. Использовать неизменяемость тривиально, но я не могу понять, как скрыть неизменность от вызывающих, и просто заставить читателя-писателя-читателя танцевать за кулисами с изменчивым Я.

По сути, я хотел бы что-то вроде:

struct Container<'a> {
    reader: Reader<'a, File>
}

fn update(&mut self) {
    let writer = self.reader.as_writer();
    writer.write_something();
    self.reader = writer.as_reader();
}

который просто дает cannot move out of borrowed content ошибка. Попытка добавить Box, Cell или RefCell вокруг Reader, которые просто приводят к другим ошибкам.

Может ли reader быть скрытым за изменяемым интерфейсом, или это также приводит к изменчивости всей иерархии структуры? (т.е. похож на IO в Haskell)


Автономный образец с типами, соответствующими реальной вещи (я думаю)

#[derive(Debug)]
struct NoCopy(u32);

#[derive(Debug)]
struct Flipper<'a, T: 'a> {
    data: &'a mut T,
}
#[derive(Debug)]
struct Flopper<'a, T: 'a> {
    data: &'a mut T,
}
impl<'a, T> Flipper<'a, T> {
    fn flip(self) -> Flopper<'a, T> {
        Flopper{data: self.data}
    }
}
impl<'a, T> Flopper<'a, T> {
    fn flop(self) -> Flipper<'a, T> {
        Flipper{data: self.data}
    }
}

#[derive(Debug)]
struct Container<'a, T: 'a> {
    flipper: Flipper<'a, T>,
}
impl<'a, T> Container<'a, T> {
    fn run(&mut self) {
        self.flipper = self.flipper.flip().flop();
    }
}

fn main() {
    let f = Flipper{data: &mut NoCopy(42)};
    let f = f.flip().flop();
    println!("f={:?}", f);

    let mut c = Container{flipper: f};
    c.run();
    println!("c={:?}", c);
}
ошибка [E0507]: невозможно выйти из заимствованного контента
  -> src/main.rs:29:24
   |
29 |         self.flipper = self.flipper.flip().flop();
   |                        ^^^^ не может выйти из заимствованного контента

2 ответа

Решение

Более простое решение - использовать Option обернуть Reader,

Option имеет take метод, который возвращает содержимое (и помещает None в Option), то вы можете разместить Reader назад, назначив.

struct Container<'a> {
    reader: Option<Reader<'a, File>>
}

fn update(&mut self) {
    let writer = self.reader.take().unwrap().as_writer();
                             ^~~~~~~
    writer.write_something();
    self.reader = Some(writer.as_reader());
                  ^~~~~                  ^
}

Опираясь на ответ Матье, я решил, что легко добавить (тривиальную) обертку вокруг Option обеспечить проверки времени компиляции, которые позволяют только "заимствовать" Reader экземпляр, требуя другого, возвращается позже.

struct State<T>(Option<T>);

impl<T> State<T> {
    pub fn new(val: T) -> Self {
        State(Some(val))
    }
    pub fn modify<F>(&mut self, fun: F)
        where F: FnOnce(T) -> T
    {
        self.0 = Some(fun(self.0.take().unwrap()));
    }
}

Использование:

fn modify(&mut self) {
    self.state.modify(|reader| {
        let writer = reader.as_writer();
        writer.write_something();
        writer.as_reader()
    }
}

Должно быть достаточно хорошим, чтобы избежать случайных злоупотреблений. Понятия не имею о panic! разматывать хотя.

Вероятно, не следуя правилам именования Rust, state а также modify родом из Хаскелла State,

Другие вопросы по тегам