Consider this snippet:
use thiserror::error;
pub fn parse_something(input: &str) -> Result<i32, MyError> {
let (_, number) = nom::number::complete::i32(input)?;
Ok(number)
}
// define error
#[derive(Error, Debug, PartialEq)]
pub enum MyError {
#[error("Parsing error in the input")]
ParseError(#[from] nom::Err<nom::error::Error<&'static str>>),
}
It won't compile because apparently the
borrowed data escapes outside the function
I'm a bit stumped. I just want to catch all the parsing errors in a nice way and return that variant.
I could always use map_err
in parse_something
, but I thought the #[from]
macro exists precisely so I don't have to do it manually.
The reason it's not working in your snippet is because the lifetime of input
is elided, so the Rust compiler assigns a distinct lifetime (e.g. 'a
) to input
. Your function (after expanding the lifetime) looks like this to the compiler:
pub fn parse_something<'a>(input: &'a str) -> Result<i32, MyError>
The lifetime 'a
, unless manually specified by you, is assumed to be shorter than 'static
. The error is expecting the lifetime of its &str
to be 'static
, so the elided lifetime is too short, and the compiler complains.
As you probably found if you tried giving the error any lifetime shorter than 'static
, thiserror
doesn't let you give references any non-static lifetimes because std::error::Error requires the source is dyn Error + 'static
. So, non-static lifetimes are out of the window as well.
To fix this, you need to make input
have a 'static
lifetime. This snippet below compiles:
use thiserror::Error;
pub fn parse_something(input: &'static str) -> Result<i32, MyError> {
let (_, number) = nom::character::complete::i32(input)?;
Ok(number)
}
// define error
#[derive(Error, Debug, PartialEq)]
pub enum MyError {
#[error("Parsing error in the input")]
ParseError(#[from] nom::Err<nom::error::Error<&'static str>>),
}
It may not be possible to have input
be 'static
, though. In cases where input
is not static, you will need to map the error into a type that contains a String
. It could look something like this:
use thiserror::Error;
pub fn parse_something(input: &str) -> Result<i32, MyError> {
let (_, number) = nom::character::complete::i32(input)
.map_err(|e: nom::Err<nom::error::Error<&str>>| e.map_input(|input| input.to_string()))?;
Ok(number)
}
// define error
#[derive(Error, Debug, PartialEq)]
pub enum MyError {
#[error("Parsing error in the input")]
ParseError(#[from] nom::Err<nom::error::Error<String>>),
}
You can find some more information about difficulty dealing with nom
's errors in this post on the Rust forums.