How to convert a slice of trait objects into a vector of trait objects?

I have a function that accepts a vector of trait objects as an argument. But I have a slice of trait objects that I want to pass to this function.

I am getting following error(ToNumber is name of my trait) when I try to use to_vec() to make a vector of this slice. How should I resolve this?

the trait bound dyn ToNumber: Clone is not satisfied
required because of the requirements on the impl of Clone for Box<dyn ToNumber>

You cannot really clone trait objects. There’s the dyn_clone crate that could help with this. Depending on your concrete code / problem, there may also be other, easier solutions; feel free to provide some more details about the traits and functions involved.

The first question is why you are doing that. Usually you should accept general iterators or maybe just slices instead of fully-fledged collection types. If you make the function accept a slice instead, this problem will go away.

Anyway, to expand on @steffahn's answer, trait objects aren't cloneable because cloning requires the concrete type of the value being cloned to be known. A trait object only contains enough information to be able to stand in for an object that implements that specific trait, by storing a pointer to the opaque data and another to a vtable of method pointers to concrete methods, which in turn know how to handle the opaque data (since they were created from the specific underlying type at compile time).

A trait object is thus incapable of being cloned; that would require restoring the static type information, performing the clone, then downgrading it to a trait object again. The first of these steps is not really feasible or practical.

However, if your function is generic enough to at least accept a vector of any type that implements ToNumber, you could create a vector of references instead of a vector of boxed trait objects:

let vec_of_refs = slice_of_boxes
    .iter()
    .map(|x| &**x)
    .collect::<Vec<&dyn ToNumber>>();

and then pass this vector to the function in question.

An example:

use dyn_clone::{clone_box, DynClone};

// add DynClone as supertrait
trait ToNumber: DynClone {
    // methods here…
}

fn needs_vec(_arg: Vec<Box<dyn ToNumber>>) {
    // implementation…
}

fn other_function(arg: &[Box<dyn ToNumber>]) {
    // still can’t use `to_vec` directly
    /*
    needs_vec(arg.to_vec());
    */

    // but this works
    needs_vec(arg.iter().map(|x| clone_box(&**x)).collect());

    // alternatively, you could add a `Clone` implementation for `Box<dyn ToNumber>`
    // let’s do that for another trait ToNumber2 (see below), then we can do
    let arg2: &[Box<dyn ToNumber2>] = &[];
    let _vec: Vec<Box<dyn ToNumber2>> = arg2.to_vec();
    // so if we did this for `ToNumber`, then
    // `needs_vec(arg.to_vec());`
    // would work, too.
}

trait ToNumber2: DynClone {
    // methods here…
}
// the `+ '_` makes the impl a bit more general, otherwise we’d just
// cover Box<dyn ToNumber2 + 'static>, which is the desugared version of
// Box<dyn ToNumber2>
impl Clone for Box<dyn ToNumber2 + '_> {
    fn clone(&self) -> Self {
        clone_box(&**self)
    }
}

(in the playground)

2 Likes

This only works assuming the presence of some implementation impl<T: ?Sized> ToNumber for &T where T: ToNumber, otherwise Vec<&dyn ToNumber> isn’t “a vector of a type that implements ToNumber”.

1 Like

I would try to refactor my code to use slices(or see if vector of references to trait objects would work for me) as @H2CO3 suggested, or fallback to dyn_clone crate as @steffahn suggested.

Here's my ToNumber trait:

trait ToNumber {
    fn get_number(&self, symbols: &HashMap<String, Symbol>) -> Result<i32, CustomError>;
    fn serialize(&self) -> Result<Value, CustomError>;
}

impl ToNumber for i32 {
    fn get_number(&self, _symbols: &HashMap<String, Symbol>) -> Result<i32, CustomError> {
        Ok(*self)
    }

    fn serialize(&self) -> Result<Value, CustomError> {
        Ok(json!(self))
    }
}

