我有以下代码(没有多大意义,只是最小化的测试用例):
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" }
}
在第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>)
答案 0 :(得分:6)
这里发生了很多事情。为了解释它,让我们来看看这个更简化的问题版本。为避免隐藏内容,我还将try!()
替换为explicit form。
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![]);
}
这是第一件可能令人困惑的事情:try!
调用std::convert::From::from(e)
(或等效,但更短:e.into()
)。这意味着有趣的地方只是标有#1
的部分。
那么那里发生什么?
调用into()
意味着Rust编译器必须为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>
在这里,我们看到另一个关键点:类型Box<Error + 'a>
具有生命周期约束。简单英语中的类型将读取类似于:“实现特征Error
的盒装类型,并且至少在生命周期'a
中存活。”
现在我们看一下我们的函数签名,看看Result<(), Box<Error>>
:它没有生命周期限制!好吧......我们没有明确写出一个,但编译器总是添加一个,因为它是处理这种盒装特征的唯一方法。 This RFC是关于编译器自动添加的默认生存期限。对于Box<Trait>
,添加了生命周期'static
。因此,明确写出的返回类型是Result<(), Box<Error + 'static>>
。
要编译代码,请将输入参数和的显式生存期添加到输出类型,如下所示:
fn get_things<'a>(doc: &'a Vec<u32>) -> Result<(), Box<Error + 'a>> {
...
}
这样你就可以避免默认情况下添加的'static
生命周期绑定,并告诉编译器你的框中的东西只有输入参数才能生命。