Can the compiler tell when a `break` is never reached

时间:2015-10-30 22:16:21

标签: rust

I have this code:

fn make_guess() -> u32 {
    loop {
        let mut guess = String::new();
        io::stdin().read_line(&mut guess)
            .ok()
            .expect("Failed to read line");

        match guess.trim().parse() {
            Ok(num) => return num,
            Err(_) => {
                println!("Please input a number!");
                continue;
            }
        };
        break;
    }
}

When I run this code, the compiler complains about:

expected `u32`,
  found `()`

Seemingly the break; results in this returning a void value. However, there is no way for the break; to be reached because of the return and continue.

In fact, if I remove the break; this works fine.

Is this a bug in the compiler or intended for some reason?

2 个答案:

答案 0 :(得分:4)

A loop expression that doesn't contain a break expression evaluates to ! (i.e. it diverges), so it is compatible with all types.

fn main() {
    let _x: u32 = loop {}; // this compiles
}

On the other hand, a loop with a break returns (), so it is only compatible with the () type.

fn main() {
    let _x: u32 = loop { break; }; // mismatched types: expected `u32`,  found `()`
}

In your make_guess function, the compiler adds an implicit return before the loop expression. ! is compatible with u32, but () is not. (This implicit return seems to occur for all diverging expressions, even if they end with a ;.) Adding a break statement in the loop changes the type of the loop expression. It does not matter that the break is unreachable.

Heads up to @ker for noticing that the type of a loop changes based on the presence or absence of a break expression in its body.

答案 1 :(得分:1)

问题是您的代码可能不会返回u32。保持代码习惯生锈的最佳方法是使返回类型为Option<T>这是一个包含两个成员Some(T)None的枚举,这意味着该函数可以,或者可能不会返回值。

fn make_guess() -> Option<u32> {
    loop {
        let mut guess = String::new();
        io::stdin().read_line(&mut guess)
            .ok()
            .expect("Failed to read line");

        match guess.trim().parse() {
            Ok(num) => return Some(num),
            Err(_) => {
                println!("Please input a number!");
                continue;
            }
        };
        break;
    }
    None
}