我有一个特征,我想提供一种方法。该方法将根据一些帮助者来实现,这些帮助者没有业务在特征内,并且非常简单,动态多态性比使它们通用更有意义。所以我的代码是
fn use_trait(x: &Trait) {
println!("object says {}", x.needed());
}
trait Trait {
fn needed(&self) -> &str;
fn provided(&self) {
use_trait(self);
}
}
struct Struct();
impl Trait for Struct {
fn needed(&self) -> &str {
"Hello, world!"
}
}
fn main() {
Struct().provided();
}
然而,does not compile,错误:
error[E0277]: the trait bound `Self: std::marker::Sized` is not satisfied
--> <anon>:9:19
|
9 | use_trait(self);
| ^^^^ the trait `std::marker::Sized` is not implemented for `Self`
|
= help: consider adding a `where Self: std::marker::Sized` bound
= note: required for the cast to the object type `Trait`
我理解为什么 - 不能保证有人不会为未归类的类型实现特征(从&T where T: Trait
转换为&Trait
需要T: Sized
,但声明并不要求)。
然而,建议不会做我需要的。我可以添加
fn needed(&self) -> &str where Self: Sized
但是needed()
方法将无法在&Trait
(因为Trait : ?Sized
)上访问,这会使事情变得无用,因为类型(实际的一个有用的东西)总是处理为Arc<Trait>
。并添加
trait Trait: Sized
更糟糕,因为它根本不允许&Trait
(Trait
因为类型未归类,所以Trait
类型不实现特征{ {1}})。
当然我可以简单地制作
Trait
但实际代码背后有很多,所以我不想在那里进行单态化,特别是因为特性总是作为特征对象处理。
有没有办法告诉Rust所有fn use_trait<T: Trait>(x: &T)
必须调整大小的类型,这里是一个应该适用于所有类型的方法的定义?
答案 0 :(得分:2)
as_trait
及其实施需要额外的Trait
功能:
trait Trait {
fn needed(&self) -> &str;
fn provided(&self) {
use_trait(self.as_trait());
}
fn as_trait(&self) -> &Trait;
}
struct Struct();
impl Trait for Struct {
fn needed(&self) -> &str {
"Hello, world!"
}
fn as_trait(&self) -> &Trait {
self as &Trait
}
}
你可以在操场上试试。 (trait objects)
答案 1 :(得分:0)
帮助程序as_trait
函数可以放在一个辅助特征中,可以为试图实现Sized
的所有Trait
类型实现全面实现。然后Trait
的实施者不需要做任何特殊的事情,转换就可以了。
fn use_trait(x: &Trait) {
println!("object says {}", x.needed());
}
trait Trait : AsTrait {
fn needed(&self) -> &str;
fn provided(&self) where Self : AsTrait {
use_trait(self.as_trait());
}
}
trait AsTrait {
fn as_trait(&self) -> &Trait;
}
impl<T : Trait + Sized> AsTrait for T {
fn as_trait(&self) -> &Trait { self }
}
struct Struct();
impl Trait for Struct {
fn needed(&self) -> &str {
"Hello, world!"
}
}
fn main() {
Struct().provided();
}
(在play上)。
也可以简单地将 provided
放在辅助特征中,但是它必须动态地调度到Self
的其他方法。
更新:实际上,重点是仍然可以覆盖provided
。
现在可以通过使其成为通用来进一步改进上述内容。有std::makrer::Unsize,在撰写本文时不稳定。我们无法做到
trait Trait : Unsize<Trait>
因为Rust不允许CRTP,但幸运的是,将约束放在方法上就足够了。所以
fn use_trait(x: &Trait) {
println!("object says {}", x.needed());
}
trait Trait {
fn needed(&self) -> &str;
fn provided(&self) where Self: AsObj<Trait> {
use_trait(self.as_obj());
}
}
trait AsObj<Tr: ?Sized> {
fn as_obj(&self) -> &Trait;
}
// For &'a Type for Sized Type
impl<Type: Trait> AsObj<Trait> for Type {
fn as_obj(&self) -> &Trait { self }
}
// For trait objects
impl AsObj<Trait> for Trait {
fn as_obj(&self) -> &Trait { self }
}
struct Struct();
impl Trait for Struct {
fn needed(&self) -> &str {
"Hello, world!"
}
fn provided(&self) {
println!("Aber dieses Objekt sagt Grüß Gott, Welt!"); // pardon my German, it is rusty.
}
}
fn main() {
let s: &Trait = &Struct();
s.provided();
}
(在play上)
这最终使其对其他版本的实现者透明。