Как захватить самопотребляющую переменную в структуре?
У меня есть экземпляр 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
,