Rust에서 `.map()` 클로저 내부에서 `Result`를 반환하는 함수를 사용할 때 발생하는 오류를 해결하는 간단한 방법들을 살펴봅니다.
Rust에서 .map() 클로저 안에서 Result를 반환하는 함수를 사용하려고 할 때 오류가 발생하나요? Rust를 배우는 사람들이 자주 겪는 문제라고 생각합니다. 다행히 해결 방법은 아주 간단합니다.
다음 Rust 코드를 살펴봅시다. 여기에는 둘 다 Result 타입을 반환하는 두 개의 함수가 있습니다. 첫 번째 함수는 한 줄(문자열 슬라이스)에 접두사를 붙이고, 두 번째 함수는 줄 목록을 받아 첫 번째 함수를 사용해 각 줄에 접두사를 붙입니다.
use std::error::Error;
fn prefix_line(line: &str) -> Result<String, Box<dyn Error>> {
Ok(format!("PREFIX: {}", line))
}
fn prefix_each(lines: &[&str]) -> Result<Vec<String>, Box<dyn Error>> {
let result = lines
.iter()
.map(|line| prefix_line(line)?)
.collect::<Vec<_>>();
Ok(result)
}
이 코드를 컴파일하려고 하면 10번째 줄에서 오류가 발생합니다.
'?' 연산자는 'Result' 또는 'Option'을 반환하는 클로저(또는 'FromResidual'을 구현한 다른 타입)에서만 사용할 수 있습니다 [E0277] 도움말: 'FromResidual<Result<Infallible, Box<dyn StdError>>>' 트레이트는 'std::string::String'에 구현되어 있지 않습니다
오류를 풀어서 보면 다음과 같습니다.
map 함수는 인자로 클로저를 받습니다.map() 클로저 내부의 ? 연산자가 문제가 됩니다? 연산자가 클로저가 Result 또는 Option을 반환하기를 기대하기 때문입니다map의 기대 출력 타입과 같도록 추론되며, 그 타입은 String입니다String은 FromResidual을 구현하지 않으므로 이 오류가 발생합니다제가 찾은 가장 간단한 해결 방법은 다음과 같습니다.
? 연산자 없이 prefix_line(line)를 호출하면 Vec<Result<...>>가 생성됩니다collect::<Result<Vec<_>, _>>()?를 사용해 타입을 올바르게 추론하고, Result들을 수집하며, 오류가 있으면 전파합니다Vec<String>을 얻게 됩니다fn prefix_each(lines: &[&str]) -> Result<Vec<String>, Box<dyn Error>> {
let result = lines
.iter()
.map(|line| prefix_line(line))
.collect::<Result<Vec<_>, _>>()?;
Ok(result)
}
가장 먼저 떠올릴 수 있는 명백한 해결책이지만 다음과 같은 단점이 있습니다.
fn prefix_each(lines: &[&str]) -> Result<Vec<String>, Box<dyn Error>> {
let mut result = Vec::new();
for line in lines {
result.push(prefix_line(line)?);
}
Ok(result)
}
filter_map은 Err 값을 버리고 성공한 Ok 값만 수집합니다fn prefix_each(lines: &[&str]) -> Result<Vec<String>, Box<dyn Error>> {
let result = lines
.iter()
.filter_map(|line| prefix_line(line).ok())
.collect::<Vec<_>>();
Ok(result)
}
try_fold를 사용해 결과를 집계하고 오류를 깔끔하게 처리할 수 있습니다Result 타입을 직접 반환할 수 있게 해줍니다fn prefix_each(lines: &[&str]) -> Result<String, Box<dyn Error>> {
let result = lines.iter().try_fold(String::new(), |mut acc, line| {
prefix_line(line).map(|prefixed_line| {
acc.push_str(&prefixed_line);
acc.push_str("\n");
acc
})
})?;
Ok(result)
}