Referência mutável no objeto retornado do Iterator
Eu quero criar um Iteratorcapaz de expor itens vizinhos também. Contanto que eu não queira alterar esses itens, é bom e fácil. Mas como fazer uma variante mutável da mesma estrutura?
Imutável:
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 mutável que não funciona devido a tempos de vida:
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],
}
)
}
}
O compilador aponta 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],
| ^^^^^^^^^^^^^^^
|
Respostas
Infelizmente, não há como EnumerateNearestNeighbors2DMutser feito corretamente - é incorreto. Cada vez que você chama, nextobtém &mutreferências que potencialmente se sobrepõem às &mutreferências que foram retornadas da chamada anterior para next. Isso significa que, se funcionasse, estaria violando as regras de referências ao criar aliases &mut.
Este é o mesmo motivo pelo qual existe um std::slice::Windows, mas não WindowsMut(embora ChunksMutseja bom porque os pedaços não se sobrepõem).
Alguns problemas ( aqui está um exemplo ) fornecem mensagens de erro de aparência semelhante, mas são realmente solucionáveis (em alguns casos com unsafe) porque os itens sendo referenciados não se sobrepõem de fato. Essas soluções não funcionam aqui. Se você pudesse escrever um iterador que retornasse referências a si mesmo (um "iterador de streaming"), a API poderia se tornar sólida. No entanto, Iteratornão permite isso.
Aqui estão três opções possíveis para você. Certamente existem outros.
Não permite iteração mutável (com vizinhos). Apenas exponha a iteração por meio da
&referência shared ( ), e se você precisar alterar a grade original, em vez disso, crie uma cópia modificada e troque-a com a original após terminar a iteração. Freqüentemente, é isso que você deseja de qualquer maneira, se estiver escrevendo algo como um filtro de imagem ou um autômato celular em que cada saída depende de várias entradas.Aceite um encerramento e use iteração interna em sua API em vez de iteração externa. Então, em vez de algo assim:
for neighbors in array.enumerate_2d_neighbors_mut() { println!("{}", neighbors.top); }você escreveria algo assim:
array.foreach_2d_neighbors_mut(|neighbors| { println!("{}", neighbors.top); });Nesse caso, as referências aos
arrayitens de são feitas em umforloop dentro doforeach_2d_neighbors_mutmétodo e não escapam dele. Esta API pode ser escrita com bastante facilidade, mesmo semunsafecódigo.Use mutabilidade interior (
Cell,RefCell,Atomic???, etc.) para mutar através de uma&referência em vez de necessitar&mut. Dependendo do que você está fazendo, este pode ser o caminho certo a seguir. Esteja ciente de que você pode usar sorrateiramente Cellpara mutabilidade interior sem ter que alterar o tipo I, quando Ié uma fatia ou vetor . No entanto, esta não seria minha primeira escolha na maioria das vezes.