Attenzione: L'iteratore presentata destra sotto è unsafe
perché permette di ottenere più alias ad un singolo elemento mutevole; salta alla seconda parte per la versione corretta.(Sarebbe bene se il tipo di ritorno contenesse riferimenti immutabili).
Se si è disposti a scrivere l'iteratore della propria finestra, diventa abbastanza semplice.
In primo luogo, l'iteratore in tutti i suoi dettagli scabrosi:
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)
}
}
richiamato su [1, 2, 3]
tornerà (&[], &[1, 2, 3])
poi (&[1], &[2, 3])
, ... fino (&[1, 2, 3], &[])
. In breve, itera su tutte le potenziali partizioni della slice (senza mischiare).
che è sicuro da usare come:
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);
}
Purtroppo può essere utilizzato anche come:
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 _);
}
che quando tiratura: 0x7f73a8435000 0x7f73a8435000
, show-involucro che entrambi i riferimenti mutevoli alias lo stesso elemento.
Dal momento che non è possibile eliminare l'aliasing, è necessario eliminare la mutabilità; o almeno rinviare alla mutabilità interna (Cell
qui dal u8
è Copy
).
Fortunatamente, Cell
non ha costi di esecuzione, ma costa un po 'in termini di ergonomia (tutte quelle .get()
e .set()
).
Colgo l'occasione per rendere l'iteratore leggermente più generico e rinominarlo dal Window
è già un nome usato per un concetto diverso.
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))
}
}
lo usiamo come un mattone edificio:
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 Questo stampa: [1, 5, 3, 10, 5, 15, 7, 17, 9, 22, ...]
, che sembra corretto.
È questo quello che vuoi? https://play.rust-lang.org/?gist=e42f4ff5833f68a56fd4dfe5540992c0&version=stable&backtrace=0 – Adrian
Qualcosa del genere funzionerebbe, sì, grazie. Mi piacerebbe essere il più idiomatico possibile anche se alterando l'elemento in index1, possibilmente permettendo l'iterazione come questa attraverso l'intera lista/vec ecc. Se possibile senza iter di accesso diretto sarebbe buono. Ciò funzionerebbe per la situazione che sto osservando, facile aggiungere un altro accesso diretto e modificare la posizione index1. – dirvine
Ho qualcosa che funziona senza molta indicizzazione [sul box] (https://play.rust-lang.org/?gist=e42f4ff5833f68a56fd4dfe5540992c0&version=stable&backtrace=0) ma è ancora abbastanza dettagliato. Penso che un iteratore basato su 'split_at_mut' sarebbe un mattone da costruzione più interessante (un iteratore che restituisce' (& mut [T], & mut [T]) 'con lo spostamento dei confini ad ogni iterazione). –