"静态生命从何而来"

vir*_*tor 2 rust

我有以下代码(没有多大意义,只是一个最小化的测试用例):

extern crate rustc_serialize;

use rustc_serialize::json::Json;
use std::error::Error;

struct SomeStruct;

#[derive(Debug)]
enum SomeError<'a> {
    Something(&'a str),
    Other,
}

fn do_stuff(doc: &Json) -> Result<SomeStruct, SomeError> {
    Ok(SomeStruct)
}

fn get_things(doc: &Vec<Json>) -> Result<SomeStruct, Box<Error>> {
    let res = try!(doc.get(0).ok_or(SomeError::Other));
    Ok(try!(do_stuff(&res)))                             //// line 20
}

fn main() {
    let _ = get_things(&vec!(Json::Null));
}

impl<'a> std::fmt::Display for SomeError<'a> {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        write!(f, "blah")
    }
}
impl<'a> Error for SomeError<'a> {
    fn description(&self) -> &str { "blah" }
}
Run Code Online (Sandbox Code Playgroud)

这在第20行出现类型不匹配时失败: expected std::result::Result<SomeStruct, Box<std::error::Error + 'static>>, found std::result::Result<SomeStruct, Box<std::error::Error>>

我不明白'static终身要求突然来自何处.如果我改变枚举使用Something(&'static str)它工作得很好,但为什么我不能在这里使用限制较少的值?

错误提到的doc是错误不能超过的借用内容......但这两种类型之间似乎没有任何关系.

完整错误:

error: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements [E0495]
  --> src/main.rs:19:24
19 |>     let res = try!(doc.get(0).ok_or(SomeError::Other));
   |>                        ^^^
src/main.rs:19:15: 19:55: note: in this expansion of try! (defined in <std macros>)
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the block at 18:65...
  --> src/main.rs:18:66
18 |> fn get_things(doc: &Vec<Json>) -> Result<SomeStruct, Box<Error>> {
   |>                                                                  ^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:19:20
19 |>     let res = try!(doc.get(0).ok_or(SomeError::Other));
   |>                    ^^^
src/main.rs:19:15: 19:55: note: in this expansion of try! (defined in <std macros>)
note: but, the lifetime must be valid for the static lifetime...
note: ...so that types are compatible (expected std::result::Result<SomeStruct, Box<std::error::Error + 'static>>, found std::result::Result<SomeStruct, Box<std::error::Error>>)
 --> <std macros>:5:8
5 |> return $ crate :: result :: Result :: Err (
  |>        ^
src/main.rs:20:8: 20:28: note: in this expansion of try! (defined in <std macros>)
Run Code Online (Sandbox Code Playgroud)

Luk*_*odt 7

这里有很多事情要做.为了解释它,让我们来看看这个更简化的问题版本.为了避免隐藏东西,我也try!()用它的显式形式替换了它.

enum SomeError<'a> {
    Something(&'a str),
    Other,
}

// ... impls Debug, Display, Error for SomeError ...

fn do_stuff(doc: &u32) -> Result<(), SomeError> { Ok(()) }

fn get_things(doc: &Vec<u32>) -> Result<(), Box<Error>> {
    match do_stuff(&v[0]) {   // `try!` expands into this match 
        Ok(v) => Ok(v),
        Err(e) => Err(e.into()),
    }  //             ^^^^^^^^--- #1

}

fn main() {
    let _ = get_things(&vec![]);
}
Run Code Online (Sandbox Code Playgroud)

这是第一件可能令人困惑的事情:try!调用std::convert::From::from(e)(或等效,但更短:) e.into().这意味着有趣的地方只是标有的部分#1.

那么那里发生了什么?

呼叫into()意味着是生锈编译器来搜索一些实施Into<Box<Error>>SomeError.通过魔术impl<T, U> Into<U> for T where U: From<T>间接,编译器找到了一些可能有用的实现,特别是这个:

impl<'a, E: Error + 'a> From<E> for Box<Error + 'a>
Run Code Online (Sandbox Code Playgroud)

在这里,我们看到另一个关键点:类型Box<Error + 'a>具有生命周期约束.简单英语的类型可以是:"一种实现特性的盒装类型,Error至少在一生中是活着的'a".

现在我们看看我们的函数签名,看看Result<(), Box<Error>>:它没有生命周期!好吧......我们没有明确写出一个,但编译器总是添加一个,因为它是处理这种盒装特征的唯一方法.此RFC是关于编译器自动添加的默认生存期限.并且为Box<Trait>生命周期'static添加了界限.因此明确写出的返回类型是Result<(), Box<Error + 'static>>.

要使代码编译,请在输入参数输出类型中添加显式生存期,如下所示:

fn get_things<'a>(doc: &'a Vec<u32>) -> Result<(), Box<Error + 'a>> {
    ...
}
Run Code Online (Sandbox Code Playgroud)

这样你可以避免'static默认添加的生命周期绑定,并告诉编译器你的框中的东西只有你的输入参数生命.