In Rust, is it possible to define an object safe trait that requires a constructor function of the implementing type?

I’m writing a sorting visualisation program and have different structs corresponding to different sorting algorithms. They all implement the Algorithm trait (a subtrait of Ord and From<usize>), which requires an implementation of fn new() -> Self and of fn tick(&mut self, l: &mut List). Since the constructor returns a Self this trait is not object safe. However, I would like the user to be able to pass in the algorithm they want to see at runtime. I think this should be possible in theory, but I’m struggling to come up with how. I’m not particularly looking forward to constructing vtables manually, but surely this is either a solved problem / definitive "nope, not possible".

I realise that by defifnition, requiring Self: Sized opts out of object safety; however I don’t see a way around needing to assign Self::new() to *self in the reset() function. Any input would be greatly appreciated.

The full trait definitions looks like the following:

pub trait ListItem
where
    Self: Ord + Clone + From<usize>,
{
    fn select(&mut self);
    fn deselect(&mut self);
}

pub trait Algorithm
where
    Self::Item: ListItem,
    Self: Sized,
{
    type Item;
    fn new() -> Self;
    fn tick(&mut self, l: &mut List<Self::Item>) -> AlgorithmState; // just an enum
    fn reset(&mut self) {
        *self = Self::new();
    }
    fn name(&self) -> &'static str;
}

>Solution :

No it’s not possible since the docs on object safety say all methods must be dispatchable or explicitly non-dispatchable and that includes it must

  • Be a method that does not use Self except in the type of the receiver.

  • Have a receiver with one of the following types:

    &Self (i.e. &self)
    &mut Self (i.e &mut self)
    Box<Self>
    Rc<Self>
    Arc<Self>
    Pin<P> where P is one of the types above
    

and the return type is not the receiver nor do you even have a receiver.

And those rules aren’t arbitrary either, you can’t return a plain Self when you don’t know it’s size, so new can’t be dynamically dispatched in that case.

Things that don’t stop your trait from being object safe that you could return if your trait was object safe to begin with:

  • Box<dyn Algorithm>
  • Rc<dyn Algorithm> (or Arc)
  • any other Algorithm trait object

Of course none of these variants allow you to provide a default impl for reset and I don’t see an object safe way to provide one.

And by the way Self: Sized on the trait disallows trait objects on it’s own since a dyn Trait does never implement Sized so it can never implement such a trait either.

Leave a Reply