[Rust] map과 flat_map 차이점

임민교·2024년 6월 17일
post-thumbnail

Rust의 mapflat_map 차이점 및 사용 예제

Rust의 mapflat_map은 이터레이터 메서드로, 각각 이터레이터의 요소에 함수를 적용하지만 중요한 차이점이 있습니다. 두 메서드의 차이점과 사용 예제를 통해 설명하겠습니다.

map

map 메서드는 이터레이터의 각 요소에 함수를 적용하여 새로운 이터레이터를 생성합니다. 각 요소를 변환하지만 결과는 여전히 변환된 요소의 이터레이터입니다.

예제

fn main() {
    let numbers = vec![1, 2, 3, 4];
    let squares: Vec<i32> = numbers.iter().map(|&x| x * x).collect();

    println!("{:?}", squares); // [1, 4, 9, 16]
}

위 예제에서 map은 각 요소에 제곱 함수를 적용하여 새로운 벡터를 만듭니다.

flat_map

flat_map 메서드는 이터레이터의 각 요소에 함수를 적용하고, 이 함수는 또 다른 이터레이터를 반환해야 합니다. 그런 다음 flat_map은 이러한 내부 이터레이터를 평탄화하여 단일 이터레이터를 생성합니다.

예제

fn main() {
    let numbers = vec![1, 2, 3, 4];
    let expanded: Vec<i32> = numbers.iter().flat_map(|&x| vec![x, x * 2]).collect();

    println!("{:?}", expanded); // [1, 2, 2, 4, 3, 6, 4, 8]
}

위 예제에서 flat_map은 각 요소에 대해 두 개의 값을 포함하는 벡터를 반환합니다. 그런 다음 flat_map은 이 중첩된 이터레이터를 평탄화하여 단일 벡터를 만듭니다.

주요 차이점 요약

  • map: 각 요소를 변환하지만 결과는 여전히 변환된 요소의 이터레이터입니다.
  • flat_map: 각 요소를 변환하여 새로운 이터레이터를 반환하고, 이 이터레이터들을 평탄화하여 단일 이터레이터를 만듭니다.

추가 예제: 문자열을 단어로 분할

이 예제는 각 문자열을 단어로 분할하여 모든 단어를 단일 벡터로 수집하는 방법을 보여줍니다.

map 예제

fn main() {
    let sentences = vec!["hello world", "rust is great"];
    let words: Vec<_> = sentences.iter().map(|&s| s.split_whitespace()).collect();

    println!("{:?}", words);
    // 결과: [SplitWhitespace { inner: "hello world" }, SplitWhitespace { inner: "rust is great" }]
    // SplitWhitespace 이터레이터가 포함된 벡터
}

flat_map 예제

fn main() {
    let sentences = vec!["hello world", "rust is great"];
    let words: Vec<&str> = sentences.iter().flat_map(|&s| s.split_whitespace()).collect();

    println!("{:?}", words); // ["hello", "world", "rust", "is", "great"]
}

위 예제에서 flat_map은 각 문자열을 단어로 분할하고, 모든 단어를 평탄화하여 단일 벡터로 수집합니다.

Rust에서 mapflat_map의 차이점: Result와 함께 사용

mapflat_map의 차이는 Result 타입과 함께 사용할 때 특히 중요합니다.

map 예제:

fn main() {
    let input_numbers = "10 20 30 invalid 40";
    let numbers: Vec<Result<i32, _>> = input_numbers
        .split_ascii_whitespace()
        .map(str::parse::<i32>)
        .collect();

    for num in &numbers {
        match num {
            Ok(n) => println!("Parsed number: {}", n),
            Err(e) => println!("Failed to parse: {}", e),
        }
    }
}

위 코드는 각 요소에 대해 str::parse::<i32>를 호출하고, 그 결과(Result<i32, _>)를 그대로 유지합니다. collect()는 이를 Vec<Result<i32, _>>로 변환합니다. 파싱 실패 여부를 확인할 수 있습니다.

flat_map 예제:

fn main() {
    let input_numbers = "10 20 30 invalid 40";
    let numbers: Vec<i32> = input_numbers
        .split_ascii_whitespace()
        .flat_map(str::parse::<i32>)
        .collect();

    for num in &numbers {
        println!("Parsed number: {}", num);
    }
}

위 코드는 flat_map을 사용하여 Result에서 성공한 값들만 남기고 오류를 무시합니다. flat_map은 각 요소에 대해 str::parse::<i32>를 호출하고, 성공적인 결과를 평탄화하여 단일 이터레이터로 반환합니다.

결론

  • map: 각 요소에 대해 Result를 반환하고, 이를 그대로 유지합니다. 즉, 실패한 파싱 결과도 Result 형태로 벡터에 포함됩니다.
  • flat_map: Result를 평탄화하여 성공적인 값들만 단일 이터레이터로 반환합니다. 실패한 파싱 결과는 무시됩니다.

따라서, 입력 문자열에서 숫자만 추출하고 싶다면 flat_map을 사용하는 것이 더 적합합니다. 이는 성공적으로 파싱된 값들만 남기고 나머지를 무시하기 때문입니다. 반면, 파싱 실패 여부도 확인하고 싶다면 map을 사용하는 것이 적합합니다.

profile
매일 1% 성장하는 개발자

0개의 댓글