How to implement list of trait types?

#![allow(unused)]
use std::vec::Vec;
use std::rc::Rc;
use std::ops::{Deref, DerefMut};
use std::borrow::BorrowMut;

trait Animal {
    fn age_comp(&mut self, value: f64) -> bool;
}
struct Cat {
    name: String,
    age: f64,
}

impl Cat {
    fn new(name: String, age: f64) -> Cat {
        Cat {name, age}
    }
}

impl Animal for Cat {
    fn age_comp(&mut self, value: f64) -> bool {
         self.age > value
    }
}

struct AnimalList {
    objects: Vec<Rc<dyn Animal>>,
}

impl AnimalList {
    fn new(objects: Vec<Rc<dyn Animal>>) -> Self {
        AnimalList {objects}
    }
    
    fn add(&mut self, object: Rc<dyn Animal>) {
        self.objects.push(object);
    }
    
    fn age_comp(&mut self, value: f64) -> bool {
        let mut is_true = false;
        for object in self.objects.iter() {
            if object.age_comp(value) {
                is_true = true;
            }
        }
        
        return is_true;
    }
}


fn main() {
    let name = String::from("carl");
    let carl = Rc::new(Cat::new(name, 6.0));
    
    let tom_name = String::from("tom");
    let tom = Rc::new(Cat::new(tom_name, 4.5));
    
    let animalList: Vec<Rc<dyn Animal>> = Vec::new();
    let mut listOfAnimals = AnimalList::new(animalList);
    listOfAnimals.add(carl);
    listOfAnimals.add(tom);
    
    if listOfAnimals.age_comp(5.0) {
        println!("true");
    }
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0596]: cannot borrow data in an `Rc` as mutable
  --> src/main.rs:43:16
   |
43 |             if object.age_comp(value) {
   |                ^^^^^^ cannot borrow as mutable
   |
   = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Rc<dyn Animal>`

For more information about this error, try `rustc --explain E0596`.
error: could not compile `playground` (bin "playground") due to 1 previous error

Looks like you want Box<dyn Animal> and iter_mut, or just change age_comp to take &self.

1 Like

Hi, welcome! @drewtato posted a good solution. I just want to add a couple things.

To me it makes sense to apply their second suggested fix (change age_comp to take &self). age_comp doesn't modify self so &mut self should not be used, unless perhaps you intend to add code to age_comp that will modify self. And you should probably do this to AnimalList::age_comp as well, since it doesn't modify self either.

If you want to know more about using Rc vs Box, please let us know, and it would help to tell us why you're currently using Rc.

If you do need to use Rc and you also need to use &mut self in your age_comp function, then you can use iter_mut and Rc::get_mut:

fn age_comp(&mut self, value: f64) -> bool {
    let mut is_true = false;
    for object in self.objects.iter_mut() {
        if Rc::get_mut(object).unwrap().age_comp(value) {
            is_true = true;
        }
    }

    return is_true;
}

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.