生命周期省略

Rust 有一些规则允许在编译器可以推断出合理的默认选择的情况下省略生命周期。

函数中的生命周期省略

为了使常见模式更加符合人体工程学,可以在 函数项函数指针闭包特征 签名中_省略_生命周期参数。以下规则用于推断省略生命周期的生命周期参数。省略无法推断的生命周期参数是错误的。占位符生命周期 '_' 也可以用于以相同的方式推断生命周期。对于路径中的生命周期,建议使用 '_'。特征对象生命周期遵循下方讨论的不同规则。

  • 参数中的每个省略的生命周期都将成为一个不同的生命周期参数。
  • 如果参数中只使用了一个生命周期(省略或未省略),则该生命周期将分配给_所有_省略的输出生命周期。

在方法签名中还有另一条规则

  • 如果接收者的类型为 &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
}
}

默认特征对象生命周期

特征对象 持有的引用的假定生命周期称为其_默认对象生命周期边界_。这些在 RFC 599 中定义,并在 RFC 1156 中进行了修改。

当完全省略生命周期边界时,将使用这些默认对象生命周期边界,而不是上面定义的生命周期参数省略规则。如果 '_' 用作生命周期边界,则该边界遵循通常的省略规则。

如果特征对象用作泛型类型的类型参数,则首先使用包含类型来尝试推断边界。

  • 如果包含类型中存在唯一边界,则该边界为默认边界
  • 如果包含类型中存在多个边界,则必须指定显式边界

如果以上规则均不适用,则使用特征上的边界

  • 如果特征定义为具有单个生命周期_边界_,则使用该边界。
  • 如果对任何生命周期边界使用 'static,则使用 'static
  • 如果特征没有生命周期边界,则在表达式中推断生命周期,在表达式外为 '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
}