函数返回值的生命周期错误

时间:2015-04-24 22:37:12

标签: rust lifetime

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

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 |的返回值的生命周期。 struct lifetime 'a]?我认为两者都指的是同一件事?

1 个答案:

答案 0 :(得分:1)

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

此代码显示&#34;我将返回SecondStruct,其生命周期为'a&#34;。代码的调用者可以确定生命周期'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::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)
}