2

I am trying to expose a function from the woothee-rust crate to Ruby. To do this, I am parsing an input string and attempting to return the result as a C struct. I run into an issue where the lifetime of the parser "does not live long enough". I am not sure why the parser's lifetime must live past the function.

#![feature(libc)]
#![feature(cstr_to_str)]
#![feature(cstr_memory)]
extern crate libc;
extern crate woothee;

use woothee::parser::{Parser,WootheeResult};
use std::ffi::{CStr,CString};

#[no_mangle]
pub extern fn parse<'a>(ua_string: *const libc::c_char) -> WootheeResult<'a> {
    let input = unsafe { CStr::from_ptr(ua_string) };
    let parser = Parser::new();
    parser.parse(input.to_str().unwrap()).unwrap()
}

Here is the error I get:

error: `parser` does not live long enough
  --> src/lib.rs:14:5
   |
14 |     parser.parse(input.to_str().unwrap()).unwrap()
   |     ^^^^^^ does not live long enough
15 | }
   | - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'a as defined on the body at 11:77...
  --> src/lib.rs:11:78
   |
11 |   pub extern fn parse<'a>(ua_string: *const libc::c_char) -> WootheeResult<'a> {
   |  ______________________________________________________________________________^ starting here...
12 | |     let input = unsafe { CStr::from_ptr(ua_string) };
13 | |     let parser = Parser::new();
14 | |     parser.parse(input.to_str().unwrap()).unwrap()
15 | | }
   | |_^ ...ending here
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
ianks
  • 1,708
  • 19
  • 15

1 Answers1

7

After expanding lifetime elision, the signature of Parser::parse is

fn parse<'a, 'b>(&'a self, agent: &'b str) -> Option<WootheeResult<'a>>

In words, that is:

Given a reference to a Parser and a reference to a str, maybe return a WootheeResult that contains one or more references to the Parser or some component of it.

However, you immediately destroy the Parser when the function exits. So, no, you cannot do this because to do so would allow accessing a reference to undefined memory. Rust has prevented you from introducing a security hole into your program.

Returning to the error message, hopefully it makes more sense now:

  • "parser does not live long enough"
  • "borrowed value must be valid for the lifetime 'a"

I haven't dug into the implementation of woothee, but this signature is pretty surprising. I could understand if it returned references to the string that was parsed, but not to the parser. This is especially surprising as the method takes &self — it's unlikely to be modifying the internals based on the parsing, so why would it return a reference to itself?

Looking at the implementation of Parser::new, the lifetime appears to be driven from dataset::get_default_dataset:

pub fn get_default_dataset<'a>() -> HashMap<&'a str, WootheeResult<'a>>

As stated in Is there any way to return a reference to a variable created in a function?, you can't return a reference to a local variable unless that local variable is 'static. With the caveat that I haven't tried this, I'm 80% sure that the crate could be changed to return 'static strings from get_default_dataset, then parse would be

impl<'a> Parser<'a> {
    fn parse<'b>(&'b self, agent: &'b str) -> Option<WootheeResult<'a>>
}

And the WootheeResult would be WootheeResult<'static>, and then things would "just work".

Community
  • 1
  • 1
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Ok the error makes sense now. However, I do not know how to make the parser live past the lifetime of the function. What is the best practice for doing this? – ianks Mar 02 '17 at 20:26
  • 1
    @ianks the only solution to ensuring that the parser lives longer than the function call is to create the parser outside the function and then pass in the reference. By construction, the parser will live longer than the function. – Shepmaster Mar 02 '17 at 23:17
  • There is another option; return the parser as well as the `WootheeResult`. One way to do this is using [owning_ref](https://crates.io/crates/owning_ref), which lets you return an object which behaves as a reference to `WootheeResult` but contains an owned parser. – Chris Emerson Mar 03 '17 at 10:12
  • @ChrisEmerson a good point, but since this is an `extern` function, I don't think that returning complex structs is going to be a great fit. Also, the function cannot mutate anything to be valid for owning_ref, which is true in this case but doesn't make sense... – Shepmaster Mar 03 '17 at 15:04