cadolphs
cadolphs

Reputation: 9627

Converting error to boxed when inside a collection

I have a function that reads a file line by line and parses. Right now to i32 but really I'd like this to be templated. Just keeping things simple for now.

I can get it sorted out using explicit for-loops, but not using iterators.

The below code complains that the error type can't be converted to the Box<dyn Error> type and I'm not sure what to do about that. Aren't the parse errors part of the standard library and thus implement the Error trait, and doesn't Box do automatic conversion of errors (because it's such a common idiom?)

Playground link

use std::error::Error;
use std::fs;

pub fn read_file_to_list(filename: &str) -> Result<Vec<i32>, Box<dyn Error>> {
    let file_as_string = fs::read_to_string(filename)?;
    file_as_string.lines().map(|line| line.parse()).collect()
}

Error is in the parse:

   Compiling playground v0.0.1 (/playground)
error[E0271]: type mismatch resolving `<i32 as FromStr>::Err == Box<dyn std::error::Error>`
 --> src/lib.rs:6:44
  |
6 |     file_as_string.lines().map(|line| line.parse()).collect()
  |                                            ^^^^^ expected struct `ParseIntError`, found struct `Box`
  |
  = note: expected struct `ParseIntError`
             found struct `Box<dyn std::error::Error>`

I assume I'll need to do some sort of map_err but I can't figure out what and how.

Upvotes: 0

Views: 123

Answers (1)

orlp
orlp

Reputation: 117711

Luckily, conversion from ParseIntError to Box<dyn Error> can be done using the Into trait as there is a blanket impl for that in the stdlib. So we can replace line.parse() with

line.parse().map_err(|e| e.into())

or:

line.parse().map_err(Into::into)

A common trick to make this more ergonomic is abusing the fact that ? automatically calls .into() on the error type:

Ok(line.parse()?)

It looks a bit silly to use ? only to wrap in Ok, but it's actually a common idiom precisely for this purpose. So this compiles:

pub fn read_file_to_list(filename: &str) -> Result<Vec<i32>, Box<dyn Error>> {
    let file_as_string = fs::read_to_string(filename)?;
    file_as_string.lines().map(|line| Ok(line.parse()?)).collect()
}

Upvotes: 3

Related Questions