iter와 into_iter의 차이점은 무엇입니까?


175

이 코드 스 니펫이 있는 Rust by Example 튜토리얼을하고 있습니다.

// Vec example
let vec1 = vec![1, 2, 3];
let vec2 = vec![4, 5, 6];

// `iter()` for vecs yields `&i32`. Destructure to `i32`.
println!("2 in vec1: {}", vec1.iter()     .any(|&x| x == 2));
// `into_iter()` for vecs yields `i32`. No destructuring required.
println!("2 in vec2: {}", vec2.into_iter().any(| x| x == 2));

// Array example
let array1 = [1, 2, 3];
let array2 = [4, 5, 6];

// `iter()` for arrays yields `&i32`.
println!("2 in array1: {}", array1.iter()     .any(|&x| x == 2));
// `into_iter()` for arrays unusually yields `&i32`.
println!("2 in array2: {}", array2.into_iter().any(|&x| x == 2));

수율 참조에서 반환 된 반복자와 수율 값 에서 Vec반환 된 반복자가 완전히 혼란 스럽지만 배열의 경우이 반복자는 동일합니까?iterinto_iter

이 두 가지 방법의 사용 사례 / API는 무엇입니까?

답변:


147

TL; DR :

  • 반복자에 의해 반환 된 into_iter임의의 수를 생성 T, &T또는 &mut T상황에 따라.
  • 에 의해 반환 된 이터레이터는 규칙에 따라 iteryield를 산출 &T합니다.
  • 에 의해 반환 된 이터레이터는 규칙에 따라 iter_mutyield를 산출 &mut T합니다.

첫 번째 질문은 "무엇입니까 into_iter?"

into_iterIntoIterator특성 에서 온다 :

pub trait IntoIterator 
where
    <Self::IntoIter as Iterator>::Item == Self::Item, 
{
    type Item;
    type IntoIter: Iterator;
    fn into_iter(self) -> Self::IntoIter;
}

특정 유형을 반복자로 변환하는 방법을 지정하려는 경우이 특성을 구현합니다. 특히 유형이 구현 IntoIterator하면 for루프 에서 사용할 수 있습니다 .

예를 들어 ... 3을 Vec구현합니다 IntoIterator!

impl<T> IntoIterator for Vec<T>
impl<'a, T> IntoIterator for &'a Vec<T>
impl<'a, T> IntoIterator for &'a mut Vec<T>

각 변형은 약간 다릅니다.

이것은 소비 Vec하고 반복자는 값을 산출 합니다 ( T직접).

impl<T> IntoIterator for Vec<T> {
    type Item = T;
    type IntoIter = IntoIter<T>;

    fn into_iter(mut self) -> IntoIter<T> { /* ... */ }
}

다른 두 개는 벡터를 참조로 사용하고 ( 두 경우 모두 참조 into_iter(self)이므로 서명에 속지 마십시오 self) 반복자는 내부 요소에 대한 참조를 생성 Vec합니다.

이것은 불변의 참조를 산출합니다 .

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> { /* ... */ }
}

이것은 가변 참조를 생성 하지만 :

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> { /* ... */ }
}

그래서:

차이점은 무엇이며 iter그리고 into_iter?

into_iter이 이터레이터가 값을 산출하는지, 불변의 참조 또는 변경 가능한 참조 가 컨텍스트에 의존 하고 때로는 놀랍지 않은지 반복자를 얻는 일반적인 방법 입니다.

iteriter_mut임시 방법입니다. 따라서 반환 유형은 컨텍스트와 무관하며 일반적으로 각각 불변 참조 및 변경 가능한 참조를 생성하는 반복자입니다.

Rust by Example 게시물의 저자 into_iter는 호출 되는 컨텍스트 (예 : 유형)에 대한 의존성에서 오는 놀라움을 설명하고 다음 과 같은 사실을 사용하여 문제를 복잡하게 만듭니다.

  1. IntoIterator[T; N]에 대해서만 구현되지 않으며 &[T; N]&mut [T; N]
  2. 메소드가 값에 대해 구현되지 않으면 대신 해당 값에 대한 참조 를 자동으로 검색 합니다.

into_iter모든 유형 (제외 [T; N])이 3 가지 변형 (값 및 참조)에 대해 구현 하기 때문에 매우 놀랍습니다 . 배열이 항목을 포기하기 위해 "축소"할 수 없기 때문에 값을 생성하는 반복자를 구현할 수 없습니다.

왜 배열이 IntoIterator(놀라운 방식으로) 구현되는지에 관해서 는 for루프 에서 배열 에 대한 참조를 반복 할 수 있습니다.


