2

I want to have something as follows:

pub trait Domainable: Eq + Hash {}
impl<T: Eq + Hash> Domainable for T {}

pub struct Variable<T: Domainable> {
    domain: HashSet<T>,
}

pub struct Assignment<'a, T: Domainable> {
    variable: &'a Variable<T>,
    assignment: T,
}

pub struct State<'a, T: Domainable> {
    assignments: Vec<&'a Assignment<'a, dyn Domainable>>,
}

In effect, I want to have a vector of assignments of possibly different domainable types within the state. For example:

let var1 = Variable::new(HashSet::from([1, 2, 3, 4]));
let var2 = Variable::new(HashSet::from([true, false]));

let assignment1 = Assignment::new(&var1, 2);
let assignment2 = Assignment::new(&var2, false);

let state = State::new(vec![assignment1, assignment2]);

However, it keeps giving errors for dyn Domainable:

error[E0038]: the trait `Domainable` cannot be made into an object
   --> src/main.rs:17:41
    |
17  |     assignments: Vec<&'a Assignment<'a, dyn Domainable>>,
    |                                         ^^^^^^^^^^^^^^ `Domainable` cannot be made into an object
    |
note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
    |
   ::: src/main.rs:4:11
    |
4   | pub trait Domainable: Eq + Hash {}
    |           ---------- this trait cannot be made into an object...

Playground

I have read other posts where they have suggested to use &dyn ThingTrait similarly for the trait objects, so I am not sure what's wrong with my code.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Mike
  • 31
  • 1
  • 5
  • Does this answer your question? [How can I create hashable trait objects / trait objects with generic method parameters?](https://stackoverflow.com/questions/49711479/how-can-i-create-hashable-trait-objects-trait-objects-with-generic-method-para) – John Kugelman Jun 10 '22 at 20:13
  • Also: [How to test for equality between trait objects?](https://stackoverflow.com/questions/25339603/how-to-test-for-equality-between-trait-objects) – John Kugelman Jun 10 '22 at 20:15
  • @JohnKugelman Thank you for linking those posts. However, I think my question is more about how (or whether it is even possible) to store these assignments as list/hashset/etc. rather than testing/creating objects. – Mike Jun 10 '22 at 20:22
  • 2
    I think it's mad about the generic. In the [object safety](https://doc.rust-lang.org/reference/items/traits.html#object-safety) section of the rust reference, it has examples of traits that cannot be turned into trait objects. – Jeremy Meadows Jun 10 '22 at 20:40
  • 1
    @Mike You can't do this because `Hash` isn't object-safe -- it has a method that takes a generic type. You can't dynamically dispatch on generic methods because the vtable would have to be infinitely large to accommodate all of the possible types `H` could be. (This is, for example, why you can't have a template virtual member function in C++. It's the same problem.) – cdhowie Jun 10 '22 at 20:57
  • @cdhowie That does make a lot of sense! Though it seems that `Eq` will also not work but none of the exclusion rule in [object safety](https://doc.rust-lang.org/reference/items/traits.html#object-safety) seems to apply. Also, if I remove all supertraits to just `pub trait Domainable {}` then it gives a new error [E0277](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=8163062cf4da9f476bdf394600019a62) – Mike Jun 11 '22 at 07:01
  • 1
    `eq` breaks the rule "Be a method that does not use `Self` except in the type of the receiver." That makes it non-dispatchable, but it doesn't have a `where Self: Sized` bound to mark it as explicitly non-dispatchable. – John Kugelman Jun 11 '22 at 18:08
  • 1
    @Mike `dyn` values are unsized. If you want to use them in a context that requires a sized value, you likely need to box it. (`Box`) – cdhowie Jun 11 '22 at 18:50

0 Answers0