函数返回值的生存期错误



这是我试图实现的一段代码的简化版本:

struct FirstStruct
{
a:  i8,
}
impl FirstStruct
{
fn get(&self) -> Option<&str>
{
Some("aaa")
}
}
pub struct SecondStruct<'a>
{
pub name:           Option<&'a str>,
}
impl<'a> SecondStruct<'a>
{
fn extract_string(obj: &/*'a*/ FirstStruct) -> Option<&'a str>
{
obj.get() //this is where the error happen
}
pub fn from_string() -> SecondStruct<'a>
{
let obj = FirstStruct{a: 1};
SecondStruct{
name:       SecondStruct::extract_string(&obj),
}
}
}
fn main()
{
let g_def_res = SecondStruct::from_string();
}

此代码引发以下错误:

test2.rs:23:13: 23:18 error: cannot infer an appropriate lifetime for autoref due to conflicting requirements
test2.rs:23         obj.get() //this is where the error happen
^~~~~
test2.rs:21:5: 24:6 help: consider using an explicit lifetime parameter as shown: fn extract_string(obj: &'a FirstStruct) -> Option<&'a str>
test2.rs:21     fn extract_string(obj: &FirstStruct) -> Option<&'a str>
test2.rs:22     {
test2.rs:23         obj.get() //this is where the error happen
test2.rs:24     }
error: aborting due to previous error

应用所提出的解决方案会抛出以下错误:

test2.rs:30:55: 30:58 error: `obj` does not live long enough
test2.rs:30             name:       SecondStruct::extract_string(&obj),
^~~
test2.rs:27:5: 32:6 note: reference must be valid for the lifetime 'a as defined on the block at 27:4...
test2.rs:27     {
test2.rs:28         let obj = FirstStruct{a: 1};
test2.rs:29         SecondStruct{
test2.rs:30             name:       SecondStruct::extract_string(&obj),
test2.rs:31         }
test2.rs:32     }
test2.rs:28:37: 32:6 note: ...but borrowed value is only valid for the block suffix following statement 0 at 28:36
test2.rs:28         let obj = FirstStruct{a: 1};
test2.rs:29         SecondStruct{
test2.rs:30             name:       SecondStruct::extract_string(&obj),
test2.rs:31         }
test2.rs:32     }
error: aborting due to previous error

总结:

如何说FirstStruct::get的返回值的生存期必须为[SecondStruct::from_str的返回值|结构体生存期'a]之一?我想两者指的是同一件事?

pub fn from_string() -> SecondStruct<'a> {
let obj = FirstStruct { a: 1 };
SecondStruct {
name: SecondStruct::extract_string(&obj),
}
}

此代码表示"我将返回一个具有生存期'aSecondStruct"。代码的调用者可以确定'a的生存期长度。这几乎不是你想要的!

// Lifetime elision means the method is equivalent to this
// fn get<'a>(&'a self) -> Option<&'a str>
fn get(&self) -> Option<&str> {
Some("aaa")
}

此代码使用表示返回的字符串将与self的寿命一样长。

把这两个概念放在一起,你就能理解你的错误。变量obj仅在函数调用处于活动状态时才被定义为有效。但是,您正试图在调用之外返回对结构内部工作的引用!实际上,您正试图在调用方决定的任何任意生存期内返回它!这是Rust防止你开枪打自己的脚,为Rust欢呼!


那么你如何解决你的问题呢?对于提供的示例代码,最简单的方法是只使用'static生存期:

struct FirstStruct { a: i8 }
impl FirstStruct {
fn get(&self) -> Option<&'static str> { Some("aaa") }
}
pub struct SecondStruct<'a> { name: Option<&'a str> }
impl<'a> SecondStruct<'a> {
fn extract_string(obj: &FirstStruct) -> Option<&'static str> { obj.get() }
pub fn from_string() -> SecondStruct<'static> {
let obj = FirstStruct { a: 1 };
SecondStruct { name: SecondStruct::extract_string(&obj) }
}
}
fn main() {
let g_def_res = SecondStruct::from_string();
}

但这可能不是你真正想要的。接下来要尝试的是FirstStruct嵌入到SecondStruct中,并简单地委托给它。另一个选项是从&str移动到String-String拥有字符串数据,因此您可以将所有权从First转移到Second

无论您做什么,都必须确保字符串数据源的寿命超过对from_string的函数调用。


FirstStruct::get的返回值已在堆栈上分配,或者已在堆上分配。

这比这更棘手。返回值在堆栈上总是。也就是说,Option<&str>占用了堆栈上的空间。&str可能包含一个指向堆栈或堆上分配的东西的指针,这段代码并不知道。您所知道的是,指向值保证在特定FirstStruct项目的生命周期内有效。

你不拥有字符串,所以你不能转移所有权。

我无法移动FirstStruct,因为它来自另一个库(rustc serialize

我不知道你的意思。如果你有一个对象,那么你可以把它嵌入到你的对象中。它来自另一个板条箱这一事实并不起作用。如果你有对某个事物的引用,那么你仍然可以捕获该引用,但你的对象必须比该引用活得更短(这样它就永远不会无效)。

展开Option,更新为字符串并在Option中重写是一个很大的样板。

你看过Option::map吗?它使这类事情非常简洁。结合From,您可以编写一个非常简短的东西来将Option<&str>转换为Option<String>:

// fn is just to establish some types, you'd just use the `.map` call in real code
fn foo(a: Option<&str>) -> Option<String> {
a.map(From::from)
}

最新更新