0

I have this Rust code:

use std::fmt;

struct S {
    a: i32,
    b: i32,
    c: Vec<String>,
}

impl fmt::Display for S {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self.a {
            -1 => {
                if &self.b < &0 {
                    write!(f, "Invalid a & b\n")
                } else {
                    write!(f, "Invalid a, valid b\n")
                }
            }
            _ => write!(f, "Valid a & b\n"),
        };
        // not including this above semicolon causes an error

        if &self.c.len() > &0 {
            write!(f, "{}", &self.c.join("\n"))
        } else {
            write!(f, "")
        }
    }
}

fn main() {
    let s = S {
        a: 0,
        b: 0,
        c: vec![String::from("something")],
    };
    println!("{}", s)
}

Playground

The match is there to check whether arguments a and b are valid, and the if statement below is to check whether argument c has at least 1 string in it to print out.

This code fails when I don't include a semicolon just after the match statement:

error[E0308]: mismatched types
  --> src/main.rs:14:21
   |
13 | /                 if &self.b < &0 {
14 | |                     write!(f, "Invalid a & b\n")
   | |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found enum `std::result::Result`
15 | |                 } else {
16 | |                     write!(f, "Invalid a, valid b\n")
17 | |                 }
   | |_________________- expected this to be `()`
   |
   = note: expected unit type `()`
                   found enum `std::result::Result<(), std::fmt::Error>`
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
help: try adding a semicolon
   |
2  | ($ dst . write_fmt ($ crate :: format_args ! ($ ($ arg) *));)
   |                                                            ^
help: consider using a semicolon here
   |
17 |                 };
   |                  ^

error[E0308]: mismatched types
  --> src/main.rs:16:21
   |
13 | /                 if &self.b < &0 {
14 | |                     write!(f, "Invalid a & b\n")
15 | |                 } else {
16 | |                     write!(f, "Invalid a, valid b\n")
   | |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found enum `std::result::Result`
17 | |                 }
   | |_________________- expected this to be `()`
   |
   = note: expected unit type `()`
                   found enum `std::result::Result<(), std::fmt::Error>`
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
help: try adding a semicolon
   |
2  | ($ dst . write_fmt ($ crate :: format_args ! ($ ($ arg) *));)
   |                                                            ^
help: consider using a semicolon here
   |
17 |                 };
   |                  ^

I am trying to make sense of what this error means. I know that semicolons are used to discard the result of an expression, and it seems by putting a semicolon after a match I am discarding the result of the match expression. Looking at the error message it says that it expected a () type instead of the type std::fmt::Result on the lines where a write! is performed.

  1. Why does it expect this () type, when all branches of the match statement and its inner if are calling write! and returning a std::fmt::Result?
  2. When I don't include the second part of the code that checks the length of the argument c, suddenly the code works even without the semicolon. Why does it not error out without a semicolon when the second if that checks for c.len() > 0 is omitted?
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Raees Rajwani
  • 487
  • 5
  • 18
  • 1
    So what should happen with the result of `match`? – KamilCuk Mar 02 '20 at 17:49
  • 1
    *When I don't include the second part of the code* — in that case you are returning the `Result` that the `match` evaluates to from the `fmt` function, the same as your original `if`/`else` does. – Shepmaster Mar 02 '20 at 17:52
  • 1
    The right solution here is to add a question mark operator to each of the `write!()` invocations in the `match` statement. This will propagate any errors encountered, and you also won't need the semicolon after the `match` expression anymore, since it now has type `()`. – Sven Marnach Mar 02 '20 at 19:29

0 Answers0