14
이 블로그 게시물이 도움이되었다고 생각했습니다 : hermanradtke.com/2015/06/22/…
poy

>이 반복자가 값을 생성하는지, 불변 참조 또는 변경 가능한 참조가 상황에 따라 달라지는가? 예를 들어 iter_mut가 변경 가능한 값을 생성하도록하려면 어떻게해야합니까?
Dan M.

@DanM .: (1) into_iter수신자가 값, 참조 또는 변경 가능한 참조인지에 따라 구현 을 선택 함을 의미합니다 . (2) Rust에는 변경 가능한 값이 없거나 소유권이 있기 때문에 모든 값을 변경할 수 있습니다.
Matthieu M.

@ MatthieuM.hm, 그건 내 테스트의 경우가 아닌 것 같습니다. 나는에 대한 IntoIter을 구현했습니다 &'a MyStruct그리고 &mut 'a MyStruct내가 전화해도 존재하는 경우 첫 번째는 항상 선택되었다 into_iter().for_each()mut와 값 &mut람다의 인수.
Dan M.

1
@Ixx : 매우 유용합니다. 중간에 답을 묻지 않기 위해 질문의 맨 위에 TL; DR을 제공하기로 결정했습니다. 어떻게 생각하십니까?
Matthieu M.

78

나는 (Rust 초보자) 다른 답변으로는 제공되지 않은 간단한 답변을 찾기 위해 Google에서 왔습니다. 그 간단한 대답은 다음과 같습니다.

  • iter() 참조로 항목을 반복
  • into_iter() 항목을 반복하여 새 범위로 이동
  • iter_mut() 항목을 반복하여 각 항목에 대한 변경 가능한 참조를 제공합니다.

그래서 for x in my_vec { ... }본질적으로 동일하다 my_vec.into_iter().for_each(|x| ... )모두 - move의 요소 my_vec...범위.

데이터를 "보아야" iter할 필요가있는 경우을 사용하고 , 데이터 를 편집 / 변경해야하는 경우을 사용하고을 사용 iter_mut하고 새 소유자에게 제공해야하는 경우을 사용하십시오 into_iter.

도움이되었습니다 : http://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html

내가 실수 한 경우 Rust 전문가 가이 답변을 편집 할 수 있도록 이것을 커뮤니티 위키로 만듭니다.


7
감사합니다 ... 허용 된 답변이와 iter와 (과) 의 차이점을 어떻게 나타내는 지 알기가 어렵습니다 into_iter.
mmw

바로 내가 찾던 것입니다!
Cyrusmith

6

.into_iter()배열 자체에 대해서는 구현되지 않고 만 구현됩니다 &[]. 비교:

impl<'a, T> IntoIterator for &'a [T]
    type Item = &'a T

impl<T> IntoIterator for Vec<T>
    type Item = T

이후 IntoIterator에서만 정의 &[T], 슬라이스 자체와 같은 방법으로 제거 될 수 없습니다 Vec당신이 값을 사용할 때를. (값은 이동할 수 없습니다)

자, 왜 그런 경우가 다른 문제인지, 저는 스스로 배우고 싶습니다. 추측 : 배열은 데이터 자체이며 슬라이스는보기 일뿐입니다. 실제로 배열을 다른 함수로 값으로 옮길 수 없으며 뷰를 전달하기 만하면 거기서도 소비 할 수 없습니다.


IntoIterator또한 구현되어 &'a mut [T]이 때문에, 배열에서 객체를 이동합니다. 리턴 구조체 IntoIter<T>에는 평생 인수 Iter<'a, T>가 없으므로 전자는 슬라이스를 보유 할 수 없다는 사실과 관련이 있다고 생각합니다 .
rodrigo

mut값을 옮길 수있는 것이 아니라 변경할 수 있음을 의미합니다.
viraptor

@rodrigo let mut a = ["abc".to_string()]; a.into_iter().map(|x| { *x });=> "오류 : 빌린 내용 밖으로 이동할 수 없습니다"
viraptor

네, 당신이 옳고 값을 배열에서 이동할 수 없다고 생각합니다. 그러나 여전히 ArrayIntoIter라이브러리의 일부로 안전하지 않은 Rust를 사용하여 일종의 구조체 를 구현하는 것이 가능해야한다고 생각 Vec합니다. 어쨌든 그 경우에 사용해야하므로 가치가 없을 수도 있습니다.
rodrigo

