`no method named `unwrap` found for type` when writing a generic input parser


#1

Hi, I’m trying to write a generic input parser. Here is my code:

use std::io;
use std::fmt::Debug;
use std::str::FromStr;

fn token_iter<'a, T: Debug + FromStr>
(line: &'a String) -> Box<Iterator<Item = T> + 'a> {
    Box::new((*line).split_whitespace().map(|x| x.parse::<T>().unwrap()))
}

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

    io::stdin().read_line(&mut line1).unwrap();

    let iter: Box<Iterator<Item = i32>> = token_iter(&line1);

    for token in iter {
        println!("{}", token);
    }
}

The compiling error I got is

error: no method named `unwrap` found for type `std::result::Result<T, <T as std::str::FromStr>::Err>` in the current scope
 --> src/main.rs:7:64
  |
7 |     Box::new((*line).split_whitespace().map(|x| x.parse::<T>().unwrap()))
  |                                                                ^^^^^^
  |
  = note: the method `unwrap` exists but the following trait bounds were not satisfied: `<T as std::str::FromStr>::Err : std::fmt::Debug`

error: aborting due to previous error

I think I used multiple trait bounds in function signature, but it still won’t compile because the unwrap method derives that T should be FromStr from the usage of parse method.


#2

The answer is right there in the error message.

fn token_iter<'a, T>(line: &'a String) -> Box<Iterator<Item=T> + 'a>
where
    T: FromStr,
    <T as std::str::FromStr>::Err: std::fmt::Debug,
{
    Box::new((*line).split_whitespace().map(|x| x.parse::<T>().unwrap()))
}

(I also moved the FromStr into the where clause, and removed the unnecessary existing Debug constraint.)


#3

Thank you for the reply.

I don’t quite understand <T as std::str::FromStr>::Err: std::fmt::Debug. I know that T: FromStr means that type T must impl trait FromStr

Could you please link me to documentation pages that explain this?


#4

if you look at the FromStr trait you see that it has a associated type Err. and the <T as std::str::FromStr>::Err: std::fmt::Debug means that this associated type has to implement fmt::Debug also for a simple case like this you can just write

fn token_iter<'a, T>(line: &'a String) -> Box<Iterator<Item=T> + 'a>
where
    T: FromStr,
    T::Err: std::fmt::Debug,
{
    Box::new((*line).split_whitespace().map(|x| x.parse::<T>().unwrap()))
}

the <T as std::str::FromStr>::Err is only really necessary if T implements more as one trait with a associate Err type each


#5

juggle-tux just beat me to it, so here are some relevant links:


#6

Thank you both for detailed explanation.


#7

btw. i think you want to pass a &'a str to the function so you can pass &str or &String to it.
&String since it derefs to str


#8

I had the same kind of error with quick and dirty code using Futures, and it took me a while to understand why I could not use unwrap.

Instead of

error: no method named `unwrap` found for type `std::result::Result<T, <T as std::str::FromStr>::Err>` in the current scope
 --> src/main.rs:7:64
  |
7 |     Box::new((*line).split_whitespace().map(|x| x.parse::<T>().unwrap()))
  |                                                                ^^^^^^
  |
  = note: the method `unwrap` exists but the following trait bounds were not satisfied: `<T as std::str::FromStr>::Err : std::fmt::Debug`

Maybe the compiler could say instead

error: the method `unwrap` is defined in `impl<T, E> Result<T, E> where E: Debug`
but this is not applicable because the following is not satisfied:
`<T as std::str::FromStr>::Err : std::fmt::Debug`
 --> src/main.rs:7:64
  |
7 |     Box::new((*line).split_whitespace().map(|x| x.parse::<T>().unwrap()))
  |                                                                ^^^^^^
  |

What do you say?