John
John

Reputation: 45

Why is Rust ok with accepting a different struct when implementing the same trait?

I have the following in my tests which compiles and works well:

#[test]
fn zoo_test() {
    let mut zoo = Zoo::new();

    zoo.add(Monkey::new("m1".to_string()));
    zoo.add(Limur::new("l1".to_string()));

    assert_eq!(zoo.hi_all::<Monkey>(), vec![
        "Monkey m1 says: ah ah ah".to_string(),
        "Limur l1 says: yo".to_string()
    ]);
}

However, I'm a bit confused why zoo.hi_all::<Monkey>() works since it could be either monkey or limur. In other words I don't understand why it works for limur, or am I doing this wrong?

Edit: Here's the code I'm using for src/zoo.rs:

pub mod animal;
pub mod monkey;
pub mod limur;

use std::collections::VecDeque;
use animal::Animal;

pub struct Zoo<'a> {
    list: VecDeque<Box<dyn Animal + 'a>>,
}

impl<'a> Zoo<'a> {
    pub fn new() -> Zoo<'a> {
        Zoo {
            list: VecDeque::new(),
        }
    }

    pub fn add<T>(&mut self, animal: T)
    where
        T: Animal + 'a,
    {
        self.list.push_back(Box::new(animal));
    }

    pub fn hi_all<T>(&self) -> Vec<String>
    where
        T: Animal + 'a,
    {
        let mut hi: Vec<String> = vec![];
        for animal in &self.list {
            if let Some(what) = animal.says() {
                hi.push(what);
            }
        }
        hi
    }
}

Upvotes: 0

Views: 122

Answers (1)

Peter Hall
Peter Hall

Reputation: 58695

The type variable T is declared in the type of this function:

pub fn hi_all<T>(&self) -> Vec<String>
where
    T: Animal + 'a,
{
    let mut hi: Vec<String> = vec![];
    for animal in &self.list {
        if let Some(what) = animal.says() {
            hi.push(what);
        }
    }
    hi
}

but is never actually used in the function's body! Removing that constraint will have no impact because there is no variable of type T mentioned in the function.

In fact, it never could be used in any practical way, because the elements of the VecDeque are of a specific concrete type, Box<dyn Animal> whereas, from the perspective of code inside the function, T could be any type that implements Animal.

If you need to filter the animals by type then I suggest you use an enum instead of trait objects, so you can discriminate on the enum variant.

Upvotes: 3

Related Questions