简体   繁体   中英

Return struct with lifetime for FFI

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

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".

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM