0

Suppose, I have a number of traits:

trait Device {}
trait Named {}
trait Report {}

I have a number of structs, some of which implement some subset of these traits, and then I have some logic that expects a type that implements all of them, say, I want to have a collection of devices that have names and can produce reports. To reduce the typing (on the keyboard), I create a combined trait:

pub trait ReportNamedDevice: Named + Device {}
impl<T: Named + Device> ReportNamedDevice for T {}

(Report is omitted for now until I can make the existing ones work)

And then I use a collection of those in a struct:

pub struct Room<'a> {
    pub name: String,
    pub devices: Vec<&'a dyn ReportNamedDevice>,
}

Now, the room itself is Named, and I want to be able to add it to a House. And I want all of my collections of Named things to contain things with unique names, so I write the following filter function:

fn name_is_in_named_vector(name: &String, haystack: &Vec<&dyn Named>) -> bool {
    haystack
        .iter()
        .map(|x| x.name().clone())
        .collect::<Vec<String>>()
        .contains(name)
}

But when I try to use it in my Room impl, I get a mismatched types error, because the compiler doesn't treat ReportNamedDevice as Named:

impl<'a> Room<'a> {
    pub fn add_device(&mut self, device: &'a dyn ReportNamedDevice) {
        if !name_is_in_named_vector(device.name(), &self.devices) {
            self.devices.push(device)
        }
    }
}

I know I could replace the haystack type in the filter function, but I would like to be able to reuse it for filtering rooms as well.

Is there a way I can make this work?

Ibolit
  • 9,218
  • 7
  • 52
  • 96

1 Answers1

4

Yes there is a way, just use generics instead of trait objects, that works becaues while a &dyn ReportNamedDevice isn't the same as a &dyn Named it does implement Named:

fn name_is_in_named_vector<T: ?Sized + Named>(name: &str, haystack: &[&T]) -> bool {
    haystack
        .iter()
        .find(|x| x.name() == name)
        .is_some()
}

Playground

Note: I've applied isaactfa's hint about find in the comments and the tips of Why is it discouraged to accept a reference &String, &Vec, or &Box as a function argument?

cafce25
  • 15,907
  • 4
  • 25
  • 31