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>
(orArc
)- 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.