Reputation: 1463
I have a trait Transformable
which looks something like this:
pub trait Transformable {
fn position(&self) -> Vec2f;
fn set_position<V>(&mut self, position: V) where V: Into<Vec2f>;
}
And I have a SceneNode
trait which I need to be a trait object, which looks something like this (although the details here are not really relevant):
pub trait SceneNode {
fn update(&mut self, dt: Duration);
I'd like to enforce that SceneNode
trait objects also implement the Transformable
trait. However, the generic V
in Transformable
means that SceneNode
cannot be a trait object.
I like having V: Into<Vec2f>
as a generic type parameter on Transformable
because it makes for a more convenient API instead of having to explicitly create new Vec2f
's all the time, e.g.:
impl From<[f32; 2]> for Vec2f { ... }
transformable.set_position([10.0, 10.0]); // Yes please.
transformable.set_position([10.0, 10.0].into()); // No thanks.
transformable.set_position(Vec2f::new(10.0, 10.0)); // No thanks.
Is there a way for SceneNode
to implement Transformable
that allows me to keep this type parameter or in which Transformable
can be redesigned such that I can still have the current usage?
Upvotes: 3
Views: 136
Reputation: 6651
I'm afraid there's very little wiggle room here. If you need the trait to be object safe, i.e. be able to be turned into a trait object, none of its methods can have generic type parameters. Period. The only thing I can think of is splitting the generic set_position
into its own trait with a blanket impl:
pub trait Transformable {
fn position(&self) -> Vec2f;
// no longer generic + a different name for trait method resolution
fn set_position_raw(&mut self, position: Vec2f);
}
// put the generic version into a new trait...
pub trait SetPosition {
fn set_position<V>(&mut self, position: V) where V: Into<Vec2f>;
}
// ...with a blanket impl for `Transformable` types
impl<T: ?Sized> SetPosition for T where T: Transformable {
fn set_position<V>(&mut self, position: V) where V: Into<Vec2f> {
self.set_position_raw(position.into());
}
}
Now you can write this:
let mut t = SomeTransformableType { ... };
t.set_position([1.0, 1.0]);
let t_dyn: &mut dyn Transformable = &mut t;
t.set_position([-1.0, -1.0]);
Upvotes: 3