그래서 이해가 안됩니다 ... array.into_iter반환 하는 이유 는 &T-자동으로 변환하는 것이 마술이기 때문에 &array.into_iter-값이 움직이거나 움직이지 않는 것이 무엇인지 이해하지 못하기 때문입니다. 또는 @rodrigo가 말했듯이, (어떤 이유로 든) 값을 배열 밖으로 이동할 수 없기 때문에 단순히 참조를 얻는다는 것 입니까? 여전히 매우 혼란 스럽다.
vitiral

2

좀 더 명확히 할 것이 있다고 생각합니다. 컬렉션과 같은 종류의, Vec<T>그리고 VecDeque<T>,이 into_iter수익률이 있다는 방법 T들이 구현 때문에를 IntoIterator<Item=T>. Foo<T>반복되는 유형을 작성하는 것을 막을 수있는 것은 없습니다 . T다른 유형이 아닙니다 U. 즉,를 Foo<T>구현 IntoIterator<Item=U>합니다.

사실, 몇 가지 예제가 있습니다 std: &Path 구현 IntoIterator<Item=&OsStr>&UnixListener 구현합니다 IntoIterator<Item=Result<UnixStream>> .


차이 into_iteriter

의 차이에 원래의 질문으로 돌아 가기 into_iteriter. 다른 사람들이 지적한 것과 마찬가지로 차이점은에 지정된 모든 유형을 생성 할 수 into_iter있는 필수 방법입니다 . 일반적으로, 타입 구현하는 경우 그것은 또한 두 개의 임시 방법을이 규칙에 따라, : 그리고 어떤 수율 및 각각.IntoIteratorIntoIterator::ItemIntoIterator<Item=I>iteriter_mut&I&mut I

의미 into_iter하는 것은 특성 바인딩을 사용하여 메소드 가있는 유형 (즉 반복 가능) 을받는 함수를 만들 수 있다는 것입니다.

fn process_iterable<I: IntoIterator>(iterable: I) {
    for item in iterable {
        // ...
    }
}

그러나, 우리는 할 수없는 * 가지고 유형을 요구하는 바인딩 특성 사용 iter방법 또는 iter_mut그들은 단지 규칙이기 때문에, 방법. 또는 into_iter보다 더 널리 사용할 수 있다고 말할 수 있습니다 .iteriter_mut

대안 iteriter_mut

관찰해야 할 또 다른 흥미로운 점 iter은 반복자를 얻는 유일한 방법은 아닙니다 &T. 관례에 따르면, 메소드 SomeCollection<T>std있는 콜렉션 유형 iter에도 불변의 참조 유형이 &SomeCollection<T>구현 IntoIterator<Item=&T>됩니다. 예를 들어 &Vec<T> implements IntoIterator<Item=&T> 이므로 다음을 반복 할 수 있습니다 &Vec<T>.

let v = vec![1, 2];

// Below is equivalent to: `for item in v.iter() {`
for item in &v {
    println!("{}", item);
}

경우 v.iter()에 해당합니다 &v구현하는 두 점에서 IntoIterator<Item=&T>, 왜 다음 녹 모두를 제공합니까? 인체 공학적입니다. for루프 에서는 사용하기 &v보다 약간 더 간결합니다 v.iter(). 그러나 다른 경우에는 다음 v.iter()보다 훨씬 명확합니다 (&v).into_iter().

let v = vec![1, 2];

let a: Vec<i32> = v.iter().map(|x| x * x).collect();
// Although above and below are equivalent, above is a lot clearer than below.
let b: Vec<i32> = (&v).into_iter().map(|x| x * x).collect();

마찬가지로 for루프에서 다음 v.iter_mut()&mut v같이 바꿀 수 있습니다 .

let mut v = vec![1, 2];

// Below is equivalent to: `for item in v.iter_mut() {`
for item in &mut v {
    *item *= 2;
}

유형에 대한 제공 (구현) into_iteriter방법

유형에 반복 할 "way"가 하나만 있으면 두 가지를 모두 구현해야합니다. 그러나 두 가지 이상의 방법을 반복 할 수있는 경우 각 방법마다 임시 방법을 제공해야합니다.

예를 들어, String도 제공 into_iter이나 iter문자의 표현 바이트 또는 반복의 표현을 반복 : 반복하는 두 가지 방법이 있기 때문에 그것. 대신, 메소드의 대안으로 bytes바이트 chars반복 및 문자 반복을 위한 두 가지 방법을 제공합니다 iter.


* 기술적으로 특성을 만들어서 할 수 있습니다. 그러나 우리는 우리가 impl사용하고자하는 각 유형마다 그 특성이 필요 합니다. 한편 많은 유형의 std이미 구현되어 IntoIterator있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.