collect 함수를 이해하려면, 이 함수가 이터레이터를 처리해 결과를 만들어 내는 데 쓰인다는 점을 먼저 알아두는 게 중요해요. 이터레이터는 일련의 항목으로, 다른 이터레이터와 연결할 수 있어요. 이터레이터는 게으르게 동작해요. 즉, 이터레이터를 소비하는 코드를 호출하기 전까지는 실행되지 않아요. 그런 코드로는 for문과 소비 어댑터가 있어요. 소비 어댑터는 이터레이터의 모든 항목을 처리해서 어떤 형태의 출력을 만들어 내요.
collect도 그런 소비 어댑터 중 하나로, FromIterator 트레이트를 구현하는 어떤 타입의 인스턴스든 만들어 낼 수 있어요. 이 트레이트는 표준 라이브러리의 모든 컬렉션에 구현되어 있어요. FromIterator는 여러 타입에 구현되어 있기 때문에, 어떤 종류의 컬렉션을 원하는지 Rust에게 알려줘야 해요. 예를 들어 문자열 슬라이스에서 Vec<char>를 얻고 싶다면, 다음과 같이 collect 호출에 타입을 끼워 넣을 수 있어요.
let my_chars = "Hello, World!".chars().collect::<Vec<char>>();
// since the item type of the `String::chars()` iterator can be inferred to be `char`, we can omit that information
// let my_chars = "Hello, World!".chars().collect::<Vec<_>>();
이중 콜론과 바깥쪽 꺾쇠괄호(즉 ::<_>)를 turbofish라고 불러요. 이 경우 터보피시는 필요 이상으로 번잡한 문법이에요. 바인딩 자체에 타입을 지정하면, collect가 만들어야 할 컬렉션을 더 적은 문법적 잡음으로 추론할 수 있어요.
let my_chars: Vec<char> = "Hello, World!".chars().collect();
// or
// let my_chars: Vec<_> = "Hello, World!".chars().collect();
문자가 뒤집힌 String을 원한다면 이렇게 하면 돼요.
let rev_chars: String = "Hello, World!".chars().rev().collect();
Rust의 타입 추론은 충분히 강력해서, 때로는 명시적인 인라인 표기가 필요 없을 때도 있어요.
fn get_reversed_chars(phrase: &str) -> Vec<char> {
phrase.chars().rev().collect()
}
get_reversed_chars 함수의 반환 타입 덕분에 collect 함수는 어떤 컬렉션을 반환할지 추론할 수 있어요. 다만 여기서는 Vec<_>가 아니라 Vec<char>를 명시해야 해요. Rust는 함수 시그니처에 대해서는 타입 추론을 하지 않거든요. 이렇게 해야 코드를 읽는 사람이 함수가 어떤 타입을 받고 반환하는지 빠르게 알 수 있어요.