How to implement a trait method that returns a trait object (of a different trait)

53 Views Asked by At

I'm studying Rust, and I'm struggling to replicate some OOP pattern using Rust's traits.

Briefly, I'd like to know what is Rust's way to solve this kind of problem. I have an interface whose methods return objects of another interface. Talking about a particular example, a trait Population contains referneces to objets that implement the Human trait.

I'm not sure at what stage I should use Person instead of Human, because I have tried several ways to implement this, and I always end with some issue. Or maybe I'm missing some special syntax that is mandatory to implement this schema in Rust.

I've come up with this code example to show what I try to do:

use rand::{distributions::Alphanumeric, Rng};
use std::fmt;
trait Population {
    fn get_a_human(&self) -> &Box<dyn Human>;
}

trait Human {
    fn say_my_name(&self) -> &str;
}

#[derive(Debug)]
struct Person {
    name: String,
}

impl Person {
    fn new(name: &str) -> Person {
        Person {
            name: String::from(name),
        }
    }
}

impl Human for Person {
    fn say_my_name(&self) -> &str {
        &self.name
    }
}

impl fmt::Debug for dyn Human {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_tuple("").field(&self.say_my_name()).finish()
    }
}

#[derive(Debug)]
struct City {
    city_name: String,
    pop: Vec<Box<dyn Human>>,
}

impl City {
    fn new(name: &str, pop: Vec<Box<dyn Human>>) -> City {
        City {
            city_name: String::from(name),
            pop,
        }
    }
}

impl Population for City {
    fn get_a_human(&self) -> &Box<dyn Human> {
        &self.pop[0]
    }
}

fn add_rnd_pop() -> Vec<Box<Person>> {
    let mut v = Vec::new();

    for _ in 0..10 {
        let name: String = rand::thread_rng()
            .sample_iter(&Alphanumeric)
            .take(7)
            .map(char::from)
            .collect();

        v.push(Box::new(Person::new(&name)));
    }

    v
}

fn main() {
    let pop = add_rnd_pop();
    let cordoba = City::new("Cordoba", pop);

    println!("A person's name from Córdoba: {:?}", cordoba.get_a_human());
}
0

There are 0 best solutions below