How to iterate over Vec<dyn Sized>

fn process(data: Vec<dyn Sized>) {
    // How do I iterate over the `data`, and determine the type of each item at runtime?
}

It's not even possible to construct a Vec of dyn anything, because a Vec's elements must be equally sized (and dyn Sized is a contradiction), but you can make a Vec<Box<dyn std::any::Any>>. The Box handles the variable sizes and the Any trait allows comparing the type of the value and downcasting.

6 Likes

Doing this in a verbose way:

use std::any::Any;

fn process(data: Vec<Box<dyn Any>>) {
    for element in data {
        if element.is::<String>() {
            let s: Box<String> = element.downcast::<String>().unwrap();
            println!("Got String: {s}");
        } else if element.is::<i32>() {
            let i: Box<i32> = element.downcast::<i32>().unwrap();
            println!("Got i32: {i}");
        } else {
            println!("Got unknown element");
        }
    }
}

fn main() {
    let vec: Vec<Box<dyn Any>> = vec![
        Box::new(true),
        Box::new("Hello!".to_string()),
        Box::new(17),
        Box::new(18),
        Box::new("&str"),
    ];
    process(vec);
}

(Playground)

Output:

Got unknown element
Got String: Hello!
Got i32: 17
Got i32: 18
Got unknown element

If you want to take the values out of the Box:

-            let s: Box<String> = element.downcast::<String>().unwrap();
+            let s: String = *element.downcast::<String>().unwrap();
-            let i: Box<i32> = element.downcast::<i32>().unwrap();
+            let i: i32 = *element.downcast::<i32>().unwrap();

(Playground)

If you keep the type annotations for s and i, you can also remove the turbofish after downcast:

-            let s: String = *element.downcast::<String>().unwrap();
+            let s: String = *element.downcast().unwrap();
-            let i: i32 = *element.downcast::<i32>().unwrap();
+            let i: i32 = *element.downcast().unwrap();

(Playground)

And if you don't want to move the values but work with references:

use std::any::Any;

fn process(data: &[Box<dyn Any>]) {
    for element in data {
        if element.is::<String>() {
            let s: &String = element.downcast_ref().unwrap();
            println!("Got String: {s}");
        } else if element.is::<i32>() {
            let i: &i32 = element.downcast_ref().unwrap();
            println!("Got i32: {i}");
        } else {
            println!("Got unknown element");
        }
    }
}

fn main() {
    let vec: Vec<Box<dyn Any>> = vec![
        Box::new(true),
        Box::new("Hello!".to_string()),
        Box::new(17),
        Box::new(18),
        Box::new("&str"),
    ];
    process(&vec);
}

(Playground)


The last example with references can be written even nicer:

fn process(data: &[Box<dyn Any>]) {
    for element in data {
        if let Some(s) = element.downcast_ref::<String>() {
            println!("Got String: {s}");
        } else if let Some(i) = element.downcast_ref::<i32>() {
            println!("Got i32: {i}");
        } else {
            println!("Got unknown element");
        }
    }
}

(Playground)

4 Likes

exactly what I want! :hand_with_index_finger_and_thumb_crossed: