I have code that match
es on a Result<usize>
, with cases for Ok(usize_var)
and Err(why)
. I get a "match arms have incompatible types" error upon compilation which I believe is incorrect.
I'm not incredibly experienced with this, but discussion in irc (https://botbot.me/mozilla/rust/2015-04-06/?msg=35985521&page=37) supports that this may be a bug.
I tried this code (available at http://is.gd/Y00aRi):
fn main() {
let mut input: String = String::new();
match std::io::stdin().read_line(&mut input) {
Ok(bytes_read) => bytes_read,
Err(why) => panic!("{}", why)
}
println!("{}", input);
}
I expected it to compile, however there is a "match arms have incompatible types" error even though I believe the match arms should both be of type usize.
I should note that adding a semicolon after the match causes clean compilation.
(@steveklabnik I don't think the A-diagnostics label is appropriate for this issue. The bug is that this gives an error at all, not that the error _message_ is bad, so perhaps I-wrong would be better?)
I mean, it's a very bad error regardless, but yeah, maybe. I'll add it too.
My rustc version, if it helps
$ rustc --verbose --version
rustc 1.0.0-beta (9854143cb 2015-04-02) (built 2015-04-02)
binary: rustc
commit-hash: 9854143cba679834bc4ef932858cd5303f015a0e
commit-date: 2015-04-02
build-date: 2015-04-02
host: x86_64-apple-darwin
release: 1.0.0-beta
Yielding an error is correct here. The error message is merely misleading.
The core issue here is your first match arm is attempting to evaluate to a usize
value, but the entire match
statement is not semicolon-terminated. If you replace the panic!()
with, say, 42
, you get a similar error:
<anon>:3:5: 6:6 error: mismatched types:
expected `()`,
found `usize`
(expected (),
found usize) [E0308]
<anon>:3 match std::io::stdin().read_line(&mut input) {
<anon>:4 Ok(bytes_read) => bytes_read,
<anon>:5 Err(why) => 42
<anon>:6 }
This is also a bit misleading. The error looks like this because the match expression occurs in statement position, so either the match expression needs to resolve to the type ()
or it needs to be semicolon-terminated. The error here could definitely be better. In your case, the error is about the match arms having different types because Rust apparently decides to unify the panic!()
with the expected ()
return value, instead of with the first match arm's usize
value. If it resolved it with the usize
then you'd see the error I listed above instead.
Given that, I'm removing I-wrong.
@kballard Huh, you're right. I always assumed that adding a semicolon to a statement-position match
shouldn't ever change anything.
Funnily enough, just the other day in IRC @bstrie discovered the exact same thing :)
Another interesting test case, by @Binero from https://github.com/rust-lang/rust/issues/24568, that we should include as part of the fix:
fn main() {
is_one(1)
}
fn is_one(a: u8) {
match a {
1 => true,
_ => panic!("It's not one!"),
}
}
<anon>:6:5: 9:6 error: match arms have incompatible types:
expected `bool`,
found `()`
(expected bool,
found ()) [E0308]
<anon>:6 match a {
<anon>:7 1 => true,
<anon>:8 _ => panic!("It's not one!"),
<anon>:9 }
<anon>:8:14: 8:38 note: match arm with an incompatible type
<anon>:8 _ => panic!("It's not one!"),
^~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
playpen: application terminated with error code 101
Program ended.
Just to note, that this is super confusing for new rust developers - I sought help in #rust after beating my head on the wall for a bit :)
Another code that to me looks the same issue or even #35685. But it is a little different so I'm not really sure if should open a new issue:
pub trait Foo {}
struct Bar;
struct Baz;
impl Foo for Bar { }
impl Foo for Baz { }
fn not_all_paths(a: &str) -> u32 {
match a {
"baz" => 0,
_ => 1,
}; // "not all control paths return a value" due to ;
}
fn right(b: &str) -> Box<Foo> {
match b {
"baz" => Box::new(Baz),
_ => Box::new(Bar),
}
}
fn wrong(c: &str) -> Box<Foo> {
match c {
"baz" => Box::new(Baz),
_ => Box::new(Bar),
}; // "match arms have incompatible types" due to ;
}
fn main() {
}
Play here: https://is.gd/GTv1gf
Using: rustc 1.14.0-nightly (7c69b0d5a 2016-11-01)
Could someone outline what the recommended approach is for differently typed match arms? What do I do with options where i want to just run a operation in the Some
case but ignore in the None
case?
So I think the fix here would involve basically throwing away individual match arms, returning ()
instead of their type so as to "unify" when the match is in statement position. This seems relatively fine, but cc @rust-lang/lang -- would we need an RFC for this? Is that the correct interpretation of the best option?
The current behavior seems preferable to a change to me.
In all of these cases you are doing something quite unidiomatic, and in several the programmer has almost certainly made a mistake and should receive an error. For example:
fn is_one(a: u8) {
match a {
1 => true,
_ => panic!("It's not one!"),
}
}
It seems clear to me that this function is supposed to return bool
; if you change the return type, it will compile.
For the rest of them I see similar errors.
There are diagnostics issues, but I definitely think nothing should change about the semantics.
I agree with @withoutboats that this is a purely diagnostic issue.
For @withoutboats case, the output is the original error:
error[E0308]: match arms have incompatible types
--> src/main.rs:6:5
|
6 | / match a {
7 | | 1 => true,
8 | | _ => panic!("It's not one!"),
9 | | }
| |_____^ expected bool, found ()
|
= note: expected type `bool`
found type `()`
note: match arm with an incompatible type
--> src/main.rs:8:14
|
8 | _ => panic!("It's not one!"),
| ^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in a macro outside of the current crate
But if we "fix" the incompatible arms "problem", we get get a suggestion to change the return type:
error[E0308]: mismatched types
--> src/main.rs:8:40
|
5 | fn is_one(a: u8) {
| - help: possibly return type missing here?: `-> bool `
...
8 | _ => {panic!("It's not one!"); false},
| ^^^^^ expected (), found bool
|
= note: expected type `()`
found type `bool`
I'm guessing that adding that suggestion to match arms have incompatible types
would be worthwhile (maybe with a suggestion to add a semicolon at the end of the match, but I don't think that's as worthwhile).
@fungos's code now produces a code suggestion to remove the incorrect semicolons:
error[E0308]: mismatched types
--> src/main.rs:9:34
|
9 | fn not_all_paths(a: &str) -> u32 {
| __________________________________^
10 | | match a {
11 | | "baz" => 0,
12 | | _ => 1,
13 | | }; // not all control paths return a value due to ;
| | - help: consider removing this semicolon
14 | | }
| |_^ expected u32, found ()
|
= note: expected type `u32`
found type `()`
error[E0308]: match arms have incompatible types
--> src/main.rs:24:5
|
24 | / match c {
25 | | "baz" => Box::new(Baz),
26 | | _ => Box::new(Bar),
27 | | }; // match arms have incompatible types due to ;
| |_____^ expected struct `Baz`, found struct `Bar`
|
= note: expected type `std::boxed::Box<Baz>`
found type `std::boxed::Box<Bar>`
note: match arm with an incompatible type
--> src/main.rs:26:14
|
26 | _ => Box::new(Bar),
| ^^^^^^^^^^^^^
error[E0308]: mismatched types
--> src/main.rs:23:31
|
23 | fn wrong(c: &str) -> Box<Foo> {
| _______________________________^
24 | | match c {
25 | | "baz" => Box::new(Baz),
26 | | _ => Box::new(Bar),
27 | | }; // match arms have incompatible types due to ;
| | - help: consider removing this semicolon
28 | | }
| |_^ expected struct `std::boxed::Box`, found ()
|
= note: expected type `std::boxed::Box<Foo + 'static>`
found type `()`
_Updated to Jan 2019:_
For @withoutboats case, the output is the original error:
error[E0308]: match arms have incompatible types
--> src/main.rs:6:5
|
6 | / match a {
7 | | 1 => true,
8 | | _ => panic!("It's not one!"),
9 | | }
| |_____^ expected bool, found ()
|
= note: expected type `bool`
found type `()`
note: match arm with an incompatible type
--> src/main.rs:8:14
|
8 | _ => panic!("It's not one!"),
| ^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in a macro outside of the current crate
But if we "fix" the incompatible arms "problem", we get get a suggestion to change the return type:
error[E0308]: mismatched types
--> src/main.rs:8:40
|
5 | fn is_one(a: u8) {
| - help: possibly return type missing here?: `-> bool `
...
8 | _ => {panic!("It's not one!"); false},
| ^^^^^ expected (), found bool
|
= note: expected type `()`
found type `bool`
I'm guessing that adding that suggestion to match arms have incompatible types
would be worthwhile (maybe with a suggestion to add a semicolon at the end of the match as we do in some cases, but I don't think that's as worthwhile).
@fungos's code now produces a code suggestion to remove the incorrect semicolons:
error[E0308]: mismatched types
--> src/main.rs:9:30
|
9 | fn not_all_paths(a: &str) -> u32 {
| ------------- ^^^ expected u32, found ()
| |
| this function's body doesn't return
...
13 | }; // "not all control paths return a value" due to ;
| - help: consider removing this semicolon
|
= note: expected type `u32`
found type `()`
error[E0308]: match arms have incompatible types
--> src/main.rs:24:5
|
24 | / match c {
25 | | "baz" => Box::new(Baz),
26 | | _ => Box::new(Bar),
| | ------------- match arm with an incompatible type
27 | | }; // "match arms have incompatible types" due to ;
| |_____^ expected struct `Baz`, found struct `Bar`
|
= note: expected type `std::boxed::Box<Baz>`
found type `std::boxed::Box<Bar>`
error[E0308]: mismatched types
--> src/main.rs:23:22
|
23 | fn wrong(c: &str) -> Box<Foo> {
| ----- ^^^^^^^^ expected struct `std::boxed::Box`, found ()
| |
| this function's body doesn't return
...
27 | }; // "match arms have incompatible types" due to ;
| - help: consider removing this semicolon
|
= note: expected type `std::boxed::Box<(dyn Foo + 'static)>`
found type `()`
I believe there's little room for improvement here beyond what has been done so far:
error[E0308]: match arms have incompatible types
--> src/main.rs:5:21
|
3 | / match std::io::stdin().read_line(&mut input) {
4 | | Ok(bytes_read) => bytes_read,
| | ---------- this is found to be of type `usize`
5 | | Err(why) => panic!("{}", why)
| | ^^^^^^^^^^^^^^^^^ expected usize, found ()
6 | | }
| |_____- `match` arms have incompatible types
|
= note: expected type `usize`
found type `()`
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0308]: match arms have incompatible types
--> src/main.rs:8:14
|
6 | / match a {
7 | | 1 => true,
| | ---- this is found to be of type `bool`
8 | | _ => panic!("It's not one!"),
| | ^^^^^^^^^^^^^^^^^^^^^^^ expected bool, found ()
9 | | }
| |_____- `match` arms have incompatible types
|
= note: expected type `bool`
found type `()`
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
@estebank I can't see why the examples you provided don't compile, while this chapter in the official book says that panic!
has a return type !
so should never conflict with other arms in a match
expression: https://doc.rust-lang.org/book/ch19-04-advanced-types.html?highlight=diverg#the-never-type-that-never-returns
You're right, this error should suggest writing match a { /* ... */ };
with a finishing semicolon. This arm is being evaluated to ()
because that obligation is flowing up from the match
block.
Most helpful comment
Just to note, that this is super confusing for new rust developers - I sought help in #rust after beating my head on the wall for a bit :)