Типы объектов-черт имеют связанный срок жизни, но его можно опустить.Тип объекта с полным признаком пишется dyn Trait + 'a
(когда за ссылкой необходимо добавить круглые скобки: &(dyn Trait + 'a)
).
Хитрость заключается в том, что когда граница времени жизни опущена, правила немного сложны .
Во-первых, мы имеем:
impl PartialOrd for dyn SimpleOrder {
Здесь компилятор выводит + 'static
.Параметры времени жизни никогда не вводятся в impl
блоках (по состоянию на Rust 1.32.0).
Далее мы имеем:
fn partial_cmp(&self, other: &dyn SimpleOrder) -> Option<Ordering> {
Тип other
выводится как &'b (dyn SimpleOrder + 'b)
, где 'b
- это неявный параметр времени жизни, введенный в partial_cmp
.
fn partial_cmp<'a, 'b>(&'a self, other: &'b (dyn SimpleOrder + 'b)) -> Option<Ordering> {
Итак, теперь у нас есть self
с типом &'a (dyn SimpleOrder + 'static)
, в то время как other
имеет тип &'b (dyn SimpleOrder + 'b)
.В чем проблема?
Действительно, cmp
не дает никакой ошибки, потому что его реализация не требует, чтобы время жизни двух объектов признаков было одинаковым.Почему partial_cmp
заботится, хотя?
Потому что partial_cmp
звонит Ord::cmp
.При проверке типа вызова метода черты компилятор проверяет сигнатуру черты.Давайте рассмотрим эту подпись:
pub trait Ord: Eq + PartialOrd<Self> {
fn cmp(&self, other: &Self) -> Ordering;
Эта черта требует, чтобы other
был типа Self
.Это означает, что когда partial_cmp
вызывает cmp
, он пытается передать &'b (dyn SimpleOrder + 'b)
параметру, который ожидает &'b (dyn SimpleOrder + 'static)
, потому что Self
равен dyn SimpleOrder + 'static
.Это преобразование недопустимо ('b
не может быть преобразовано в 'static
), поэтому компилятор выдает ошибку.
Итак, почему допустимо установить тип other
в &'b (dyn SimpleOrder + 'b)
при реализации Ord
?Поскольку &'b (dyn SimpleOrder + 'b)
является супертипом из &'b (dyn SimpleOrder + 'static)
, а Rust позволяет заменять тип параметра одним из его супертипов при реализации метода черты (он делает метод строго более общим, даже если он явно немного использовался при проверке типов).
Чтобы сделать вашу реализацию как можно более общей, вы должны ввести параметр времени жизни в impl
s:
use std::cmp::Ordering;
pub trait SimpleOrder {
fn key(&self) -> u32;
}
impl<'a> PartialOrd for dyn SimpleOrder + 'a {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<'a> Ord for dyn SimpleOrder + 'a {
fn cmp(&self, other: &Self) -> Ordering {
self.key().cmp(&other.key())
}
}
impl<'a> PartialEq for dyn SimpleOrder + 'a {
fn eq(&self, other: &Self) -> bool {
self.key() == other.key()
}
}
impl<'a> Eq for dyn SimpleOrder + 'a {}