Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How type safety is guaranteed?

Tags:

rust

Just skimming through the Rust guide (guessing game), this code fragment doesn't seem right to me:

let num = match input_num {
    Some(num) => num,
    None      => {
        println!("Please input a number!");
        continue;
    }
};

How does type inference of num work in this scenario? The first match case obviously return a number, whereas the second match case is just println & continue statement, which doesn't return anything(or return ()). How does the compiler assume it's type safe?

like image 232
basic_bgnr Avatar asked Nov 28 '14 06:11

basic_bgnr


1 Answers

Let's look at that piece of code more closely:

loop {
    // ... some code omitted ...
    let num = match input_num {
        Some(num) => num,
        None      => {
            println!("Please input a number!");
            continue;
        }
    };
    // ... some code omitted ...
}

The match statement is located inside a loop, and there are several constructs in the language which help control the looping process. break exits from a loop early, while continue skips the rest of the code in the loop and goes back to its beginning (restarts it). So this match above basically can be read basically as "Check the number, and if it is there, assign it to num variable, otherwise output a message and restart from the beginning".

The behavior of "otherwise" branch is important: it ends with a control transfer operation, continue in this case. The compiler sees continue and knows that the loop is going to be restarted. Consequently, it does not really matter what value this branch yields, because it will never be used! It may as well never yield anything.

Such behavior often is modeled with so-called bottom type, which is a subtype of any type and which does not have values at all. Rust does not have subtyping (essentially), so such type is deeply magical. It is denoted as ! in type signatures:

fn always_panic() -> ! {
    panic!("oops!")
}

This function always panics, which causes stack unwinding and eventual termination of the thread it was called in, so its return value, if there was one, will never be read or otherwise inspected, so it is absolutely safe not to return anything at all, even if it is used in expression context which expects some concrete type:

let value: int = always_panic();

Because always_panic() has return type !, the compiler knows that it is not going to return anything (in this case because always_panic() starts stack unwinding), it is safe to allow it to be used in place of any type - after all, the value, even if it was there, is never going to be used.

continue works exactly in the same way, but locally. None branch "returns" type !, but Some branch returns value of some concrete numeric type, so the whole match statement is of this numeric type, because the compiler knows that None branch will lead to control transfer, and its result, even if it had one, will never be used.

like image 154
Vladimir Matveev Avatar answered Nov 15 '22 11:11

Vladimir Matveev