TL; DR:
- A iteração retornado por
into_iter
podem produzir qualquer um de T
, &T
ou &mut T
, dependendo do contexto.
- O iterador retornado por
iter
produzirá &T
, por convenção.
- O iterador retornado por
iter_mut
produzirá &mut T
, por convenção.
A primeira pergunta é: "O que é into_iter
?"
into_iter
vem da IntoIterator
característica :
pub trait IntoIterator
where
<Self::IntoIter as Iterator>::Item == Self::Item,
{
type Item;
type IntoIter: Iterator;
fn into_iter(self) -> Self::IntoIter;
}
Você implementa essa característica quando deseja especificar como um tipo específico deve ser convertido em um iterador. Mais notavelmente, se um tipo implementa, IntoIterator
ele pode ser usado em um for
loop.
Por exemplo, Vec
implementa IntoIterator
... três vezes!
impl<T> IntoIterator for Vec<T>
impl<'a, T> IntoIterator for &'a Vec<T>
impl<'a, T> IntoIterator for &'a mut Vec<T>
Cada variante é um pouco diferente.
Este consome Vec
e seu iterador gera valores ( T
diretamente):
impl<T> IntoIterator for Vec<T> {
type Item = T;
type IntoIter = IntoIter<T>;
fn into_iter(mut self) -> IntoIter<T> { /* ... */ }
}
Os outros dois tomam o vetor por referência (não se deixe enganar pela assinatura de into_iter(self)
porque self
é uma referência nos dois casos) e seus iteradores produzirão referências aos elementos internos Vec
.
Este produz referências imutáveis :
impl<'a, T> IntoIterator for &'a Vec<T> {
type Item = &'a T;
type IntoIter = slice::Iter<'a, T>;
fn into_iter(self) -> slice::Iter<'a, T> { /* ... */ }
}
Enquanto este produz referências mutáveis :
impl<'a, T> IntoIterator for &'a mut Vec<T> {
type Item = &'a mut T;
type IntoIter = slice::IterMut<'a, T>;
fn into_iter(self) -> slice::IterMut<'a, T> { /* ... */ }
}
Assim:
Qual é a diferença entre iter
e into_iter
?
into_iter
é um método genérico para obter um iterador, se esse iterador gera valores, referências imutáveis ou referências mutáveis depende do contexto e às vezes pode ser surpreendente.
iter
e iter_mut
são métodos ad-hoc. Seu tipo de retorno é, portanto, independente do contexto e será convencionalmente iteradores que produzem referências imutáveis e referências mutáveis, respectivamente.
O autor do post Rust by Example ilustra a surpresa que vem da dependência do contexto (ou seja, o tipo) no qual into_iter
é chamado e também está agravando o problema usando o fato de que:
IntoIterator
não é implementado para [T; N]
, apenas para &[T; N]
e&mut [T; N]
- Quando um método não é implementado para um valor, ele é automaticamente procurado por referências a esse valor.
o que é muito surpreendente, into_iter
pois todos os tipos (exceto [T; N]
) o implementam nas três variações (valor e referências). Não é possível para a matriz implementar um iterador que gera valores porque não pode "encolher" para abrir mão de seus itens.
Por que as matrizes são implementadas IntoIterator
(de maneira tão surpreendente): é para possibilitar a iteração das referências a elas em for
loops.
into_iter
escolhe uma implementação com base no fato de o receptor ser um valor, referência ou referência mutável. (2) Não há valores mutáveis em Rust, ou melhor, qualquer valor é mutável, pois você é o proprietário.&'a MyStruct
e&mut 'a MyStruct
e o primeiro foi escolhido sempre se presente mesmo se eu chamadainto_iter().for_each()
emmut
valor, com&mut
argumentos em lambda.Eu (um novato da Rust) vim aqui do Google buscando uma resposta simples, que não foi fornecida pelas outras respostas. Aqui está essa resposta simples:
iter()
itera sobre os itens por referênciainto_iter()
itera sobre os itens, movendo-os para o novo escopoiter_mut()
itera sobre os itens, fornecendo uma referência mutável para cada itemEntão
for x in my_vec { ... }
é essencialmente equivalente amy_vec.into_iter().for_each(|x| ... )
- ambosmove
os elementosmy_vec
para o...
escopo.Se você apenas precisar "examinar" os dados, use
iter
, se precisar editá-los / modificá-los, useiter_mut
e, se precisar dar a um novo proprietário, useinto_iter
.Isso foi útil: http://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html
Tornando este um wiki da comunidade para que, com sorte, um profissional do Rust possa editar essa resposta se eu cometer algum erro.
fonte
iter
einto_iter
..into_iter()
não é implementado para uma matriz em si, mas apenas&[]
. Comparar:com
Como
IntoIterator
está definido apenas em&[T]
, a fatia em si não pode ser descartada da mesma maneira queVec
quando você usa os valores. (os valores não podem ser removidos)Agora, por que esse é o caso é uma questão diferente, e eu gostaria de aprender. Especulação: array são os dados em si, fatia é apenas uma visão dele. Na prática, você não pode mover a matriz como um valor para outra função, basta passar uma visão dela, para que você também não possa consumi-la.
fonte
IntoIterator
também é implementado para&'a mut [T]
, para que ele possa mover os objetos para fora da matriz. Eu acho que isso está relacionado ao fato de que a estrutura de retornoIntoIter<T>
não tem um argumento vitalício enquanto oIter<'a, T>
possui, portanto o primeiro não pode conter uma fatia.mut
significa que você pode alterar os valores, não que você pode movê-los.let mut a = ["abc".to_string()]; a.into_iter().map(|x| { *x });
=> "erro: não é possível sair do conteúdo emprestado"ArrayIntoIter
estrutura usando o Rust inseguro, como parte da biblioteca ... Talvez não valha a pena, pois você deve usarVec
nesses casos de qualquer maneira.array.into_iter
retorna&T
- porque está fazendo mágica para convertê-lo automaticamente em&array.into_iter
- e, nesse caso, não entendo o que isso tem a ver com valores em movimento ou não em valores. Ou é como @rodrigo disse, que você obtém a referência simplesmente porque (por algum motivo) você não pode mover valores das matrizes ? Ainda muito confuso.Eu acho que há algo para esclarecer um pouco mais. Tipos de coleção, como
Vec<T>
eVecDeque<T>
, têm uminto_iter
método que geraT
porque eles implementamIntoIterator<Item=T>
. Não há nada para nos impedir de criar um tipo,Foo<T>
se for iterado, ele produzirá nãoT
apenas outro tipoU
. Ou seja,Foo<T>
implementaIntoIterator<Item=U>
.De fato, existem alguns exemplos em
std
:&Path
implementosIntoIterator<Item=&OsStr>
e&UnixListener
implementosIntoIterator<Item=Result<UnixStream>>
.A diferença entre
into_iter
eiter
Voltar à pergunta original sobre a diferença entre
into_iter
eiter
. Semelhante ao que outros já apontaram, a diferença é queinto_iter
é um método exigidoIntoIterator
que pode gerar qualquer tipo especificado emIntoIterator::Item
. Normalmente, se um tipo é implementadoIntoIterator<Item=I>
, por convenção, ele também possui dois métodos ad-hoc:iter
eiter_mut
que produzem&I
e&mut I
, respectivamente.O que isso implica é que podemos criar uma função que recebe um tipo que possui
into_iter
método (ou seja, é iterável) usando um atributo vinculado:No entanto, não podemos * usar uma característica vinculada para exigir que um tipo tenha
iter
método ouiter_mut
método, porque são apenas convenções. Podemos dizer queinto_iter
é mais amplamente utilizável queiter
ouiter_mut
.Alternativas
iter
eiter_mut
Outro interessante a observar é que
iter
não é a única maneira de obter um iterador que produza&T
. Por convenção (novamente), os tipos de coleçãoSomeCollection<T>
nosstd
quais oiter
método também tem seus tipos de referência imutáveis&SomeCollection<T>
implementadosIntoIterator<Item=&T>
. Por exemplo,&Vec<T>
implementaIntoIterator<Item=&T>
, portanto, permite iterar sobre&Vec<T>
:Se
v.iter()
é equivalente a&v
que ambos implementamIntoIterator<Item=&T>
, por que então Rust fornece ambos? É para ergonomia. Emfor
loops, é um pouco mais conciso do&v
quev.iter()
; mas em outros casos,v.iter()
é muito mais claro que(&v).into_iter()
:Da mesma forma, em
for
loops,v.iter_mut()
pode ser substituído por&mut v
:Quando fornecer (implementar)
into_iter
eiter
métodos para um tipoSe o tipo tiver apenas uma "maneira" de ser repetida, devemos implementar as duas. No entanto, se há duas maneiras ou mais para iterar, devemos fornecer um método ad-hoc para cada maneira.
Por exemplo,
String
fornece neminto_iter
nemiter
porque existem duas maneiras de iterá-lo: iterar sua representação em bytes ou iterar sua representação em caracteres. Em vez disso, fornece dois métodos:bytes
para iterar os bytes echars
para iterar os caracteres, como alternativas aoiter
método.* Bem, tecnicamente, podemos fazer isso criando uma característica. Mas então precisamos
impl
dessa característica para cada tipo que queremos usar. Enquanto isso, muitos tiposstd
já implementamIntoIterator
.fonte