hyunsu blog

홈강좌Rust 입문·중급 47편 중 27편째

Rust if let 과 let else — 한 경우만 볼 때 match 를 줄여 쓴다

목차 · 3
  1. 01if let — 한 경우만 볼 때
  2. 02let else — 아니면 빠져나간다
  3. 03실습 — else 가 빠져나가지 않으면

Rust 의 if let Some(x) = 값 { } 은 그 경우일 때만 실행하는, 갈래 하나짜리 match 다. let Some(x) = 값 else { return …; }; 은 아니면 바로 빠져나가고, 맞으면 x 를 그 아래에서 계속 쓴다. let else 의 else 블록이 빠져나가지 않으면 E0308 이 난다.

01 if let — 한 경우만 볼 때

쿠폰이 있을 때만 할인 문구를 찍어 보자. 25편의 match 로 쓰면 None => {} 처럼 아무것도 안 하는 갈래를 하나 더 써야 한다. if let 은 그 갈래를 생략한다.

rust
fn main() {
    let coupon: Option<u32> = Some(3000);

    if let Some(amount) = coupon {
        println!("discount {amount} won");
    }

    let none: Option<u32> = None;
    if let Some(amount) = none {
        println!("discount {amount} won");
    } else {
        println!("no coupon");
    }
}
text
   Compiling coupon v0.1.0 (~/projects/coupon)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.15s
     Running `target/debug/coupon`
discount 3000 won
no coupon
  • if let Some(amount) = coupon 은 "coupon 이 Some 이면, 안의 값을 amount 로 꺼내서 중괄호를 실행하라" 는 뜻이다.
  • = 가 하나지만 대입이 아니라 "이 모양에 맞는가" 를 묻는 것이다.
  • 맞지 않으면(None) 건너뛴다. else 를 붙이면 맞지 않을 때 실행할 곳을 정한다.

match 와 달리 다른 경우를 빠뜨려도 E0004 가 나지 않는다. 다른 경우는 "아무것도 안 한다" 고 정한 것이기 때문이다. 짧은 대신 빠짐없는지 검사해 주는 힘은 잃는다.

02 let else — 아니면 빠져나간다

함수 맨 앞에서 "값이 없으면 바로 끝낸다" 를 쓸 때는 let else 가 깔끔하다.

rust
fn price_after(price: u32, coupon: Option<u32>) -> u32 {
    let Some(amount) = coupon else {
        return price;
    };
    price - amount
}

fn main() {
    println!("{}", price_after(12000, Some(3000)));
    println!("{}", price_after(12000, None));
}
text
   Compiling coupon v0.1.0 (~/projects/coupon)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.16s
     Running `target/debug/coupon`
9000
12000
  • let Some(amount) = coupon else { return price; }; 은 맞으면 amount 를 만들고 다음 줄로 간다. 맞지 않으면 else 블록을 실행한다.
  • if let 과 달리 꺼낸 amount 를 그 아래 줄에서 계속 쓸 수 있다. 중괄호 안에 갇히지 않는다.
  • 끝의 세미콜론을 잊지 않는다. let 문이기 때문이다.

03 실습 — else 가 빠져나가지 않으면

else 블록에서 문구만 찍고 return 을 빼 보자.

rust
fn price_after(price: u32, coupon: Option<u32>) -> u32 {
    let Some(amount) = coupon else {
        println!("no coupon");
    };
    price - amount
}

fn main() {
    println!("{}", price_after(12000, None));
}
text
error[E0308]: `else` clause of `let...else` does not diverge
 --> src/main.rs:2:36
  |
2 |       let Some(amount) = coupon else {
  |  ____________________________________^
3 | |         println!("no coupon");
4 | |     };
  | |_____^ expected `!`, found `()`
  |
  = note:   expected type `!`
          found unit type `()`
  = help: try adding a diverging expression, such as `return` or `panic!(..)`
  1. 무엇 — let...else 의 else 가 빠져나가지 않는다(does not diverge).
  2. 어디 — 2~4번째 줄의 else 블록 전체를 선으로 묶었다. ! 는 "여기서 끝나고 다음 줄로 안 간다" 는 뜻의 타입인데, 블록이 ()(11편의 값 없음)로 끝났다.
  3. 고치는 법 — help: 가 return 이나 panic!(..) 처럼 빠져나가는 식을 넣으라고 한다. 아니면 match 를 쓰라고 한다.

else 를 그냥 지나가게 두면 그 아래에서 amount 를 쓸 때 값이 없다. 그래서 else 는 반드시 return 같은 것으로 함수를 빠져나가야 한다. 13편의 반복 안이라면 continue 나 break 도 된다.

쓰는 것쓰는 때
match경우를 모두 다룬다
if let한 경우일 때만 무언가 한다
let else아니면 일찍 빠져나가고, 맞으면 꺼낸 값으로 계속한다

다음 편은 길이가 늘었다 줄었다 하는 목록, Vec 이다.