impl ToNumber for BigDecimal {
    fn get_number(&self, _symbols: &HashMap<String, Symbol>) -> Result<i32, CustomError> {
        match self.to_i32() {
            Some(v) => Ok(v),
            None => Err(CustomError::Message(Message::ErrUnexpected)),
        }
    }

    fn serialize(&self) -> Result<Value, CustomError> {
        match self.to_i32() {
            Some(v) => Ok(json!(v)),
            None => Err(CustomError::Message(Message::ErrUnexpected)),
        }
    }
}

I see… I was referring to those

that have the slice of trait objects (the caller) / need the vec of trait objects (the callee).

In any case, your trait – only containing &self methods – does support an implementation like

impl<T: ?Sized> ToNumber for &T
where
    T: ToNumber
{ … }

(and e.g. also for Box<T> / &mut T, I guess) so the option of generically taking a Vec<impl ToNumber> instead of Vec<Box<dyn ToNumber>> could indeed work. If it turns out that your method doesn’t need a Vec at all but can be implemented with slices, that’s even better, of course.

This also seem to have a ripple effect on all my enums which makes me introduce lifetimes everywhere. Since this seems like a lot of work, I will fallback to use dyn_clone crate. @steffahn

I have enums like:

enum ArithmeticExpression {
    Add((Box<dyn ToNumber>, Vec<Box<dyn ToNumber>>)),
}
1 Like

This was really a lifesaver. I don't know how much pain I would have gone through otherwise. Thanks a lot.

Just noticed, there’s a macro for this in dyn_clone as-well: dyn_clone::clone_trait_object - Rust

clone_trait_object!(ToNumber2);
1 Like

You could also consider using Rc<dyn ToNumber> instead of Box<dyn ToNumber>. Those can always be cloned, even without using the DynClone supertrait, and it’s cheaper if the underlying type implementing ToNumber has a nontrivial cost in order to be cloned.

OTOH, Rc don’t allow mutable access to the underlying value; but AFAICT, your ToNumber trait doesn't offer any way to mutate self anyway.

But if you do ever need mutable access, you could use Rc in a clone-on-write fashion, only cloning values if necessary, still saving cloning overhead in cases where you didn't need mutation

impl dyn ToNumber + '_ {
    /// Give mutable reference, clone if necessary
    fn make_mut<'a>(self: &'a mut Rc<Self>) -> &'a mut Self {
        if Rc::get_mut(self).is_some() {
            Rc::get_mut(self).unwrap()
        } else {
            *self = Rc::from(clone_box(&**self));
            Rc::get_mut(self).unwrap()
        }
    }
}
2 Likes

I would do that since it makes things simpler

Since it's your own trait, you could also just add a required method:

fn dyn_clone(&self) -> Box<dyn ToNumber>;

Edit: Or even if it's not, you can wrap the trait and blanket implement.

trait DynClonableToNumber: ToNumber {
    fn dyn_clone<'a>(&self) -> Box<dyn DynClonableToNumber + 'a> where Self: 'a;
}

impl<T: ToNumber + Clone> DynClonableToNumber for T {
    fn dyn_clone<'a>(&self) -> Box<dyn DynClonableToNumber + 'a> where Self: 'a {
        Box::new(self.clone())
    }
}
2 Likes

something I had eventually realized as well:

1 Like

I ended up replacing Box with the Rc in my enums approach since it seems suffice for now.

enum MatchExpression {
    Variant(
        (
            Rc<dyn ToNumber>,
            Vec<(Rc<dyn ToNumber>, Rc<dyn ToNumber>)>,
            Rc<dyn ToNumber>,
        ),
    ),
}

I also realised a hack that would have worked for me. I was serializing (Box, Vec<Box>) , I combined both of them into a collection and serialized to Json Array. The problem arose when I had to deserialize this, I could have tried to split the JsonArray at index 1, and then getting a Box and Vec<Box> separately from it. Or I could have change the serialization and deserialization format which didn't make me split anything. But the solutions suggested are better to move forward with.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.