I have some traits that are expected to be dynamically polymorphic and used as objects. I want to provide methods for them. Something like
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();
}
Except that
use_trait(self);
does not compile, because it turns out that Trait is not guaranteed to be object-safe here. And fixing it by requiring Sized
won't work, because the trait is to be used as &Trait
most of the time and requiring Trait : Sized
or needed() where Self : Sized
would both break that.
So I asked over on SO some time ago and managed to buidl this solution:
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();
}
Now this works, but it is Ugly™. Worse, I could not get this to work:
fn use_trait(x: &Trait) {
println!("object says {}", x.needed());
}
trait Trait : AsObj<Trait> {
fn needed(&self) -> &str;
fn provided(&self) where Self : AsObj<Trait> {
use_trait(self.as_obj());
}
}
trait AsObj<T> {
fn as_obj(&self) -> &T;
}
impl<Trait, Type : Trait + Sized> AsObj for Type {
fn as_obj(&self) -> &Trait { self }
}
struct Struct();
impl Trait for Struct {
fn needed(&self) -> &str {
"Hello, world!"
}
}
fn main() {
Struct().provided();
}
because the compiler didn't like the declaration
trait Trait : AsObj<Trait>
In C++, this is common idiom. In fact, it is so common that it derives it's name, curiously recurring template pattern, from the fact it is common rather than from what it does.
Anyway, my questions are:
- Is there a simpler way to solve this?
- And if there is not, should there be?
Without ability to specify that it shall be possible to cast &Self
to &Trait
, using dynamic polymorphism for something more complicated gets rather unwieldy.