Riferimento modificabile nell'oggetto restituito da Iterator
Voglio creare un in Iterator
grado di esporre anche gli oggetti vicini. Finché non voglio cambiare anche questi elementi, va bene e facile. Ma come realizzare una variante mutevole della stessa struttura?
Immutabile:
struct NearestNeighbours2D<'a, T> {
mid: &'a T,
left: &'a T,
right: &'a T,
top: &'a T,
bot: &'a T,
}
struct EnumerateNearestNeighbours2D<'a, I>
where I: std::ops::Index<usize> {
x: usize,
y: usize,
width: usize,
height: usize,
inner: &'a I
}
impl<'a, I: std::ops::Index<usize>> Iterator for EnumerateNearestNeighbours2D<'a, I>
where <I as std::ops::Index<usize>>::Output: std::marker::Sized {
type Item = NearestNeighbours2D<'a, I::Output>;
fn next(&mut self) -> std::option::Option<<Self as std::iter::Iterator>::Item> {
let (top, left, mid, right, bot) = (
(self.y - 1) * self.width + self.x,
self.y * self.width + self.x - 1,
self.y * self.width + self.x,
self.y * self.width + self.x + 1,
(self.y + 1) * self.width + self.x,
);
Some(
NearestNeighbours2D {
mid: &self.inner[mid],
left: &self.inner[left],
right: &self.inner[right],
top: &self.inner[top],
bot: &self.inner[bot],
}
)
}
}
Variante mutevole che non funziona a causa della durata:
struct NearestNeighbours2DMut<'a, T> {
mid: &'a mut T,
left: &'a mut T,
right: &'a mut T,
top: &'a mut T,
bot: &'a mut T,
}
struct EnumerateNearestNeighbours2DMut<'a, I>
where I: std::ops::IndexMut<usize> {
x: usize,
y: usize,
width: usize,
height: usize,
inner: &'a mut I
}
impl<'a, I: std::ops::IndexMut<usize>> Iterator for EnumerateNearestNeighbours2DMut<'a, I>
where <I as std::ops::Index<usize>>::Output: std::marker::Sized {
type Item = NearestNeighbours2DMut<'a, I::Output>;
fn next(&mut self) -> std::option::Option<<Self as std::iter::Iterator>::Item> {
let (top, left, mid, right, bot) = (
(self.y - 1) * self.width + self.x,
self.y * self.width + self.x - 1,
self.y * self.width + self.x,
self.y * self.width + self.x + 1,
(self.y + 1) * self.width + self.x,
);
Some(
NearestNeighbours2DMut {
mid: &mut self.inner[mid],
left: &mut self.inner[left],
right: &mut self.inner[right],
top: &mut self.inner[top],
bot: &mut self.inner[bot],
}
)
}
}
Il compilatore sottolinea che:
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements
--> src\lib.rs:99:27
|
99 | mid: &mut self.inner[mid],
| ^^^^^^^^^^^^^^^
|
Risposte
Sfortunatamente, non è EnumerateNearestNeighbors2DMut
possibile farlo correttamente: non è corretto. Ogni volta che si chiama next
si ottengono &mut
riferimenti che potenzialmente si sovrappongono a &mut
riferimenti restituiti dalla chiamata precedente a next
. Ciò significa che, se funzionasse, violerebbe le regole dei riferimenti creando &mut
s alias .
Questo è lo stesso motivo per cui è presente un std::slice::Windows, ma no WindowsMut
(anche se ChunksMut
va bene perché i blocchi non si sovrappongono).
Alcuni problemi ( ecco un esempio ) danno messaggi di errore simili, ma sono effettivamente risolvibili (in alcuni casi con unsafe
) perché gli elementi a cui si fa riferimento non sono in realtà sovrapposti. Quelle soluzioni non funzionano qui. Se si potesse scrivere un iteratore che restituisce riferimenti a se stesso (un "iteratore di streaming"), l'API potrebbe essere resa sonora. Tuttavia, Iterator
non lo consente.
Ecco tre possibili opzioni per te. Ce ne sono sicuramente altri.
Non consentire affatto l'iterazione mutevole (con i vicini). Esponi semplicemente l'iterazione tramite il
&
riferimento shared ( ) e, se devi cambiare la griglia originale, crea invece una copia modificata e scambiala con l'originale dopo aver finito di iterare. Questo è spesso quello che vuoi comunque, se stai scrivendo qualcosa come un filtro di immagine o un automa cellulare in cui ogni output dipende da più input.Accetta una chiusura e utilizza l'iterazione interna nella tua API anziché l'iterazione esterna. Quindi, invece di qualcosa di simile:
for neighbors in array.enumerate_2d_neighbors_mut() { println!("{}", neighbors.top); }
scriveresti qualcosa del genere:
array.foreach_2d_neighbors_mut(|neighbors| { println!("{}", neighbors.top); });
In questo caso i riferimenti agli
array
elementi di vengono presi in unfor
ciclo all'interno delforeach_2d_neighbors_mut
metodo e non ne escono. Questa API può essere scritta abbastanza facilmente, anche senzaunsafe
codice.Utilizzare mutabilità interno (
Cell
,RefCell
,Atomic???
, ecc) per mutare attraverso un&
riferimento invece di aver bisogno&mut
. A seconda di cosa stai facendo, questa potrebbe essere la strada giusta da percorrere. Tieni presente che puoi usarlo di nascosto Cellper la mutabilità interna senza dover cambiare il tipo I, quando Iè una fetta o un vettore . Tuttavia questa non sarebbe la mia prima scelta la maggior parte delle volte.