Ответ 1
Как эта проблема возникает в признаке, но не в ее реализации для MyBTree?
Эти сигнатуры методов становятся более нюансированными, если вы рассматриваете реализацию BTree<T>
для типа, имеющего время жизни. Мой общий совет для всех ошибок времени жизни, связанных с типичным параметром типа или типом Self
, является: сосредоточиться на случае, когда тип является заимствованным типом.
Проблема с заимствованными типами заключается в том, что у вас никогда не будет ссылки с более длительным сроком службы, чем данные, на которые она ссылается. Самый простой пример этого принципа:
fn f<'a, 'b>() {
// error[E0491]: in type `&'a &'b ()`, reference has a longer
// lifetime than the data it references
let _: &'a &'b ();
}
Ржавчина заставляет нас гарантировать, что данные, на которые ссылается ссылка, вызывают ссылку, в этом случае 'b
переживает 'a
.
fn f<'a, 'b: 'a>() {
let _: &'a &'b ();
}
Теперь примените это к вашей ситуации BTree
, если подумать, что не так, если T
является заимствованным типом типа &()
. Сначала рассмотрим следующие два метода, которые вы разместили в impl<T> BTree<T> for MyBTree<T>
. Я написал конкретные сроки жизни, чтобы уточнить обсуждение.
impl<T> BTree<T> for MyBTree<T> {
fn left<'a>(&'a self) -> Option<&'a Self> { /* ... */ }
fn value<'a>(&'a self) -> Option<&'a T> { /* ... */ }
}
Чтобы вызывающий вызывал left
, они должны знать, что Self
переживает время жизни 'a
. И чтобы вызывающий вызывал value
, они должны знать, что Self
переживает время жизни 'a
и T
переживает время жизни 'a
(для того, чтобы &'a T
будет значимым типом, как мы видели выше). Контроллер заимствования не позволит им вызывать эти методы, если эти требования не выполняются, и поэтому реализация может предполагать выполнение этих требований.
Кроме того, средство проверки заемщика может видеть, что , если Self
переживает 'a
, тогда также T
переживает 'a
, потому что MyBTree<T>
содержит значение тип T
.
Вот почему не было проблем с реализацией left
и value
внутри impl<T> BTree<T> for MyBTree<T>
. Соединитель и структура MyBTree<T>
вместе гарантируют, что все будет работать до тех пор, пока нам нужно.
Теперь, когда мы использовали эти методы в определении определения BTree<T>
.
trait BTree<T> {
fn left<'a>(&'a self) -> Option<&'a Self> { /* ... */ }
fn value<'a>(&'a self) -> Option<&'a T> { /* ... */ }
}
Дела идут неправильно, потому что если вызывающий вызывает left
, они должны знать, что Self
переживает 'a
, но они не гарантировали, что T
переживания 'a
. Например, они могут иметь T=&'b ()
для некоторого полностью несвязанного более короткого времени жизни 'b
. Как мы видели выше, это сделало бы &'a T
равным &'a &'b ()
, который не был бы юридическим типом.
Причина, по которой Rust довольна value
, определяемой в этом признаке, заключается в том, что вызывающий абонент гарантирует, что Self
и T
переживают время жизни 'a
. Причина, по которой Rust недовольна тем, что left
, определенный в этом признаке, заключается в том, что вызывающий абонент гарантирует, что только Self
переживает 'a
, а не T
переживает 'a
, который предполагает реализация.
И почему компилятор жалуется на время жизни T в методах, которые игнорируют значение T - пока он работает с методом
value
, который упоминает T в его возвращаемом типе?
Ну, ошибка заключается не в возвращаемом значении, а в вызове all()
. Посмотрите внимательно.
error[E0311]: the parameter type `T` may not live long enough
--> src/lib.rs:6:24
|
6 | match self.all() {
| ^^^
Для вызова all()
вызывающий абонент отвечает за доказательство того, что типы ввода и вывода являются допустимыми. Но в случае, если T
что-то вроде &'b ()
, это может быть неверно. all()
вернет &'a &'b ()
, поэтому средство проверки заемных средств предотвратит вызов.
Мы можем исправить это, сделав явные гарантии, которые предполагает наша реализация, в этом случае T
переживает 'a
.
trait BTree<T> {
fn left<'a>(&'a self) -> Option<&'a Self>
where
T: 'a,
{
/* ... */
}
}