junglie85
junglie85

Reputation: 1463

Rust trait object with generic type parameter

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

Answers (1)

isaactfa
isaactfa

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

Related Questions