Référence mutable dans l'objet renvoyé par Iterator
Je souhaite également créer un Iteratoroutil capable d'exposer les éléments voisins. Tant que je ne veux pas changer également ces éléments, c'est bien et facile. Mais comment faire une variante mutable de la même structure?
Immuable:
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 mutable qui ne fonctionne pas en raison de la durée de vie:
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],
}
)
}
}
Le compilateur souligne que:
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],
| ^^^^^^^^^^^^^^^
|
Réponses
Malheureusement, il n'y a aucun moyen de EnumerateNearestNeighbors2DMutle faire correctement - ce n'est pas sain. Chaque fois que vous appelez, nextvous obtenez des &mutréférences qui se chevauchent potentiellement avec des &mutréférences renvoyées par l'appel précédent à next. Cela signifie que si cela fonctionnait, cela violerait les règles de références en créant des alias &mut.
C'est la même raison pour laquelle il y a un std::slice::Windows, mais non WindowsMut(bien que ce ChunksMutsoit bien parce que les morceaux ne se chevauchent pas).
Certains problèmes (en voici un exemple ) donnent des messages d'erreur similaires, mais peuvent en fait être résolus (dans certains cas avec unsafe) car les éléments référencés ne se chevauchent pas. Ces solutions ne fonctionnent pas ici. Si vous pouviez écrire un itérateur qui renvoie des références à lui-même (un "itérateur de streaming"), l'API pourrait être rendue sonore. Cependant, Iteratorcela ne permet pas.
Voici trois options possibles pour vous. Il y en a certainement d'autres.
N'autorisez pas du tout l'itération mutable (avec des voisins). Exposez simplement l'itération via une
&référence shared ( ), et si vous avez besoin de muter la grille d'origine, créez plutôt une copie modifiée et échangez-la avec l'original une fois l'itération terminée. C'est souvent ce que vous voulez de toute façon, si vous écrivez quelque chose comme un filtre d'image ou un automate cellulaire où chaque sortie dépend de plusieurs entrées.Acceptez une fermeture et utilisez une itération interne dans votre API au lieu d'une itération externe. Donc, au lieu de quelque chose comme ça:
for neighbors in array.enumerate_2d_neighbors_mut() { println!("{}", neighbors.top); }vous écririez quelque chose comme ceci:
array.foreach_2d_neighbors_mut(|neighbors| { println!("{}", neighbors.top); });Dans ce cas, les références aux
arrayéléments de sont prises dans uneforboucle à l'intérieur de laforeach_2d_neighbors_mutméthode et ne lui échappent pas. Cette API peut être écrite assez facilement, même sansunsafecode.Utilisez mutabilité intérieur (
Cell,RefCell,Atomic???, etc.) à muter par une&référence au lieu d' avoir besoin&mut. Selon ce que vous faites, cela pourrait être la bonne voie à suivre. Sachez que vous pouvez utiliser sournoisement Cellpour la mutabilité intérieure sans avoir à changer de type I, quand Iest une tranche ou un vecteur . Cependant, ce ne serait pas mon premier choix la plupart du temps.