生命周期省略(Lifetime Elision)
Rust 有规则允许在编译器可以推断出合理默认选择的各种地方省略生命周期。
函数中的生命周期省略
为了使常见模式更符合人体工程学,可以在函数项、函数指针和闭包 trait签名中省略生命周期参数。以下规则用于推断省略生命周期的生命周期参数。
省略无法推断的生命周期参数是错误的。
占位符生命周期 '_ 也可以用于以相同方式推断生命周期。对于路径中的生命周期,首选使用 '_。
Trait 对象生命周期遵循下面讨论的不同规则。
- 参数中的每个省略生命周期都成为一个不同的生命周期参数。
- 如果参数中恰好使用了一个生命周期(省略与否),则该生命周期被分配给所有省略的输出生命周期。
在方法签名中有另一个规则
- 如果接收者类型为
&Self或&mut Self,则该对Self的引用的生命周期被分配给所有省略的输出生命周期参数。
示例:
#![allow(unused)]
fn main() {
trait T {}
trait ToCStr {}
struct Thing<'a> {f: &'a i32}
struct Command;
trait Example {
fn print1(s: &str); // elided
fn print2(s: &'_ str); // also elided
fn print3<'a>(s: &'a str); // expanded
fn debug1(lvl: usize, s: &str); // elided
fn debug2<'a>(lvl: usize, s: &'a str); // expanded
fn substr1(s: &str, until: usize) -> &str; // elided
fn substr2<'a>(s: &'a str, until: usize) -> &'a str; // expanded
fn get_mut1(&mut self) -> &mut dyn T; // elided
fn get_mut2<'a>(&'a mut self) -> &'a mut dyn T; // expanded
fn args1<T: ToCStr>(&mut self, args: &[T]) -> &mut Command; // elided
fn args2<'a, 'b, T: ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command; // expanded
fn other_args1<'a>(arg: &str) -> &'a str; // elided
fn other_args2<'a, 'b>(arg: &'b str) -> &'a str; // expanded
fn new1(buf: &mut [u8]) -> Thing<'_>; // elided - preferred
fn new2(buf: &mut [u8]) -> Thing; // elided
fn new3<'a>(buf: &'a mut [u8]) -> Thing<'a>; // expanded
}
type FunPtr1 = fn(&str) -> &str; // elided
type FunPtr2 = for<'a> fn(&'a str) -> &'a str; // expanded
type FunTrait1 = dyn Fn(&str) -> &str; // elided
type FunTrait2 = dyn for<'a> Fn(&'a str) -> &'a str; // expanded
}
#![allow(unused)]
fn main() {
// The following examples show situations where it is not allowed to elide the
// lifetime parameter.
trait Example {
// Cannot infer, because there are no parameters to infer from.
fn get_str() -> &str; // ILLEGAL
// Cannot infer, ambiguous if it is borrowed from the first or second parameter.
fn frob(s: &str, t: &str) -> &str; // ILLEGAL
}
}
默认 trait 对象生命周期
trait 对象持有的引用的假定生命周期称为其_默认对象生命周期约束_。这些在 RFC 599 中定义,并在 RFC 1156 中修订。
当生命周期约束被完全省略时,使用这些默认对象生命周期约束代替上面定义的生命周期参数省略规则。
如果 '_ 用作生命周期约束,则约束遵循通常的省略规则。
如果 trait 对象用作泛型类型的类型参数,则首先使用包含类型来尝试推断约束。
- 如果包含类型有唯一的约束,则该约束是默认值。
- 如果包含类型有多个约束,则必须指定显式约束。
如果这些规则都不适用,则使用 trait 上的约束:
- 如果 trait 定义时具有单个生命周期_约束_,则使用该约束。
- 如果任何生命周期约束使用
'static,则使用'static。
- 如果 trait 没有生命周期约束,则在表达式中推断生命周期,在表达式外部为
'static。
#![allow(unused)]
fn main() {
// For the following trait...
trait Foo { }
// These two are the same because Box<T> has no lifetime bound on T
type T1 = Box<dyn Foo>;
type T2 = Box<dyn Foo + 'static>;
// ...and so are these:
impl dyn Foo {}
impl dyn Foo + 'static {}
// ...so are these, because &'a T requires T: 'a
type T3<'a> = &'a dyn Foo;
type T4<'a> = &'a (dyn Foo + 'a);
// std::cell::Ref<'a, T> also requires T: 'a, so these are the same
type T5<'a> = std::cell::Ref<'a, dyn Foo>;
type T6<'a> = std::cell::Ref<'a, dyn Foo + 'a>;
}
#![allow(unused)]
fn main() {
// This is an example of an error.
trait Foo { }
struct TwoBounds<'a, 'b, T: ?Sized + 'a + 'b> {
f1: &'a i32,
f2: &'b i32,
f3: T,
}
type T7<'a, 'b> = TwoBounds<'a, 'b, dyn Foo>;
// ^^^^^^^
// Error: the lifetime bound for this object type cannot be deduced from context
}
请注意,最内层对象设置约束,因此 &'a Box<dyn Foo> 仍然是 &'a Box<dyn Foo + 'static>。
#![allow(unused)]
fn main() {
// For the following trait...
trait Bar<'a>: 'a { }
// ...these two are the same:
type T1<'a> = Box<dyn Bar<'a>>;
type T2<'a> = Box<dyn Bar<'a> + 'a>;
// ...and so are these:
impl<'a> dyn Bar<'a> {}
impl<'a> dyn Bar<'a> + 'a {}
}
const 和 static 省略
引用类型的常量和静态声明都具有隐式 'static 生命周期,除非指定了显式生命周期。因此,上面涉及 'static 的常量声明可以不带生命周期编写。
#![allow(unused)]
fn main() {
// STRING: &'static str
const STRING: &str = "bitstring";
struct BitsNStrings<'a> {
mybits: [u32; 2],
mystring: &'a str,
}
// BITS_N_STRINGS: BitsNStrings<'static>
const BITS_N_STRINGS: BitsNStrings<'_> = BitsNStrings {
mybits: [1, 2],
mystring: STRING,
};
}
请注意,如果 static 或 const 项包含函数或闭包引用(它们本身包含引用),编译器将首先尝试标准省略规则。如果无法通过其通常规则解析生命周期,则会产生错误。例如:
#![allow(unused)]
fn main() {
struct Foo;
struct Bar;
struct Baz;
fn somefunc(a: &Foo, b: &Bar, c: &Baz) -> usize {42}
// Resolved as `for<'a> fn(&'a str) -> &'a str`.
const RESOLVED_SINGLE: fn(&str) -> &str = |x| x;
// Resolved as `for<'a, 'b, 'c> Fn(&'a Foo, &'b Bar, &'c Baz) -> usize`.
const RESOLVED_MULTIPLE: &dyn Fn(&Foo, &Bar, &Baz) -> usize = &somefunc;
}
#![allow(unused)]
fn main() {
struct Foo;
struct Bar;
struct Baz;
fn somefunc<'a,'b>(a: &'a Foo, b: &'b Bar) -> &'a Baz {unimplemented!()}
// There is insufficient information to bound the return reference lifetime
// relative to the argument lifetimes, so this is an error.
const RESOLVED_STATIC: &dyn Fn(&Foo, &Bar) -> &Baz = &somefunc;
// ^
// this function's return type contains a borrowed value, but the signature
// does not say whether it is borrowed from argument 1 or argument 2
}