Struggling with a placeholder type

use std::io;

fn main() {
    let mut user_input = String::new();

    io::stdin()
        .read_line(&mut user_input)
        .and_then(|_| user_input.trim().parse::<i32>())
        .and_then(|number: i32| {
            println!("You entered: {number}");
            Ok(())
        })
        .expect("Unable to parse input into an integer");
}
error[E0308]: mismatched types
 --> src/main.rs:8:23
  |
8 |         .and_then(|_| user_input.trim().parse::<i32>())
  |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Result<_, Error>`, found `Result<i32, ParseIntError>`
  |
  = note: expected enum `Result<_, std::io::Error>`
             found enum `Result<i32, ParseIntError>`
help: try wrapping the expression in `Ok`
  |
8 |         .and_then(|_| Ok(user_input.trim().parse::<i32>()))
  |                       +++                                +

For more information about this error, try `rustc --explain E0308`.
error: could not compile `variables` (bin "variables") due to 1 previous error

Just trying to parse a number from the user so that I can do some calculations with it (the Fibonacci exercise).

I can't understand the reason for the error. It is my understanding that at line 8, I need to specify what type I expect after parsing. I wouldn't expect to need to specify the type for the parameter in the closure on line 9 but even after being explicit, the code still doesn't work.

The problem is that io::stdin().read_line() and user_input.trim().parse::<i32>() return Results with different error types, but and_then() can only be used if the error types are the same. Your options here include:

  • Call .unwrap() or .expect() immediately after .parse::<i32>() so that the entire input-parsing expression evaluates to an i32 rather than a Result<i32, ParseIntError>. (This will also make the and_then() calls pointless.)

  • Create a new error type enum that has a variant for std::io::Error and ParseIntError, and convert the errors from both read_line() and parse() to it. The thiserror crate can help here.

  • Crudely stuff the ParseIntError inside a std::io::Error using the latter's other() method.

This expect at the end of the chain doesn't make sense even if the two errors were of the same type, since the error returned by read_line would not be a parsing error. If you want to panic with expect on each of the errors:

    let mut user_input = String::new();

    io::stdin()
        .read_line(&mut user_input)
        .expect("Unable to read stdin");

    let number = user_input
        .trim()
        .parse::<i32>()
        .expect("Unable to parse input into an integer");

    println!("You entered: {number}");

If you're just learning Rust, I suggest that you don't try to use chains of method calls like this. Rust can be coded like that, but there are lots of limitations and it is more idiomatic to do a step or two at a time and use intermediate variables.

For more on error handling, be sure to see this if you haven't already:
https://doc.rust-lang.org/book/ch09-00-error-handling.html

1 Like

I completely missed the error types being different. I'll tinker with thiserror. Thanks

I'm not sure how idiomatic this is (I like @jumpnbrownweasel's clean approach), but if you're keen on chaining, anyhow also works:

    use anyhow::{Error, Ok};
    io::stdin()
        .read_line(&mut user_input)
        .map_err(Error::from)
        .and_then(|_| user_input.trim().parse::<i32>().map_err(Error::from))
        .and_then(|number: i32| {
            println!("You entered: {number}");
            Ok(())
        })
        .expect("Unable to parse input into an integer");