0

I'm using a nom parser to return a Vec<PdlItem>. I don't understand why the parsable_buffer needs to live beyond the last line; or how to get around the error. res owns the Vec<PdlItem> so my expectation is that it would be moved into the result.

I have no use for parsable_buffer, what have I missed?

pub enum PdlError {
    ParseError,
}

#[derive(Debug, PartialEq, Clone)]
pub enum PdlItem<'a> {
    FOO,
    STR(&'a str, Option<&'a str>, &'a str),
    NUM(&'a str, Option<&'a str>, f32),
    VNUM(&'a str, Option<&'a str>, Vec<f32>),
    VSTR(&'a str, Option<&'a str>, Vec<&'a str>),
}

pub fn pdl_to_tokens(buffer: &[u8]) -> Result<Vec<PdlItem>, PdlError> {
    // Remove comments
    let parsable_buffer: Vec<u8> = remove_comments(buffer);
    let res: IResult<&[u8], Vec<PdlItem>> = parse_pdl(&parsable_buffer);
    match res {
        IResult::Done(_, o) => {
            println!("Parsed ok {}", o.len());
            Ok(o.to_owned())
        } // Also tried .clone()
        IResult::Incomplete(i) => {
            println!("{:?}", i);
            Err(PdlError::ParseError)
        }
        IResult::Error(e) => {
            println!("Error {}", e);
            Err(PdlError::ParseError)
        }
    }
}

The error is

error[E0597]: `parsable_buffer` does not live long enough
  --> src\pdl_serialiser.rs:24:26
   |
24 |     let res = parse_pdl(&parsable_buffer);
   |                          ^^^^^^^^^^^^^^^ does not live long enough
...
31 | }
   | - borrowed value only lives until here
   |
note: borrowed value must be valid for the anonymous lifetime #1 defined on the function body at 21:1...
  --> src\pdl_serialiser.rs:21:1
   |
21 | / pub fn pdl_to_tokens(buffer : &[u8]) -> Result<Vec<PdlItem>,PdlError> {
22 | |     // Remove comments
23 | |     let parsable_buffer = remove_comments(buffer);
24 | |     let res = parse_pdl(&parsable_buffer);
...  |
30 | |     }
31 | | }
   | |_^
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Delta_Fore
  • 3,079
  • 4
  • 26
  • 46
  • What piece of memory do you think all of the `&'a str`s in `res` refer to? – Shepmaster Nov 20 '17 at 18:35
  • 2
    TL;DR the duplicates — `remove_comments` creates a new `Vec` which is owned by the function. `parse_pdl` returns a bunch of `PdlItem`s that point at the memory inside that `Vec`. You then try to return those references from the function, although the `Vec` will be dropped when the function exits. In other languages, this would cause a segfault, allow for arbitrary code execution, eat your laundry, etc. Instead, you need to (a) return owned data structures (`String` instead of `&str`) or (b) pass in `parsable_buffer` from outside of this function. – Shepmaster Nov 20 '17 at 18:50
  • Oh, it only dawned on me after quite a few read through's of your explanation. That's was not obvious to me – Delta_Fore Nov 20 '17 at 20:48
  • Ownership semantics in Rust definitely take some time to get used to, but they really are its greatest strength. – loganfsmyth Nov 21 '17 at 02:06
  • Though this all worked and I converted PdlItem to having `owned` types; what I thought would be that everything would reference `buffer &[u8]` and thus `parsable_buffer` would reference the original buffer. Maybe there's a more efficient way - a new question? – Delta_Fore Nov 21 '17 at 12:19
  • 1
    @Ronnie That's possible, but not as long as `parsable_buffer` is a `Vec`, which owns its contents; you need to modify `remove_comments` so that it returns some type that borrows from `buffer`, like `Vec<&'a [u8]>` or perhaps a custom `struct ParseableBuffer<'a>` that implements `Iterator`. Obviously this requires changes to `parse_pdl` as well. (And yes, this would definitely be a new question.) – trent Nov 21 '17 at 16:43

0 Answers0