I'm reading The Rust Programming Language book and I stumbled upon a simple expression:
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
How does match work with different kinds of expressions in its arms? E.g. the first arm would simply "return" num so that it's assigned to guess but in the second arm the expression is simply continue. How does match handle that and doesn't "assign" continue to guess but executes it? What happens with the whole assignment expression itself? Is it dropped from the call stack (if that's the correct term)?
continue has a special type: it returns the never type, denoted !.
This type means "the code after that is unreachable". Since continue jumps to the next cycle of the loop, it'll never actually return any value (the same is true for return and break, and it's also the return type of panic!(), including all macros that panic: unreachable!(), todo!(), etc.).
The never type is special because it coerces (converts automatically) to any type (because if something cannot happen, we have no problems thinking about it as u32 or String or whatever - it will just not happen). This means it also unify with any other type, meaning the intersection of any type and ! is the other type.
match requires the expressions' type to unify (as does if). So your code returns the unification of ! and u32 == u32.
You can see that if you'll denote the type (requires nightly, since using the ! type not at return type position is experimental):
let num = match num {
Ok(num) => {
let num: i32 = num;
num
}
Err(()) => {
let never: ! = continue;
never
}
};
Playground.
Related
Looking at the guessing game example from the intro book, specifically the part where you use a match statement to perform error-handling:
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
Why doesn't it complain that the different arms of the match statement have different return types? One returns a u32, the other executes a continue statement and doesn't return anything. I thought either all arms of a match statement must execute code, or all arms must return something of the same type as one another.
continue has type ! (AKA "never"), which can coerce into any other type, since no values of it can exist.
In learning Rust, I encountered the following in the official Rust book:
There’s one pitfall with patterns: like anything that introduces a new
binding, they introduce shadowing. For example:
let x = 'x';
let c = 'c';
match c {
x => println!("x: {} c: {}", x, c),
}
println!("x: {}", x)
This prints:
x: c c: c
x: x
In other words, x => matches the pattern and introduces a new binding
named x that’s in scope for the match arm. Because we already have a
binding named x, this new x shadows it.
I don't understand two things:
Why does the match succeed?
Shouldn't the differing value of c and x cause this to fail?
How does the match arm x binding get set to 'c'?
Is that somehow the return of the println! expression?
There is a fundamental misconception of what match is about.
Pattern-matching is NOT about matching on values but about matching on patterns, as the name imply. For convenience and safety, it also allows binding names to the innards of the matched pattern:
match some_option {
Some(x) => println!("Some({})", x),
None => println!("None"),
}
For convenience, match is extended to match the values when matching specifically against literals (integrals or booleans), which I think is at the root of your confusion.
Why? Because a match must be exhaustive!
match expressions are there so the compiler can guarantee that you handle all possibilities; checking that you handle all patterns is easy because they are under the compiler's control, checking that you handle all values is hard in the presence of custom equality operators.
When using just a name in the match clause, you create an irrefutable pattern: a pattern that cannot fail, ever. In this case, the entire value being matched is bound to this name.
You can exhibit this by adding a second match clause afterward, the compiler will warn that the latter binding is unreachable:
fn main() {
let x = 42;
match x {
name => println!("{}", name),
_ => println!("Other"),
};
}
<anon>:6:5: 6:6 error: unreachable pattern [E0001]
<anon>:6 _ => println!("Other"),
^
Combined with the shadowing rules, which specifically allow hiding a binding in a scope by reusing its name to bind another value, you get the example:
within the match arm, x is bound to the value of 'c'
after the arm, the only x in scope is the original one bound to the value 'x'
Your two points are caused by the same root problem. Coincidentally, the reason that this section exists is to point out the problem you asking about! I'm afraid that I'm basically going to regurgitate what the book says, with different words.
Check out this sample:
match some_variable {
a_name => {},
}
In this case, the match arm will always succeed. Regardless of the value in some_variable, it will always be bound to the name a_name inside that match arm. It's important to get this part first — the name of the variable that is bound has no relation to anything outside of the match.
Now we turn to your example:
match c {
x => println!("x: {} c: {}", x, c),
}
The exact same logic applies. The match arm with always match, and regardless of the value of c, it will always be bound to the name x inside the arm.
The value of x from the outer scope ('x' in this case) has no bearing whatsoever in a pattern match.
If you wanted to use the value of x to control the pattern match, you can use a match guard:
match c {
a if a == x => println!("yep"),
_ => println!("nope"),
}
Note that in the match guard (if a == x), the variable bindings a and x go back to acting like normal variables that you can test.
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?
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.
continue is, along with break and return, "divergent". That is, the compiler knows that control flow does not resume after it, it goes somewhere else. This is also true of any function which returns !; this is how the compiler knows that functions like std::rt::begin_unwind never return.
This is one of those simple-but-I-don't-know-how-to-do-it-in-rust things.
Simply put:
pub fn pair_matcher(tup: &(String, String)) {
match tup {
&("foo".as_string(), "bar".as_string()) => print!("foobar"),
_ => print!("Unknown"),
}
}
I get the error
-:3:17: 3:18 error: expected `,`, found `.`
-:3 &("foo".as_string(),"bar".as_string())=> { print!("foobar"); }
^
How do you match this?
The left hand side of each branch of a match is not an expression, it is a pattern, which restricts what can go there to basically just literals (plus things like ref which change the binding behaviour); function calls are right out. Given how String works, it’s not possible to get one of them into a pattern (because you can’t construct one statically). It could be achieved with if statements:
if tup == ("foo".to_string(), "bar".to_string()) {
print!("foobar")
} else {
print!("Unknown")
}
… or by taking a slice of the Strings, yielding the type &str which can be constructed literally:
match (tup.0.as_slice(), tup.1.as_slice()) {
("foo", "bar") => print!("foobar"),
_ => print!("Unknown"),
}
Constructing a new String each time is an expensive way of doing things, while using the slices is pretty much free, entailing no allocations.
Note that the .0 and .1 requires #![feature(tuple_indexing)] on the crate; one can do without it thus:
let (ref a, ref b) = tup;
match (a.as_slice(), b.as_slice()) {
("foo", "bar") => print!("foobar"),
_ => print!("Unknown"),
}
Because, you see, the left hand side of a let statement is a pattern as well, and so you can pull the tuple apart with it, taking references to each element, with (ref a, ref b) yielding variables a and b both of type &String.
The Patterns section of the guide goes into some more detail on the subject.
The solution here is that you need to cast types in the other direction:
match (tup.0.as_slice(), tup.1.as_slice()) {
("foo", "bar") => print!("foobar"),
}
I'm fairly new to Rust so I came across this piece of code in the official Guide
let input = io::stdin().read_line()
.ok()
.expect("Failed to read line");
let input_num: Option<uint> = from_str(input.as_slice());
let num = match input_num {
Some(num) => num,
None => {
println!("Please input a number!");
return;
}
};
While the understand the first two statements (on input and inputnum), I'm not quite sure about the match statement. So I checked the documentation which shows that Option<T> can take two values , either None or Some(T) for some (object?) T. So I tested the following code:
io::println(
match input_num {
Some(num) => "somenum",
None => {
println!("Please input a number only!");
return;
}
}
);
This code works as expected; it prints somenum if you enter a number and otherwise it prints the error message. However, the compiler gives a warning stating: warning: unused variable:num, #[warn(unused_variable)] on by default. This confirmed my suspicions that the num inside the `match is used as a variable.
Question: How is it possible that rust does not complain about (in the Guide's example) having two variables with the same name num? Or does it "hand over" the pointer to the inside num to the outside num?
Also in the case of an empty return what exactly is returned? I'm guessing it is unit () because it is mentioned here that
functions without a -> ... implicitly have return type ()
Edit: Sorry for missing the obvious point. The return directly exits the function without bothering to put anything into num.
P.S. I noticed that using cargo build to compile does not give warnings the second time around (not making any changes). Does cargo keep tracking of versions or something?
Do you have any experiences with Java or C#? Many mainstream programing languages allows you to shadow a variable with another variable with the same name in a new block scope. For example, take a look at this C# code:
using System;
public class Test
{
public static void Main()
{
int x = 10;
{
int x = 20;
Console.WriteLine(x);
}
Console.WriteLine(x);
}
}
http://ideone.com/OuMlc8
In fact Rust is more flexible in terms of shadowing: you can shadow variables as many times as you want without explicitly making a brace block.
fn main() {
let i = 10i32;
let i = "foo";
println!("{}", i);
}
http://is.gd/CUHUOL
Here, the first i and the second i have no relationships. We just lose a way to use (refer to) the first i.
This confirmed my suspicions that the num inside the `match is used as a variable.
Yes, pattern matching can introduce new variables. But the new variable num introduced by pattern matching has no direct relationship with the outer num whose value is being given by the match expression.
I can translate your Rust code into less idiomatic Rust to illustrate what was happening:
let num =
if input_num.is_some() {
let num = input_num.unwrap();
num
} else {
println!("Please input a number!");
return;
}
;
As for not observing warnings from cargo build, I suspect it was because Cargo had already built .o from your code and didn't bother to repeat the same job twice, knowing that there was no change in your code.