Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Сокрытие времён жизни

В 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);                                   // скрыто
fn print2(s: &'_ str);                                // также скрыто
fn print3<'a>(s: &'a str);                            // раскрыто

fn debug1(lvl: usize, s: &str);                       // скрыто
fn debug2<'a>(lvl: usize, s: &'a str);                // раскрыто

fn substr1(s: &str, until: usize) -> &str;            // скрыто
fn substr2<'a>(s: &'a str, until: usize) -> &'a str;  // раскрыто

fn get_mut1(&mut self) -> &mut dyn T;                 // скрыто
fn get_mut2<'a>(&'a mut self) -> &'a mut dyn T;       // раскрыто

fn args1<T: ToCStr>(&mut self, args: &[T]) -> &mut Command;                  // скрыто
fn args2<'a, 'b, T: ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command; // раскрыто

fn other_args1<'a>(arg: &str) -> &'a str;             // скрыто
fn other_args2<'a, 'b>(arg: &'b str) -> &'a str;      // раскрыто

fn new1(buf: &mut [u8]) -> Thing<'_>;                 // скрыто - предпочтительно
fn new2(buf: &mut [u8]) -> Thing;                     // скрыто
fn new3<'a>(buf: &'a mut [u8]) -> Thing<'a>;          // раскрыто
}

type FunPtr1 = fn(&str) -> &str;                      // скрыто
type FunPtr2 = for<'a> fn(&'a str) -> &'a str;        // раскрыто

type FunTrait1 = dyn Fn(&str) -> &str;                // скрыто
type FunTrait2 = dyn for<'a> Fn(&'a str) -> &'a str;  // раскрыто
}
#![allow(unused)]
fn main() {
// Следующие примеры показывают ситуации, где не разрешено скрывать
// параметр времени жизни.

trait Example {
// Не может быть выведено, так как нет параметров для вывода.
fn get_str() -> &str;                                 // НЕЗАКОННО

// Не может быть выведено, неясно, заимствовано ли из первого или второго параметра.
fn frob(s: &str, t: &str) -> &str;                    // НЕЗАКОННО
}
}

Времена жизни объектов трейтов по умолчанию

Предполагаемое время жизни ссылок, удерживаемых объектом трейта, называется его ограничением времени жизни объекта по умолчанию. Они были определены в RFC 599 и изменены в RFC 1156.

Эти ограничения времени жизни объекта по умолчанию используются вместо правил скрытия параметров времени жизни, определённых выше, когда ограничение времени жизни опущено полностью.

Если '_ используется как ограничение времени жизни, то ограничение следует обычным правилам сокрытия.

Если объект трейта используется как типовой аргумент обобщённого типа, то содержащий тип сначала используется для попытки вывести ограничение.

  • Если есть уникальное ограничение из содержащего типа, то оно используется по умолчанию.
  • Если есть более одного ограничения из содержащего типа, то явное ограничение должно быть указано.

Если ни одно из этих правил не применяется, то используются ограничения на трейте:

  • Если трейт определён с единственным ограничением времени жизни, то это ограничение используется.
  • Если 'static используется для любого ограничения времени жизни, то используется 'static.
  • Если у трейта нет ограничений времени жизни, то время жизни выводится в выражениях и является 'static вне выражений.
#![allow(unused)]
fn main() {
// Для следующего трейта...
trait Foo { }

// Эти два одинаковы, потому что Box<T> не имеет ограничения времени жизни на T
type T1 = Box<dyn Foo>;
type T2 = Box<dyn Foo + 'static>;

// ...и так же эти:
impl dyn Foo {}
impl dyn Foo + 'static {}

// ...так же эти, потому что &'a T требует T: 'a
type T3<'a> = &'a dyn Foo;
type T4<'a> = &'a (dyn Foo + 'a);

// std::cell::Ref<'a, T> также требует T: 'a, так что эти одинаковы
type T5<'a> = std::cell::Ref<'a, dyn Foo>;
type T6<'a> = std::cell::Ref<'a, dyn Foo + 'a>;
}
#![allow(unused)]
fn main() {
// Это пример ошибки.
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>;
//                                  ^^^^^^^
// Ошибка: ограничение времени жизни для этого объектного типа не может быть выведено из контекста
}

Обратите внимание, что самый внутренний объект устанавливает ограничение, так что &'a Box<dyn Foo> всё ещё &'a Box<dyn Foo + 'static>.

#![allow(unused)]
fn main() {
// Для следующего трейта...
trait Bar<'a>: 'a { }

// ...эти два одинаковы:
type T1<'a> = Box<dyn Bar<'a>>;
type T2<'a> = Box<dyn Bar<'a> + 'a>;

// ...и так же эти:
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}
// Разрешено как `for<'a> fn(&'a str) -> &'a str`.
const RESOLVED_SINGLE: fn(&str) -> &str = |x| x;

// Разрешено как `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!()}
// Недостаточно информации, чтобы ограничить время жизни возвращаемой ссылки
// относительно времён жизни аргументов, так что это ошибка.
const RESOLVED_STATIC: &dyn Fn(&Foo, &Bar) -> &Baz = &somefunc;
//                                            ^
// возвращаемый тип этой функции содержит заимствованное значение, но сигнатура
// не указывает, заимствовано ли оно из аргумента 1 или аргумента 2
}