Pelo que entendi, não deveria ser possível ter uma referência mutável a um dado imutável. Dessa forma, se eu quiser criar alguns struct
que implementem Iterator
com base na mutação de um estado interno, terei que declarar qualquer instância disso struct
como mutável para poder criar as referências mutáveis necessárias.
Para demonstrar, construí um exemplo simples, abaixo:
struct MyIter {
internal_state: u32
}
impl MyIter {
fn new() -> MyIter {
MyIter{ internal_state: 0 }
}
}
impl Iterator for MyIter {
type Item = u32;
fn next(&mut self) -> Option<u32> {
self.internal_state += 1;
Some(self.internal_state)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::iter::zip;
#[test]
fn mutable_declaration_no_zip() {
let mut my_iter = MyIter::new();
assert_eq!(my_iter.next(), Some(1));
assert_eq!(my_iter.next(), Some(2));
assert_eq!(my_iter.next(), Some(3));
}
}
Isso compila bem e passa, enquanto se eu alterar a função de teste para ter uma declaração imutável for my_iter
, como abaixo, ela se recusará a compilar com a mensagem esperada de error[E0596]: cannot borrow 'my_iter' as mutable, as it is not declared as mutable
:
#[test]
fn immutable_declartaion_no_zip() {
let my_iter = MyIter::new();
assert_eq!(my_iter.next(), Some(1));
assert_eq!(my_iter.next(), Some(2));
assert_eq!(my_iter.next(), Some(3));
}
Minha pergunta diz respeito ao caso em que faço basicamente o mesmo que acima, mas uso std::iter::zip
para realizar a iteração. O que acontece agora é que mesmo que eu declare my_iter
como imutável, o teste é compilado e aprovado, o que significa que nos bastidores zip
houve uma mutação no estado da variável imutável na qual passei!
#[test]
fn immutable_declaration_zip() {
let my_iter = MyIter::new();
let nums: [u32;10] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
for (i, num) in zip(my_iter, nums.iter()) {
assert_eq!(i, *num);
}
}
Então, alguém poderia explicar por que zip
foi configurado para poder fazer isso? Como posso zip
chamar o next()
método e, portanto, gerar referências mutáveis para dados que declarei como imutáveis?
As anotações de mutabilidade (
mut
ou a falta delas) seguem a variável, não o valor.Aqui, não é o valor que
my_iter
é mutável. Émy_iter
ele mesmo. Contanto quemy_iter
possua esse valor, é mutável. Então você pode ligarmy_iter.next()
, o que requer uma referência mutável.O mesmo argumento, mas sem o
mut
. Contanto quemy_iter
possua o iterador, ele é imutável , portanto você não tem permissão para fazer uma referência mutável a ele.No entanto, a frase-chave aqui é "Contanto que
my_iter
possua o iterador".A assinatura é
std::iter::zip
_Leva ambos os argumentos por value . No momento em que você chama
zip
, essa função assume a propriedade do valor. Isso significa que atualmente ninguém tem uma referência (mutável ou imutável) ao valor ezip
é livre para decidir se é mutável ou não, já que agora é o proprietário exclusivo.