Академический Документы
Профессиональный Документы
Культура Документы
Распространённые заблуждения о
временах жизни в Rust
Автор оригинала: kirill aka @pretzelhammer
Перевод
(прим. переводчика: времена жизни (lifetimes) — это одна из самых запутанных вещей
в Rust, которая часто вызывает затруднение у новичков, даже несмотря на
официальную документацию. Разъяснения по отдельным аспектам времён жизни
есть, но они все разбросаны по разным источникам и ответам на Stack Overflow.
Автор статьи собрал в одном месте и разъяснил множество связанных с временами
жизни вопросов, что и делает эту статью столь ценной (я и сам почерпнул новое
для себя отсюда). Я решил перевести её, чтобы дать возможность прочитать её
тем, кто не владеет английским в достаточной степени, чтобы свободно читать
оригинал, а также для того, чтобы повысить известность этой статьи среди
русскоязычного Rust-сообщества)
Оглавление
Вступление
Create PDF in your applications with the Pdfcrowd HTML to PDF API
Заблуждения
Заключение
Обсуждение
Create PDF in your applications with the Pdfcrowd HTML to PDF API
Контакты
Дальнейшее чтение
Вступление
Когда-то я был подвержен всем этим заблуждениям, сейчас же вижу, как многие
новички борются с ними. Некоторые используемые мной термины могут быть
нестандартными, поэтому для обозначения вкладываемого в них смысла я привожу
таблицу со значениями.
Термин Значение
Create PDF in your applications with the Pdfcrowd HTML to PDF API
Термин Значение
Заблуждения
В двух словах: время жизни переменной — это статически проверяемый компилятором
промежуток времени, в течение которого валидны обращения к данным, лежащим по
адресу, сохранённой в переменной. Следующие ~6500 слов я потрачу на более
подробный рассказ о том, где обычно возникает путаница.
Create PDF in your applications with the Pdfcrowd HTML to PDF API
Когда я впервые начал изучать Rust, я понял, что i32, &i32 и &mut i32 — это разные
типы. Я также понял, что некоторая переменная типа T представляет собой множество,
которое содержит все возможные типы. Однако, несмотря на то, что я понимал обе эти
вещи по отдельности, я не мог понять их в совокупности. Я наивно полагал, что в Rust
обобщённые типы работают так:
T содержит все владеющие типы. &T содержит все неизменяемо заимствованные типы.
&mut T содержит все изменяемо заимствованные типы. T, &T и &mut T —
непересекающиеся конечные множества. Красиво, просто, кратко, легко, интуитивно
понятно и совершенно неправильно. Вот как в Rust обобщённые типы работают по-
настоящему:
Примеры i32, &i32, &mut i32, &i32, &&i32, &mut i32, &mut &mut
&&i32, &mut &mut i32, ... &&mut i32, ... i32, &mut &i32, ...
Create PDF in your applications with the Pdfcrowd HTML to PDF API
T, &T и &mut T — бесконечные множества, так как тип можно заимствовать до
бесконечности. T является надмножеством обоих &T и &mut T, а &T и &mut T —
непересекающиеся множества. Вот пара примеров, подтверждающих эти
представления:
trait Trait {}
Create PDF in your applications with the Pdfcrowd HTML to PDF API
3 | impl<T> Trait for T {}
| ------------------- first implementation here
...
7 | impl<T> Trait for &mut T {}
| ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `&mut _`
Компилятор не позволяет нам определять реализацию Trait для &T и &mut T, потому
что она будет конфликтовать с реализацией Trait для T, которая уже включает в себя
все &T и &mut T. Программа ниже компилируется, как и ожидалось, так как &T и &mut T
не пересекаются:
trait Trait {}
Ключевые выводы
Create PDF in your applications with the Pdfcrowd HTML to PDF API
Ошибочные выводы
fn main() {
let str_literal: &'static str = "str literal";
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API
fn main() {
MUT_BYTES[0] = 99; // ошибка компиляции, изменение статической перемен
ной является небезопасной операцией
unsafe {
MUT_BYTES[0] = 99;
assert_eq!(99, MUT_BYTES[0]);
}
}
'static время жизни, вероятно, было названо так из-за времён жизни по умолчанию
static переменных, ведь так? Так что логично, что время жизни 'static должно
следовать тем же правилам, не так ли?
Ну, да, но тип, содержащий время жизни 'static, отличается от типа, ограниченного
временем жизни 'static. Последний может быть динамически создан во время
исполнения, безопасно и беспрепятственно изменён и удалён, а также может
существовать в течение произвольных промежутков времени.
Create PDF in your applications with the Pdfcrowd HTML to PDF API
&'static T — это неизменяемая ссылка на некоторый T, которую можно хранить
безопасно неопределенно долгое время, в том числе вплоть до конца работы
программы. Это возможно только в том случае, если сам T является неизменяемым и
не перемещается после создания ссылки. T не обязательно должен быть создан на
этапе компиляции. Вполне возможно генерировать произвольные динамически
выделяемые значения во время исполнения и возвращать 'static ссылки на них ценой
утечки памяти, например:
use rand;
T: 'static — это некий T, который можно хранить безопасно бесконечно долго, в том
числе вплоть до конца работы программы. T: 'static включает в себя все &'static T,
однако оно также включает в себя все владеющие типы, такие как String, Vec и т. д.
Владелец некоторых данных является гарантом того, что данные никогда не будут
инвалидированы, пока владелец их удерживает, следовательно, владелец может
безопасно хранить данные сколь угодно долго, в том числе вплоть до конца работы
программы. T: 'static должно читаться, как «T ограничен временем жизни 'static»,
а не «T имеет время жизни 'static». Вот программа для иллюстрации этих концепций:
Create PDF in your applications with the Pdfcrowd HTML to PDF API
use rand;
fn drop_static<T: 'static>(t: T) {
std::mem::drop(t);
}
fn main() {
let mut strings: Vec<String> = Vec::new();
for _ in 0..10 {
if rand::random() {
// все строки сгенерированны случайным образом
// и динамически созданы в куче во время исполнения
let string = rand::random::<u64>().to_string();
strings.push(string);
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API
Ключевые выводы
Create PDF in your applications with the Pdfcrowd HTML to PDF API
T: 'a включает в себя все &'a T, но обратное неверно.
fn main() {
let string = String::from("string");
t_bound(&string); // компилируется
t_bound(Ref(&string)); // компилируется
t_bound(&Ref(&string)); // компилируется
t_ref(&string); // компилируется
t_ref(Ref(&string)); // ошибка компиляции, ожидалась ссылка, обнаруже
на структура
t_ref(&Ref(&string)); // компилируется
Ключевые выводы
Create PDF in your applications with the Pdfcrowd HTML to PDF API
T: 'a является более общим и более гибким типом, чем &'a T
если T: 'static, то T: 'a, так как 'static >= 'a для всех 'a
Это удобное заблуждение сохраняется из-за правил вывода времён жизни (lifetime
elision), позволяющих опустить аннотации времён жизни в функциях, поскольку
компилятор Rust выведет их согласно следующим правилам:
если есть ровно одно входное время жизни, то оно приписывается всем
возвращаемым ссылкам (прим. переводчика: не только ссылкам, но и вообще всем
обобщённым типам, параметризованным временем жизни)
если есть несколько входных времён жизни, но одно из них — это время жизни
&self или &mut self, то время жизни self приписывается всем возвращаемым
Create PDF in your applications with the Pdfcrowd HTML to PDF API
ссылкам
// неявно
fn print(s: &str);
// выведено
fn print<'a>(s: &'a str);
// неявно
fn trim(s: &str) -> &str;
// выведено
fn trim<'a>(s: &'a str) -> &'a str;
Create PDF in your applications with the Pdfcrowd HTML to PDF API
// т. к. есть несколько входных времён жизни
fn overlap(s: &str, t: &str) -> &str;
// выведено
fn overlap<'a, 'b>(s: &'a str, t: &'b str) -> &'a str;
fn overlap<'a, 'b>(s: &'a str, t: &'b str) -> &'b str;
fn overlap<'a>(s: &'a str, t: &'a str) -> &'a str;
fn overlap<'a, 'b>(s: &'a str, t: &'b str) -> &'static str;
fn overlap<'a, 'b, 'c>(s: &'a str, t: &'b str) -> &'c str;
// неявно
fn compare(&self, s: &str) -> &str;
// выведено
fn compare<'a, 'b>(&'a self, &'b str) -> &'a str;
Create PDF in your applications with the Pdfcrowd HTML to PDF API
метод структуры
обобщённую функцию
Ключевые выводы
почти весь код Rust является обобщённым кодом, и везде есть выведенные
аннотации времён жизни
struct ByteIter<'a> {
remainder: &'a [u8]
}
impl<'a> ByteIter<'a> {
fn next(&mut self) -> Option<&u8> {
if self.remainder.is_empty() {
None
} else {
let byte = &self.remainder[0];
self.remainder = &self.remainder[1..];
Some(byte)
}
}
}
fn main() {
let mut bytes = ByteIter { remainder: b"1" };
assert_eq!(Some(&b'1'), bytes.next());
assert_eq!(None, bytes.next());
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API
ByteIter — это итератор, который перебирает срез байтов. Для краткости мы
пропустили реализацию трейта Iterator. Кажется, всё работает нормально, но что,
если мы хотим проверить пару байтов за раз?
fn main() {
let mut bytes = ByteIter { remainder: b"1123" };
let byte_1 = bytes.next();
let byte_2 = bytes.next();
if byte_1 == byte_2 {
// что-то делаем
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API
обобщённый итератор среза, который может перебирать любой &'a [T], то мы могли
бы захотеть использовать его в будущем с типами, которые дорого или вовсе
невозможно копировать/клонировать. Что ж, я думаю, мы ничего не можем с этим
поделать, ведь код компилируется, а значит, аннотации времён жизни должны быть
правильными, ведь так?
struct ByteIter<'a> {
remainder: &'a [u8]
}
impl<'a> ByteIter<'a> {
fn next<'b>(&'b mut self) -> Option<&'b u8> {
if self.remainder.is_empty() {
None
} else {
let byte = &self.remainder[0];
self.remainder = &self.remainder[1..];
Some(byte)
}
}
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API
Это совсем не помогло. Я всё ещё в замешательстве. Вот хорошая уловка, которую
знают только профессионалы Rust: давайте аннотациям времён жизни осмысленные
имена. Попробуем ещё раз:
struct ByteIter<'remainder> {
remainder: &'remainder [u8]
}
impl<'remainder> ByteIter<'remainder> {
fn next<'mut_self>(&'mut_self mut self) -> Option<&'mut_self u8> {
if self.remainder.is_empty() {
None
} else {
let byte = &self.remainder[0];
self.remainder = &self.remainder[1..];
Some(byte)
}
}
}
struct ByteIter<'remainder> {
remainder: &'remainder [u8]
}
impl<'remainder> ByteIter<'remainder> {
Create PDF in your applications with the Pdfcrowd HTML to PDF API
fn next(&mut self) -> Option<&'remainder u8> {
if self.remainder.is_empty() {
None
} else {
let byte = &self.remainder[0];
self.remainder = &self.remainder[1..];
Some(byte)
}
}
}
fn main() {
let mut bytes = ByteIter { remainder: b"1123" };
let byte_1 = bytes.next();
let byte_2 = bytes.next();
std::mem::drop(bytes); // теперь мы даже можем удалить итератор!
if byte_1 == byte_2 { // компилируется
// что-то делаем
}
}
Теперь, рассматривая предыдущую версию нашей программы, мы видим, что она явно
была ошибочной. Так почему же Rust скомпилировал ее? Ответ прост: это было
безопасным использованием памяти (memory safe).
Create PDF in your applications with the Pdfcrowd HTML to PDF API
программы, даже если аннотации времён жизни содержат семантические ошибки и из-
за этого программа становится излишне строгой.
#[derive(Debug)]
struct NumRef<'a>(&'a i32);
impl<'a> NumRef<'a> {
// моя структура параметризована 'a, так что мне нужно
// также аннотировать self 'a, верно? (ответ: нет, не верно)
fn some_method(&'a mut self) {}
}
fn main() {
let mut num_ref = NumRef(&5);
num_ref.some_method(); // изменяемо заимствует num_ref до конца её вр
емени жизни
num_ref.some_method(); // ошибка компиляции
println!("{:?}", num_ref); // также ошибка компиляции
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API
время жизни». На практике это означает, что анализатор заимствований Rust разрешит
не более одного вызова some_method, после которого структура станет перманентно
изменяемо заимствованной и, таким образом, непригодной к использование. Случаи, в
которых это требуется, крайне редки, но приведенный выше код новички очень легко
могут написать по ошибке, и он компилируется. Исправление ошибки состоит в том,
чтобы не добавлять ненужные явные аннотации времён жизни и положиться на
правила вывода времён жизни Rust:
#[derive(Debug)]
struct NumRef<'a>(&'a i32);
impl<'a> NumRef<'a> {
// на mut self больше нет 'a
fn some_method(&mut self) {}
fn main() {
let mut num_ref = NumRef(&5);
num_ref.some_method();
num_ref.some_method(); // компилируется
println!("{:?}", num_ref); // компилируется
}
Ключевые выводы
Create PDF in your applications with the Pdfcrowd HTML to PDF API
правила вывода времён жизни Rust не всегда подходят к конкретной ситуации
Create PDF in your applications with the Pdfcrowd HTML to PDF API
если трейт не имеет ограничений времён жизни, то в выражениях его время
жизни выводится, а вне выражений имеет ограничение 'static
Всё это звучит очень сложно, но это можно выразить коротко как «ограничения
времени жизни трейт-объекта выводятся из контекста». Изучив несколько
примеров, мы увидим, что выведенные времена жизни довольно интуитивны, поэтому
нам не нужно запоминать формальные правила:
use std::cell::Ref;
trait Trait {}
// неявно
type T1 = Box<dyn Trait>;
// выведено, Box<T> не налагает ограничений времён жизни на T,
// поэтому выводится 'static
type T2 = Box<dyn Trait + 'static>;
// неявно
impl dyn Trait {}
// выведено
impl dyn Trait + 'static {}
// неявно
type T3<'a> = &'a dyn Trait;
// выведено, &'a T требует T: 'a, поэтому выводится 'a
type T4<'a> = &'a (dyn Trait + 'a);
// неявно
Create PDF in your applications with the Pdfcrowd HTML to PDF API
type T5<'a> = Ref<'a, dyn Trait>;
// выведено, Ref<'a, T> требует T: 'a, поэтому выводится 'a
type T6<'a> = Ref<'a, dyn Trait + 'a>;
// неявно
type T7<'a> = Box<dyn GenericTrait<'a>>;
// выведено
type T8<'a> = Box<dyn GenericTrait<'a> + 'a>;
// неявно
impl<'a> dyn GenericTrait<'a> {}
// выведено
impl<'a> dyn GenericTrait<'a> + 'a {}
Конкретные типы, которые реализуют трейты, могут иметь ссылки, и, таким образом,
иметь ограничения времён жизни, поэтому созданные из них трейт-объекты имеют
соответствующие времена жизни. Также вы можете реализовать трейты
непосредственно для ссылок, которые, очевидно, имеют ограничения времён жизни:
trait Trait {}
struct Struct {}
struct Ref<'a, T>(&'a T);
Create PDF in your applications with the Pdfcrowd HTML to PDF API
impl<'a, T> Trait for Ref<'a, T> {} // реализация трейта для типа, содерж
ащего ссылку
В любом случае, это стоит повторить, поскольку это часто сбивает с толку новичков,
когда они преобразуют функцию из использующей трейт-объекты в обобщённую или
наоборот. Рассмотрим пример:
use std::fmt::Display;
error[E0310]: the parameter type `T` may not live long enough
--> src/lib.rs:10:5
|
9 | fn static_thread_print<T: Display + Send>(t: T) {
Create PDF in your applications with the Pdfcrowd HTML to PDF API
| -- help: consider adding an explicit lifetime
bound...: `T: 'static +`
10 | std::thread::spawn(move || {
| ^^^^^^^^^^^^^^^^^^
|
note: ...so that the type `[closure@src/lib.rs:10:24: 12:6 t:T]` will mee
t its required lifetime bounds
--> src/lib.rs:10:5
|
10 | std::thread::spawn(move || {
| ^^^^^^^^^^^^^^^^^^
Отлично, компилятор говорит нам, как решить проблему. Давайте исправим её.
use std::fmt::Display;
Create PDF in your applications with the Pdfcrowd HTML to PDF API
Теперь код компилируется, но эти две функции в сравнении выглядят несколько
странно. Почему вторая функция требует ограничение 'static на T, а первая функция
— нет? Это вопрос с подвохом. Используя правила вывода времён жизни, Rust
автоматически выводит ограничение 'static в первой функции, поэтому у них обеих
фактически есть ограничение 'static. Вот что видит компилятор Rust:
use std::fmt::Display;
Ключевые выводы
use std::fmt::Display;
error[E0310]: the parameter type `T` may not live long enough
--> src/lib.rs:4:5
|
3 | fn box_displayable<T: Display>(t: T) -> Box<dyn Display> {
| -- help: consider adding an explicit lifetime boun
d...: `T: 'static +`
4 | Box::new(t)
| ^^^^^^^^^^^
|
note: ...so that the type `T` will meet its required lifetime bounds
--> src/lib.rs:4:5
Create PDF in your applications with the Pdfcrowd HTML to PDF API
|
4 | Box::new(t)
| ^^^^^^^^^^^
Хорошо, давайте исправим проблему так, как говорит нам компилятор. Не будем
обращать внимание на тот факт, что он, не говоря нам, автоматически выводит для
нашего трейт-объекта ограничение времени жизни 'static и что его рекомендуемое
исправление основано на этом предположении:
use std::fmt::Display;
use std::fmt::Display;
Create PDF in your applications with the Pdfcrowd HTML to PDF API
Эта функция принимает все те же аргументы, что и в предыдущая версии, и даже сверх
этого! Делает ли это её лучше? Необязательно, это зависит от требований к нашей
программе. Данный пример несколько абстрактный, поэтому давайте рассмотрим
более простой и очевидный случай:
Выдаёт ошибку:
Create PDF in your applications with the Pdfcrowd HTML to PDF API
скомпилировалась, но эта функция чрезмерно ограничила бы тип возвращаемого
значения. На самом деле мы хотим этого:
Ключевые выводы
Create PDF in your applications with the Pdfcrowd HTML to PDF API
Этот код не компилируется:
struct Has<'lifetime> {
lifetime: &'lifetime str,
}
fn main() {
let long = String::from("long");
let mut has = Has { lifetime: &long };
assert_eq!(has.lifetime, "long");
{
let short = String::from("short");
// "переключиться" на более короткое время жизни
has.lifetime = &short;
assert_eq!(has.lifetime, "short");
Create PDF in your applications with the Pdfcrowd HTML to PDF API
Он выдаёт ошибку:
struct Has<'lifetime> {
lifetime: &'lifetime str,
}
fn main() {
let long = String::from("long");
let mut has = Has { lifetime: &long };
assert_eq!(has.lifetime, "long");
Create PDF in your applications with the Pdfcrowd HTML to PDF API
// "переключиться" на более короткое время жизни
has.lifetime = &short;
assert_eq!(has.lifetime, "short");
Ключевые выводы
Create PDF in your applications with the Pdfcrowd HTML to PDF API
времена жизни не могут расти, уменьшаться или как-то изменяться во время
исполнения
fn takes_shared_ref(n: &i32) {}
fn main() {
let mut a = 10;
takes_shared_ref(&mut a); // компилируется
takes_shared_ref(&*(&mut a)); // рассахаренная строка выше
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API
разделяемых действительно создает потенциальные проблемы безопасного доступа к
памяти:
use std::sync::Mutex;
struct Struct {
mutex: Mutex<String>
}
impl Struct {
// ослабляет мутабельную ссылку на self до разделяемой
fn get_string(&mut self) -> &str {
self.mutex.get_mut().unwrap()
}
fn mutate_string(&self) {
// если бы Rust разрешал ослабление мутабельных ссылок до разделя
емых,
// то следующая строка делала бы недействительной все
// разделяемые ссылки, возвращённые get_string
*self.mutex.lock().unwrap() = "surprise!".to_owned();
}
}
fn main() {
let mut s = Struct {
mutex: Mutex::new("string".to_owned())
};
let str_ref = s.get_string(); // мутабельная ссылка ослаблена до разд
еляемой
s.mutate_string(); // str_ref инвалидирована, теперь это висячий указ
атель
Create PDF in your applications with the Pdfcrowd HTML to PDF API
dbg!(str_ref); // ошибка компиляции, как и ожидалось
}
struct Struct;
impl Struct {
// ослабляет мутабельную ссылку на self до разделяемой
fn some_method(&mut self) -> &Self;
Create PDF in your applications with the Pdfcrowd HTML to PDF API
Даже если вы избегаете повторных заимствований в сигнатурах функций и методов,
Rust по-прежнему выполняет автоматические неявные повторные заимствования,
поэтому с этой проблемой легко можно столкнуться, не распознав её:
use std::collections::HashMap;
#[derive(Debug, Default)]
struct Player {
score: i32,
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API
use std::collections::HashMap;
#[derive(Debug, Default)]
struct Player {
score: i32,
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API
(прим. переводчика: на самом деле у этого неудобного поведения есть причина.
Допустим, что таблица server заполнена практически полностью: там есть
свободное место для одного Player, но нету для двух. В этом случае первый
экземпляр Player будет сохранён в имеющейся памяти, а второй спровоцирует
реаллокацию словаря. Если при этом увеличить количество выделенной памяти по
месту не получится, то будет выделен новый кусок памяти по новому адресу, в
который копируется содержимое старого словаря, при этом ссылки на старую
память становятся недействительными. Если бы Rust разрешал паттерн кода
выше, то в некоторых редких случаях у нас бы после вставки второго Player ссылка
на первый становилась бы висячей)
Ключевые выводы
Замыкания, несмотря на то, что они являются функциями, не следуют тем же правилам
вывода времён жизни, что и функции.
Create PDF in your applications with the Pdfcrowd HTML to PDF API
fn function(x: &i32) -> &i32 {
x
}
fn main() {
let closure = |x: &i32| x;
}
Выдаёт ошибку:
Create PDF in your applications with the Pdfcrowd HTML to PDF API
fn main() {
// аргумент и возвращаемое значение получают разные времена жизни
let closure = for<'a, 'b> |x: &'a i32| -> &'b i32 { x };
// внимание, строка выше не является корректным синтаксисом Rust, но
она требуется для пояснения
}
fn main() {
// кастуем к трейт-объекту, значение становится безразмерным, упс, ош
ибка компиляции
let identity: dyn Fn(&i32) -> &i32 = |x: &i32| x;
Create PDF in your applications with the Pdfcrowd HTML to PDF API
// в идеале нам хотелось бы написать так, но это некорректный синтакс
ис
let identity: impl Fn(&i32) -> &i32 = |x: &i32| x;
Уверен, вы уже заметили в приведенных выше примерах, что когда на типы замыканий
накладываются ограничения трейтов замыканий, они следуют обычным правилам
вывода времён жизни для функций.
Ключевые выводы
Create PDF in your applications with the Pdfcrowd HTML to PDF API
11) 'static-ссылки всегда можно привести к 'a-ссылкам
Ранее я показал следующий пример:
use rand;
Create PDF in your applications with the Pdfcrowd HTML to PDF API
fn static_str_fn() -> &'static str {
"str"
}
fn a_or_b<T>(a: T, b: T) -> T {
if rand::random() {
a
} else {
b
}
}
fn main() {
let some_string = "string".to_owned();
let some_str = &some_string[..];
let str_ref = a_or_b(some_str, generic_str_fn()); // компилируется
let str_ref = a_or_b(some_str, static_str_fn()); // компилируется
}
Однако это приведение не работает, когда ссылки являются частью сигнатуры функции,
поэтому этот код не компилируется:
use rand;
Create PDF in your applications with the Pdfcrowd HTML to PDF API
"str"
}
fn main() {
let some_string = "string".to_owned();
let some_str = &some_string[..];
let str_ref = a_or_b_fn(some_str, generic_str_fn); // компилируется
let str_ref = a_or_b_fn(some_str, static_str_fn); // ошибка компиляци
и
}
Create PDF in your applications with the Pdfcrowd HTML to PDF API
25 | let str_ref = a_or_b_fn(some_str, static_str_fn);
| ---------------------------------- argument requir
es that `some_string` is borrowed for `'static`
26 | }
| - `some_string` dropped here while still borrowed
Является ли это подвохом или нет — вопрос спорный, так как это не простой
прямолинейный случай приведения &'static str к &'a str, а приведение for<T> Fn()
-> &'static T к for<'a, T> Fn() -> &'a T. Первое — это приведение между
значениями, а второе — это приведение между типами.
Ключевые выводы
функции с сигнатурами вида for<'a, T> fn() -> &'a T являются более гибкими и
работают в большем числе случаев, чем функции с сигнатурами вида for<T> fn()
-> &'static T
Заключение
T является надмножеством как &T, так и &mut T
Create PDF in your applications with the Pdfcrowd HTML to PDF API
поскольку T: 'static включает в себя владеющие типы, это означает, что T
если T: 'static, то T: 'a, так как 'static >= 'a для всех 'a
почти весь код на Rust является обобщённым кодом, и повсюду есть выведенные
аннотации времён жизни
Create PDF in your applications with the Pdfcrowd HTML to PDF API
все трейт-объекты имеют определенные выведенные ограничения времён жизни
по умолчанию
функции с сигнатурами вида for<'a, T> fn() -> &'a T являются более гибкими и
работают в большем числе случаев, чем функции с сигнатурами вида for<T> fn()
-> &'static T
Create PDF in your applications with the Pdfcrowd HTML to PDF API
Обсуждение
Обсудите эту статью на
субреддите learnrust
субреддите Rust
Hackernews
Контакты
Подписывайтесь на pretzelhammer в Twitter, чтобы получать уведомления о будущих
публикациях в блоге!
Дальнейшее чтение
Изучение Rust в 2020 году
Теги: rust, lifetime, lifetimes, ownership, ownership & borrowing, trait object
Create PDF in your applications with the Pdfcrowd HTML to PDF API
Хабы: Программирование, Системное программирование, Rust
10,0 18,1
Карма Рейтинг
@AnthonyMikh
Типострадалец
ПОХОЖИЕ ПУБЛИКАЦИИ
Create PDF in your applications with the Pdfcrowd HTML to PDF API
ВАКАНСИИ
Программист BIOS
от 150 000 ₽ • Aquarius • Москва
Программист C# (Senior)
от 160 000 ₽ • ГК «Системные Технологии» • Калининград • Можно удаленно
Scala разработчик
от 180 000 до 300 000 ₽ • Эвотор • Москва • Можно удаленно
Реклама
Комментарии 4
Create PDF in your applications with the Pdfcrowd HTML to PDF API
envy12 вчера в 20:40 0
Отличный разбор, только разницы между &’static и: ‘static не понял. Первое является
подмножеством второго, правильно? То-есть частный случай, когда который работает
только для случаев аля &’static str?
Именно. String вполне себе 'static и при этом не содержит времён жизни.
То есть для случая Ref<'static, T>, эта структура не будет содержать нестатических
ссылок, а значит она будет удовлетворять ограничению Ref<'static, T>: 'static.
Хорошая статья. Жаль только тема ограничений лайфтаймов не раскрыта: "'a: 'b",
"for<'a>" и т.п.
Create PDF in your applications with the Pdfcrowd HTML to PDF API
Только полноправные пользователи могут оставлять комментарии. Войдите,
пожалуйста.
САМОЕ ЧИТАЕМОЕ
Create PDF in your applications with the Pdfcrowd HTML to PDF API
+106 39,7k 46 151
© 2006 – 2020 «Habr» Настройка языка О сайте Служба поддержки Мобильная верс
Create PDF in your applications with the Pdfcrowd HTML to PDF API