0

I have two related traits List and ListItem (playground link).

trait List {
    type Item: ListItem;
    fn items(&self) -> Vec<Self::Item>;
    /* more stuff */
}

where Item is an associated type on List and ListItem has a custom implementation for PartialEq and Debug

Inside of a List trait method that looks like

fn foo(&self, other: &dyn List<Item=Self::Item>)

I would like to compare sub-slices of the items, using the PartialEq defined for ListItem. e.g.

let lhs: &[&dyn ListItem] = ...;
let rhs: &[&dyn ListItem] = ...;
assert_eq!(lhs, rhs);

but I cannot coerce the slices to the correct type.

fn foo(&self, other: &dyn List<Item = Self::Item>) {
   let items = self.items();
   let items_slice: &[&dyn ListItem] = items.as_slice(); // <-- Error here
error[E0308]: mismatched types
   |
40 |         let these: &[&dyn ListItem] = these_items.as_slice();
   |                    ----------------   ^^^^^^^^^^^^^^^^^^^^^^ expected `&[&dyn ListItem]`, found `&[<Self as List>::Item]`
   |                    |
   |                    expected due to this
   |
   = note: expected reference `&[&dyn ListItem]`
              found reference `&[<Self as List>::Item]`
   = help: consider constraining the associated type `<Self as List>::Item` to `&dyn ListItem`
   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html

(playground link)

How can I coerce the slice to the correct type?

Increasingly Idiotic
  • 5,700
  • 5
  • 35
  • 73
  • 2
    trust me just don't use dyn unless you really really know why you are using dyn and there is no other solution, https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=bcfdf2774761968f3c6bf5511fe01b60 – Stargateur Jun 25 '23 at 22:17
  • Thanks! Unfortunately the linked playground does not do what I am trying to do. Specifically "I would like to compare sub-slices of the items, using the `PartialEq` defined for `ListItem`". Including `where Self::Item: PartialEq` will not use the PartialEq implementation for `ListItem`. https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=cfd704ff4d4f51fad519fb001c44de88 – Increasingly Idiotic Jun 26 '23 at 18:02
  • But why would you do that ? It's really look you are tried to copy something from another language and try to do it in Rust. Put a eq method in the trait ListItem if you really want. – Stargateur Jun 26 '23 at 19:21
  • Relying on the users of the trait to correctly define a `PartialEq` that works in the way expected by the trait would prevent structs from defining their own `PartialEq` implementation. It would be cleaner to define a separate `struct ListItem` and change the interface to accept `Into`. – Increasingly Idiotic Jun 27 '23 at 00:12
  • Adding an `eq` method to the trait makes things ugly. `!lhs.eq(rhs)` looks much worse than `lhs != rhs`, and there is no clean way to assert that collections of the trait are equal, like in this question, where I am trying to assert if two slices containing references to items that implement the trait, are equal. – Increasingly Idiotic Jun 27 '23 at 00:15

1 Answers1

1

You cannot. First, you need references, and items contains owned items. Second, you need fat references, and items has concrete types.

You need to collect them into a Vec:

let items = self.items();
let items_dyn = items
    .iter()
    .map(|item| item as &dyn ListItem)
    .collect::<Vec<_>>();
Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77