Option으로 누락된 값 처리하기

RustBeginner
지금 연습하기

소개

실제 조회에서는 값이 항상 검색되지는 않습니다. Rust는 이러한 가능성을 Option<T>으로 나타냅니다. Some(value)는 값이 존재한다는 뜻이고, None은 값이 존재하지 않는다는 뜻입니다. 따라서 임의의 숫자로 누락을 숨기거나 패닉을 일으킬 위험 없이, 값의 부재를 타입의 일부로 표현할 수 있습니다.

간단한 재고 보고 프로그램을 완성합니다. 이 프로그램은 재고가 있는 경우와 없는 경우를 처리하고, 재고가 0인 기존 기록과 기록 자체가 없는 경우를 구분합니다. 또한 값이 있을 때만 작업하면 되는 상황에서 if let을 사용합니다.

존재하는 값과 누락된 값에 match 사용하기

이 단계에서는 준비된 조회 결과가 Option인지 확인하는 모든 경우를 처리하는 match로 자리 표시자 보고 코드를 바꿉니다.

준비된 Cargo 프로젝트로 이동합니다.

cd /home/labex/project/option-inventory

소스 파일을 엽니다.

nano src/main.rs

준비된 find_stock 함수는 Option<u32>을 반환합니다. 지금은 꺾쇠괄호를 “u32를 포함할 수 있는 Option”으로 이해하면 됩니다. 꺾쇠괄호 매개변수를 사용해 재사용 가능한 타입을 정의하는 방법은 뒤에서 배웁니다. Some(12)에는 재고 수량이 들어 있고, None에는 수량이 전혀 들어 있지 않습니다.

준비된 조회 코드의 마지막 부분에서 _ => None은 와일드카드 패턴 _을 사용합니다. 이는 앞의 다른 arm에서 일치하지 않은 모든 코드를 의미합니다. 와일드카드는 사용할 수 있는 변수를 만들지 않습니다. 일치하지 않은 값을 의도적으로 무시하고 None을 반환합니다.

report 함수 안에서 자리 표시자로 작성된 println!을 찾아 해당 한 줄을 다음 match 코드로 바꿉니다.

    match find_stock(code) {
        Some(count) => println!("{code}: {count} in stock"),
        None => println!("{code}: not found"),
    }

Some(count)는 값이 존재하는지 확인하는 동시에, 내부 숫자에 지역 이름 count를 부여합니다. None arm은 값이 없는 경우를 명시적으로 처리하므로, 이 match는 가능한 모든 Option<u32> 값을 처리합니다.

Ctrl+O를 눌러 저장하고 Enter를 누른 다음, Ctrl+X로 종료합니다. 프로그램을 실행하기 전에 확인합니다.

cargo check

Finished가 표시되면 두 Option의 경우를 모두 처리했다는 뜻입니다. 이제 보고 프로그램을 실행합니다.

cargo run --quiet

다음과 같은 결과가 표시되어야 합니다.

A1: 12 in stock
B2: 0 in stock
Z9: not found

마지막 줄은 None에서 나오고, 앞의 두 줄은 Some에서 나옵니다. 여기에는 Some(0) 값도 포함됩니다.

0과 값의 부재 구분하기

이 단계에서는 재고가 0인 기존 기록을 누락된 값으로 처리하지 않고, 별도의 동작을 수행하도록 만듭니다.

Some(0)과 None은 서로 다른 의미를 가집니다. Some(0)은 조회 결과 재고 수량이 0인 기록을 찾았다는 뜻입니다. None은 해당 코드의 기록이 존재하지 않는다는 뜻입니다. 따라서 0과 같은 자리 표시자를 값의 부재 대신 사용하는 것은 안전하지 않습니다.

소스 파일을 다시 엽니다.

nano src/main.rs

report 함수의 match 안에서 기존 Some(count) arm 바로 위에 다음 arm을 추가합니다.

        Some(0) => println!("{code}: out of stock"),

match arm은 위에서 아래 순서로 확인합니다. 더 넓은 패턴인 Some(count)도 0과 일치하므로, 구체적인 Some(0) 패턴을 Some(count)보다 먼저 작성해야 합니다.

저장하고 종료한 다음 프로그램을 실행합니다.

cargo run --quiet

이제 다음과 같은 결과가 표시되어야 합니다.

A1: 12 in stock
B2: out of stock
Z9: not found

각 줄이 이제 서로 다른 상태를 나타냅니다. 재고가 있는 상태, 기록은 있지만 비어 있는 상태, 기록이 없는 상태입니다.

값이 있을 때만 동작 수행하기

이 단계에서는 조회가 성공한 경우에만 작업이 필요한 상황에서 if let을 사용합니다.

모든 경우에 서로 다른 동작이 필요할 때는 모든 경우를 처리하는 match가 가장 적합합니다. if let PATTERN = VALUE는 또 다른 일반적인 상황에서 사용하는 간결한 조건부 패턴입니다. 패턴이 일치하면 한 블록을 실행하고, 그렇지 않으면 아무 작업도 하지 않습니다.

소스 파일을 엽니다.

nano src/main.rs

main 바로 위에 다음 함수를 추가합니다.

fn highlight_available(code: &str) {
    if let Some(count) = find_stock(code) {
        println!("{code}: {count} available for promotion");
    }
}

이 패턴은 Some인 경우에만 일치하므로 중괄호 안에서 count를 사용할 수 있습니다. 이 도우미 함수는 None일 때 출력할 내용이 의도적으로 없으므로 else 블록이 없습니다.

main에서 준비된 highlight_available("C3"); 줄의 맨 앞에 있는 //를 제거합니다. 저장하고 종료한 다음, 확인하고 실행합니다.

cargo check
cargo run --quiet

전체 출력은 다음과 같아야 합니다.

A1: 12 in stock
B2: out of stock
Z9: not found
C3: 5 available for promotion

네 번째 줄은 if let이 Some(5) 안의 값을 꺼냈으며, 값이 없는 경우에 대한 동작을 임의로 만들지 않았다는 것을 보여 줍니다.

요약

Some과 None을 match로 처리하고, Some(0)과 값의 부재를 구분했으며, 값이 있을 때만 동작이 필요한 경우 if let을 사용했습니다.