Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nom's error is referencing data owned by the calling function #1706

Open
absoludity opened this issue Nov 3, 2023 · 2 comments
Open

Nom's error is referencing data owned by the calling function #1706

absoludity opened this issue Nov 3, 2023 · 2 comments

Comments

@absoludity
Copy link

absoludity commented Nov 3, 2023

Prerequisites

Here are a few things you should provide to help me understand the issue:

[package]
name = "repro_nom_error_issue"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
nom = "7.1.3"

Test case

I've created an example with the work around at:

https://github.com/absoludity/repro_nom_error_issue/blob/df4e339accb4a4ddb40c9c781f1febca1b4dd73f/src/main.rs#L1-L26 :

use nom::bytes::complete::tag;
use nom::multi::many0;
use nom::IResult;
use std::error::Error;

fn parser(s: &str) -> IResult<&str, Vec<&str>> {
    many0(tag("abc"))(s)
}

fn main() -> Result<(), Box<dyn Error>> {
    // In actual code, the input comes as a string through:
    // let input = std::fs::read_to_string(INPUT_FILE_PATH)?
    let input = "abcabc123".to_string();

    // This following fails, without mapping the error:

    // let (remaining, parsed) = parser(&input)?;

    // Whereas it works fine if I ensure that the content of the error is owned
    // (which is internally cloning the &str content, I assume):
    let (remaining, parsed) = parser(&input).map_err(|e| e.map_input(|s| s.to_owned()))?;

    assert_eq!(remaining, "123");
    assert_eq!(parsed, vec!["abc", "abc"]);
    Ok(())
}

As you can see, it will compile fine if I map the error input to ensure it is owned, but without that map, the compiler won't compile as it appears the error content is referencing the original input data, which is owned by the calling function:

$ cargo run       
   Compiling repro_nom_error_issue v0.1.0 (/xxx/repro_nom_error_issue)
error[E0515]: cannot return value referencing local variable `input`
  --> src/main.rs:17:31
   |
17 |     let (remaining, parsed) = parser(&input)?;
   |                               ^^^^^^^------^^
   |                               |      |
   |                               |      `input` is borrowed here
   |                               returns a value referencing data owned by the current function

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

If this is an actual error that could be improved in nom (as opposed to me missing something obvious here), and you think it might be a good first issue, I'd be happy to jump in and fix it.

@jchidley
Copy link

jchidley commented Jan 6, 2025

Did you work out if this was an error in nom or some usage problem? I've been tearing my hair out for several hours. Your work around gets rid of the error.

@marcdejonge
Copy link

This seems like a standard Rust borrow checker challenge. The error type of IResult is actually a nom::error::Error<I>, which means that the error has a reference to the input. So the error you get back when the parsing the input contains a reference to the place where the error occurred.

Now, the lifecycle in your example is limited to within the main function, but you want the return type of the function to point to that. That is something that you can't do, because you would be pointing to data that is actually already dropped. And Rust gives you this borrow checking warning.

This is actually completely expected behaviour. If you want to handle this, you should handle the error part of the result inside the main function and not return it. I typically find it bad practice to let the main function return a result. The main function should always be void. If you want to output something, use the .expect("some description") option of the Result type to easily print instead of the ? at the end of line where you parse.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants