Ostrzeżenie: iteracyjnej przedstawione tuż poniżej unsafe
, ponieważ pozwala na uzyskanie wielu aliasy jednym zmiennego elementu; przejdź do drugiej części poprawionej wersji.(Byłoby dobrze, gdyby typ powrotu zawierał niezmienne odniesienia).
Jeśli chcesz napisać własny iterator okna, to staje się całkiem łatwe.
pierwsze, iterator we wszystkich swych krwawych szczegółów:
use std::marker::PhantomData;
struct WindowIterMut<'a, T>
where T: 'a
{
begin: *mut T,
len: usize,
index: usize,
_marker: PhantomData<&'a mut [T]>,
}
impl<'a, T> WindowIterMut<'a, T>
where T: 'a
{
pub fn new(slice: &'a mut [T]) -> WindowIterMut<'a, T> {
WindowIterMut {
begin: slice.as_mut_ptr(),
len: slice.len(),
index: 0,
_marker: PhantomData,
}
}
}
impl<'a, T> Iterator for WindowIterMut<'a, T>
where T: 'a
{
type Item = (&'a mut [T], &'a mut [T]);
fn next(&mut self) -> Option<Self::Item> {
if self.index > self.len { return None; }
let slice: &'a mut [T] = unsafe {
std::slice::from_raw_parts_mut(self.begin, self.len)
};
let result = slice.split_at_mut(self.index);
self.index += 1;
Some(result)
}
}
Wykonano [1, 2, 3]
powróci (&[], &[1, 2, 3])
następnie (&[1], &[2, 3])
... aż (&[1, 2, 3], &[])
. W skrócie, iteruje on po wszystkich potencjalnych partycjach plasterka (bez tasowania).
który jest bezpieczny w użyciu jako:
fn main() {
let mut data: Vec<u8> = (1..100).collect();
for (head, tail) in WindowIterMut::new(&mut data) {
if let Some(element) = head.last_mut() {
if *element % 2 == 0 {
if let Some(n3) = tail.iter().filter(|i| *i % 3 == 0).next() {
*element += *n3;
}
}
}
}
println!("{:?}", data);
}
Niestety może to być również używany jako:
fn main() {
let mut data: Vec<u8> = (1..100).collect();
let mut it = WindowIterMut::new(&mut data);
let first_0 = { it.next(); &mut it.next().unwrap().0[0] };
let second_0 = &mut it.next().unwrap().0[0];
println!("{:?} {:?}", first_0 as *const _, second_0 as *const _);
}
który po Nakład: 0x7f73a8435000 0x7f73a8435000
, show-obudowy że obie Zmienne referencje alias taka sama element.
Ponieważ nie możemy pozbyć się aliasingu, musimy pozbyć się zmienności; lub przynajmniej odroczyć wewnętrzną zmienność (Cell
tutaj od u8
jest Copy
).
Na szczęście Cell
nie ma kosztów runtime, ale kosztuje trochę w ergonomii (wszystkie te .get()
i .set()
).
Korzystam z okazji, aby uczynić iterator nieco bardziej uniwersalnym i zmienić jego nazwę, ponieważ Window
jest już nazwą używaną dla innej koncepcji.
struct FingerIter<'a, T>
where T: 'a
{
begin: *const T,
len: usize,
index: usize,
_marker: PhantomData<&'a [T]>,
}
impl<'a, T> FingerIter<'a, T>
where T: 'a
{
pub fn new(slice: &'a [T]) -> FingerIter<'a, T> {
FingerIter {
begin: slice.as_ptr(),
len: slice.len(),
index: 0,
_marker: PhantomData,
}
}
}
impl<'a, T> Iterator for FingerIter<'a, T>
where T: 'a
{
type Item = (&'a [T], &'a T, &'a [T]);
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.len { return None; }
let slice: &'a [T] = unsafe {
std::slice::from_raw_parts(self.begin, self.len)
};
self.index += 1;
let result = slice.split_at(self.index);
Some((&result.0[0..self.index-1], result.0.last().unwrap(), result.1))
}
}
Używamy go jako murowany budynek:
fn main() {
let data: Vec<Cell<u8>> = (1..100).map(|i| Cell::new(i)).collect();
for (_, element, tail) in FingerIter::new(&data) {
if element.get() % 2 == 0 {
if let Some(n3) = tail.iter().filter(|i| i.get() % 3 == 0).next() {
element.set(element.get() + n3.get());
}
}
}
let data: Vec<u8> = data.iter().map(|cell| cell.get()).collect();
println!("{:?}", data);
}
On the playpen to wydruki: [1, 5, 3, 10, 5, 15, 7, 17, 9, 22, ...]
, co wydaje się prawidłowe.
Czy tego chcesz? https://play.rust-lang.org/?gist=e42f4ff5833f68a56fd4dfe5540992c0&version=stable&backtrace=0 – Adrian
Coś takiego by działało, tak, dzięki. Chciałbym być tak idiotyczny, jak to tylko możliwe, chociaż zmieniając pozycję w indeksie 1, prawdopodobnie umożliwiając iterację w ten sposób na całej liście/vec itp. Nice though. Jeśli to możliwe bez bezpośredniego dostępu, to byłoby dobrze. To zadziałałoby dla sytuacji, na którą patrzę, łatwo dodać kolejny bezpośredni dostęp i zmienić pozycję indeksu1. – dirvine
Mam coś pracy bez dużego indeksowania [na kojec] (https://play.rust-lang.org/?gist=e42f4ff5833f68a56fd4dfe5540992c0&version=stable&backtrace=0), ale nadal jest dość gadatliwy. Myślę, że iterator oparty na 'split_at_mut' byłby bardziej interesującą cegłą budynku (iterator, który daje' (& mut [T], i mut [T]) 'z przesunięciem granicy w każdej iteracji). –