为FFI返回带有生命周期的结构

时间:2017-03-02 19:29:53

标签: rust ffi lifetime

我试图将一个函数从woothee-rust crate暴露给Ruby。为此,我正在解析输入字符串并尝试将结果作为C结构返回。我遇到了一个问题,解析器的生命周期“活不够久”。我不确定为什么解析器的生命周期必须超过函数。

#![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()
}

这是我得到的错误:

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

1 个答案:

答案 0 :(得分:4)

扩展终身省略后,Parser::parse的签名为

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

用语言来说,就是:

  

鉴于对Parser的引用和对str的引用,可能会返回包含对WootheeResult或其某些组成部分的一个或多个引用的Parser

但是,当函数退出时,您会立即销毁Parser。所以,不,你不能这样做,因为这样做会允许访问对未定义内存的引用。 Rust阻止您在程序中引入安全漏洞。

回到错误信息,希望现在更有意义了:

  • parser活得不够长”
  • “借来的价值必须在一生中有效'a”

我没有深入研究woothee的实现,但这个签名非常令人惊讶。我能理解它是否返回对已解析的字符串的引用,而不是解析器。这特别令人惊讶,因为该方法需要&self - 它不太可能基于解析修改内部,所以为什么它会返回对它自己的引用?

查看Parser::new的实施情况,生命周期似乎来自dataset::get_default_dataset

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

Is there any way to return a reference to a variable created in a function?中所述,不能返回对局部变量的引用,除非该局部变量为'static。虽然我没有尝试过这个警告,但我确信可以更改箱子以从'static返回get_default_dataset个字符串,然后parse

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

WootheeResult将是WootheeResult<'static>,然后事情会“正常”。