Tony Beta Lambda
Tony Beta Lambda

Reputation: 571

A parameter that accepts a sequence of trait objects and iterate through it multiple times in Rust

I have a function that takes a sequence of trait objects and iterates through it multiple times, e.g.,

trait Animal {
    fn make_noise(&self);
}

fn make_lots_of_noises(animals: &[&dyn Animal]) {
    animals.iter().for_each(|animal| animal.make_noise());
    animals.iter().for_each(|animal| animal.make_noise());
    animals.iter().for_each(|animal| animal.make_noise());
}

But I want the function to be able to operate on both borrowed and owned data structures. Here are the options I tried:

&[&dyn Animal] as shown in the code fragment. Problem: if I have owned trait objects, e.g., animals: &[Box<dyn Animal>], then I have to call make_lots_of_noises(animals.map(Box::as_ref).collect::<Vec<_>>().as_slice(), which involves unnecessary allocation on the heap.

&[&R] where R: AsRef<dyn Animal> + ?Sized. Problem: T doesn't implement AsRef<T>! It now works with all kinds of smart pointers, but not &[&dyn Animal].

Upvotes: 0

Views: 71

Answers (1)

Kevin Reid
Kevin Reid

Reputation: 43743

Use the Borrow trait, which does have the impl Borrow<T> for T you want.

use std::borrow::Borrow;

trait Animal {
    fn make_noise(&self);
}

struct Mouse;
impl Animal for Mouse {
    fn make_noise(&self) { /* squeak */ }
}

fn make_lots_of_noises<A: Borrow<dyn Animal>>(animals: &[A]) {
    animals.iter().for_each(|animal| animal.borrow().make_noise());
    animals.iter().for_each(|animal| animal.borrow().make_noise());
    animals.iter().for_each(|animal| animal.borrow().make_noise());
}

fn main() {
    make_lots_of_noises(&[&Mouse as &dyn Animal]);
    make_lots_of_noises(&[Box::new(Mouse) as Box<dyn Animal>]);
}

Upvotes: 2

Related Questions