Less verbose type for map_err() closure argument?

Viewed 427

Is there a less verbose way to specify the type of the err argument for the closure in map_err()? In many other cases the type is inferred.

use std::convert::TryInto;
#[derive(Debug)]
struct MyError {
    pub msg: String
}

fn main() -> std::result::Result<(), MyError> {
let some_usize: usize = 0;
let some_i32: i32 = some_usize
    .try_into()
    .map_err(|err: <i32 as std::convert::TryFrom<usize>>::Error| MyError{ msg: err.to_string()})?;
//               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//                 remove this and it doesn't compile.
    Ok(())
}

Of course, the code some_i32 = some_usize as i32 works, and surprisingly, using i32::try_from(some_usize).map_err(...) DOES infer the type of err. So, I do have alternatives, but I'm still curious if there's an answer to my question.

3 Answers

You could implement the From trait for your error type.

impl From<std::num::TryFromIntError> for MyError {
    fn from (err: std::num::TryFromIntError) -> Self {
        Self{
            msg: err.to_string()
        }
    }
}

And then pass in MyError::from to map_err

let some_i32: i32 = some_usize
    .try_into()
    .map_err(MyError::from)?;

This would also allow for implementing the From trait for multiple types that you want to map into MyError.

You can use std::num::TryFromIntError. It doesn't prevent the type annotation, but it does make it more bearable.

use std::convert::TryInto;
use std::num::TryFromIntError;

#[derive(Debug)]
struct MyError {
    pub msg: String
}

fn main() -> std::result::Result<(), MyError> {
let some_usize: usize = 0;
let some_i32: i32 = some_usize
    .try_into()
    .map_err(|err: TryFromIntError| MyError{ msg: err.to_string()})?;
    Ok(())
}

You can help out Rust's type inferencing by splitting up the the problematic lines, and including a type annotation.

use std::convert::TryInto;
#[derive(Debug)]
struct MyError {
    pub msg: String
}

fn main() -> std::result::Result<(), MyError> {
    let some_usize: usize = 0;
    let some_i32: Result<i32, _> = some_usize
        .try_into();
    let some_i32 = some_i32
        .map_err(|err| MyError{ msg: err.to_string()})?;
    Ok(())
}

But honestly, I recommend just using try_from instead of try_into

let some_i32 = i32::try_from(some_usize)
    .map_err(|err| MyError{ msg: err.to_string()})?;
Related