próbuję zdefiniować struct, który może działać jako iterator dla Vec
, że odbywa się w ramach RefCell
:Encapsulating kolejno zainicjowany stan z własnym odniesień w Rust struct
use std::slice::Iter;
use std::cell::Ref;
use std::cell::RefCell;
struct HoldsVecInRefCell {
vec_in_refcell: RefCell<Vec<i32>>,
}
// TODO: struct HoldsVecInRefCellIter implementing Iterator ...
impl HoldsVecInRefCell {
fn new() -> HoldsVecInRefCell {
HoldsVecInRefCell { vec_in_refcell: RefCell::new(Vec::new()) }
}
fn add_int(&self, i: i32) {
self.vec_in_refcell.borrow_mut().push(i);
}
fn iter(&self) -> HoldsVecInRefCellIter {
// TODO ...
}
}
fn main() {
let holds_vec = HoldsVecInRefCell::new();
holds_vec.add_int(1);
holds_vec.add_int(2);
holds_vec.add_int(3);
let mut vec_iter = holds_vec.iter(); // Under the hood: run-time borrow check
for i in vec_iter {
println!("{}", i);
}
}
porównania, vec_iter
can być inicjowane w linii w main()
następująco (celowo gadatliwy):
// Elided: lifetime parameter of Ref
let vec_ref: Ref<Vec<i32>> = holds_vec.vec_in_refcell.borrow();
// Elided: lifetime parameter of Iter
let mut vec_iter: Iter<i32> = vec_ref.iter();
Czy istnieje jakiś sposób, aby zdefiniować struct wykonawczych Iterator
, która posiada zarówno Ref
(aby utrzymać niezmienny numer RefCell
pożyczać przy życiu) i Iter
(aby zachować stan iteratora dla next()
, zamiast toczyć własny iterator dla Vec
lub dowolnego innego kontenera), gdy drugi pochodzi z (i zawiera referencję uzyskaną od) pierwszy?
Próbowałem kilku podejść do realizacji tego, a wszystkie działają wbrew sprawdzeniu pożyczek. Jeśli mogę umieścić zarówno państwowych jak kawałki gołych struct członków, jak
struct HoldsVecInRefCellIter<'a> {
vec_ref: Ref<'a, Vec<i32>>,
vec_iter: Iter<'a, i32>,
}
potem nie mogę zainicjować oba pola na raz HoldsVecInRefCellIter { ... }
składni (patrz np Does Rust have syntax for initializing a struct field with an earlier field?). Gdy próbuję manewrowania sekwencyjną inicjalizacji z struct jak
struct HoldsVecInRefCellIter<'a> {
vec_ref: Ref<'a, Vec<i32>>,
vec_iter: Option<Iter<'a, i32>>,
}
// ...
impl HoldsVecInRefCell {
// ...
fn iter(&self) -> HoldsVecInRefCellIter {
let mut new_iter = HoldsVecInRefCellIter { vec_ref: self.vec_in_refcell.borrow(), vec_iter: None };
new_iter.vec_iter = new_iter.vec_ref.iter();
new_iter
}
}
potem ponieść zmienny siebie pożyczyć struct, który zapobiega jego zwrot od iter()
. To samo-pożyczanie struktury może się również zdarzyć, jeśli spróbujesz zapisać odniesienie do jednej części struktury w samej strukturze (Why can't I store a value and a reference to that value in the same struct?), co uniemożliwiłoby bezpieczne przenoszenie instancji struktury. Dla porównania wygląda na to, że struktura taka jak HoldsVecInRefCellIter
, jeśli mógłbyś zakończyć inicjalizację, zrobiłaby właściwą rzecz podczas przenoszenia, ponieważ wszystkie odniesienia wewnętrznie odnoszą się do danych w innym miejscu niż ta struktura.
Istnieją sztuczki, aby uniknąć tworzenia self-referencje użyciu Rc
(patrz przykłady na https://internals.rust-lang.org/t/self-referencing-structs/418/3), ale nie widzę w jaki sposób mogłyby one być stosowane, jeżeli chcesz zachować istniejący Iterator
struct który jest realizowany na zorganizowanie bezpośredniego odniesienia do podstawowego pojemnika, a nie do Rc
.
Jako początkujący Rust pochodzący z C++, wydaje się, że jest to problem, który często pojawiał się ("Mam pewną logikę inicjalizacji stanu złożonego w bloku kodu i chcę odjąć tę logikę i utrzymać stan wynikowy w strukturze do użycia ").
pokrewne Pytanie:Returning iterator of a Vec in a RefCell
Complex inicjalizacji jest powszechne - to zazwyczaj rozwiązane przez wzór Builder; to nie problem tutaj. Iterator plasterka oczekuje, że będzie w stanie utrzymać odniesienie do plasterka przez cały czas jego istnienia; w rzeczywistości [nie można napisać iteratora, który zawiera odniesienia do siebie] (http://stackoverflow.com/questions/25702909/can-i-write-an-iterator-that-yields-a-reference-into-self). – Shepmaster
Możesz dodać metodę 'fn zapożycz (& self) -> Ref>, którą możesz zapisać, a następnie wywołać' iter' na, lub możesz zaakceptować zamknięcie, które jest dostarczane jako iterator: 'fn iter )> (& self, f: F) ' –
Shepmaster
Aby wyjaśnić: moim zamiarem było, aby iterator zwracał wartości, a nie referencje (' i32' w przykładowym kodzie). Moim wyzwaniem było zainicjowanie struktury iteracyjnej, która może wytwarzać wartości przez przechowywanie "Iteratora" dla podstawowego kontenera. W tym przykładzie 'HoldsVecInRefCellIter' może pomieścić tylko' Ref> 'i indeks w wektorze, ale dla innych' Ref > 'Wyobrażam sobie, że chcesz" Iterator "kontenera. –