Ответ 1
Когда вы определяете черту, Rust также определяет тип объекта-объекта с тем же именем. Объекты Trait имеют параметр lifetime, который обозначает кратчайший из параметров жизненного цикла. Чтобы указать это время жизни, вы пишете тип как Foo + 'a
.
Когда вы пишете impl Foo {
, вы не указываете этот параметр lifetime, и по умолчанию он равен 'static
. Это примечание компилятора в инструкции y.foo_in_impl();
намекает на следующее:
Примечание: ссылка должна быть действительной для статического времени жизни...
Все, что нам нужно сделать, чтобы сделать это более разрешительным, - написать общий impl
в течение любого времени жизни:
impl<'a> Foo + 'a {
fn foo_in_impl(&self) { println!("in impl") }
}
Теперь обратите внимание, что аргумент self
на foo_in_impl
является заимствованным указателем, который имеет собственный параметр времени жизни. Тип self
в его полной форме выглядит как &'b (Foo + 'a)
(круглые скобки требуются из-за приоритета оператора). A Box<u8>
владеет свойством u8
– он ничего не заимствует –, поэтому вы можете создать &(Foo + 'static)
из него. С другой стороны, &42u8
создает &'b (Foo + 'a)
, где 'a
не 'static
, потому что 42u8
помещается в скрытую переменную в стеке, а объект-объект заимствует эту переменную. (Однако это не имеет смысла, u8
ничего не берет, поэтому его реализация Foo
всегда должна быть совместима с Foo + 'static
... тот факт, что 42u8
заимствован из стека, должен влиять 'b
, а не 'a
.)
Еще одна вещь, которую следует отметить, заключается в том, что методы-признаки являются полиморфными, даже если они имеют реализацию по умолчанию, и они не переопределены, а встроенные методы для объектов-признаков мономорфны (есть только одна функция, независимо от того, что стоит за признаком), Например:
use std::any::TypeId;
trait Foo {
fn foo_in_trait(&self)
where
Self: 'static,
{
println!("{:?}", TypeId::of::<Self>());
}
}
impl Foo {
fn foo_in_impl(&self) {
println!("{:?}", TypeId::of::<Self>());
}
}
impl Foo for u8 {}
impl Foo for u16 {}
fn main() {
let x = Box::new(42u8) as Box<Foo>;
x.foo_in_trait();
x.foo_in_impl();
let x = Box::new(42u16) as Box<Foo>;
x.foo_in_trait();
x.foo_in_impl();
}
Пример вывода:
TypeId { t: 10115067289853930363 }
TypeId { t: 1357119791063736673 }
TypeId { t: 14525050876321463235 }
TypeId { t: 1357119791063736673 }
В методе признаков мы получаем идентификатор типа базового типа (здесь u8
или u16
), поэтому мы можем заключить, что тип &self
будет варьироваться от одного исполнителя к другому (он '&u8
для u8
исполнитель и &u16
для u16
реализатор – не объект-объект). Однако в встроенном методе мы получаем идентификатор типа Foo
, поэтому мы можем заключить, что тип &self
всегда &Foo
(объект-объект).