生命周期省略

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 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 {}
}

'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,
};
}

请注意,如果 staticconst 项包含函数或闭包引用,而这些引用本身也包含引用,则编译器将首先尝试标准省略规则。如果无法通过其常用规则解析生命周期,则会报错。例如

#![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
}