n => Token::Operand(n.parse().unwrap()),
How does the compiler derive the type of n? n => Token::Operand(n.parse().unwrap()),
How does the compiler derive the type of n?[1]: https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_sy...
That value is an item from the iterator we got from calling split_whitespace() and split_whitespace() returns a SplitWhiteSpace, a custom iterator whose items are themselves sub-strings of the input string with (no surprise) no white space in them. In Rust's terminology these are &str, references to a string slice.
So, the type is &str
I admit when I started rust, seeing calls to .parse() was one of the more confusing things I saw in rust code, because of how much it leans on type inference to be readable. In places like these, it's a bit more readable:
let ip: IpAddr = ip_str.parse()?;
But when you see the .parse buried several levels deep and you have no idea what type it's trying to produce, it's a pain in the ass to read. This is why it's nice to use the turbo-fish syntax: let ip = ip_str.parse::<IpAddr>()?;
Since you can drop .parse::<IpAddr>()? anywhere to make the type explicit, especially when buried in type-inferred blocks like the code in TFA.That function is not shown, but it is included in the full source code which was linked. Well, technically we need to know that Rust says if there's a sum type Token::Operand which has an associated value, we can always call a function to make a Token::Operand with that value, and it just names this function Token::Operand too.
So, Token::Operand takes an i32, a 32-bit signed integer. The compiler knows we're eventually getting an i32 to call this function, if not our program isn't valid.
Which means n.parse().unwrap() has the type i32
We know n is an &str, the &str type has a generic function parse(), with the following signature:
pub fn parse<F>(&self) -> Result<F, <F as FromStr>::Err> where F: FromStr
So the type you now care about, that of n.parse() has to be Result of some kind, and we're going to call Result::unwrap() on that, to get an i32This can only work if the type F in that generic signature above is i32
Which means the new type you care about was Result<i32, ParseIntError> and the parse function called will be the one which makes Ok(i32) when presented an in-range integer.
Edited: Word-smithing, no significant change of meaning.
Because `match <exp>` could have contained an expression, you might need to handle a "catch all" case where you can refer to the result of that expression.
The code could have been `match s.doSomething() { ...`. The lines above what you have quoted just compare the result to a couple of a constants. If none are true, the line that you have quoted is equivalent to renaming the result of that expression to `n` and then handling that case.
`n` has the same type as the input of the `match` block. In other words, it's a fallback case. (In this case, it's `&str`; the same as `"+"`, `"-"`, etc)
If you're wondering how `n.parse().unwrap()` has its type computed, well that part is because type inference is able to look at the definition of `Token::Operand(u32)` and discover that it's `u32`.
From my experience: The compiler can do this, as long as the first usage of the unknown-typed-thing gives it a type. If the first usage of it doesn't, then it won't try any harder to infer the type and it won't compile unless you add your own annotations on.
[1]: https://doc.rust-lang.org/std/primitive.str.html#method.pars... [2]: https://doc.rust-lang.org/std/str/trait.FromStr.html