Search code examples
arraysrustgeneric-programming

How can I have an array of Any type?


I'm trying to model a dataframe-like structure. I know how use enums here, but I'm exploring how do it similar to C#/Python/etc.

I tried to follow Rust Trait object conversion but things are not working:

use std::any::{Any};
use std::fmt::Debug;

pub trait Value: Any + Sized {
    fn as_any(&self) -> &Any {
        self
    }

    fn as_any_mut(&mut self) -> &mut Any {
        self
    }
}

impl Value for i32 {}

#[derive(Debug)]
struct Frame {
    data: Vec<Box<Any>>,
}

fn make_int(of: Vec<i32>) -> Frame {
    let data = of.into_iter().map(|x| Box::new(x.as_any())).collect();
    Frame {
        data: data,
    }
}

The compiler complains:

error[E0277]: the trait bound `std::vec::Vec<std::boxed::Box<std::any::Any>>: std::iter::FromIterator<std::boxed::Box<&std::any::Any>>` is not satisfied
  --> src/main.rs:40:61
   |
40 |     let data = of.into_iter().map(|x| Box::new(x.as_any())).collect();
   |                                                             ^^^^^^^ a collection of type `std::vec::Vec<std::boxed::Box<std::any::Any>>` cannot be built from an iterator over elements of type `std::boxed::Box<&std::any::Any>`
   |
   = help: the trait `std::iter::FromIterator<std::boxed::Box<&std::any::Any>>` is not implemented for `std::vec::Vec<std::boxed::Box<std::any::Any>>`

Solution

  • The main problem is with this function:

    fn as_any(&self) -> &Any {
        self
    }
    

    This means that you can borrow a Value as a &Any, (it converts a &Value into a &Any).

    But then, you want to create a Box<Any> from that &Any. That will never work, because &Any is a borrowed value, while Box<Any> is owned.

    The easiest solution would be to change the trait to return the boxed value (an owned trait object):

    pub trait Value: Any + Sized {
        fn as_boxed_any(&self) -> Box<Any> {
            Box::new(self)
        }
        //The mut variation is not needed
    }
    

    Now the make_int function is trivial:

    fn make_int(of: Vec<i32>) -> Frame {
        let data = of.into_iter().map(|x| x.as_boxed_any()).collect();
        Frame {
            data: data,
        }
    }
    

    UPDATE: Tinkering a bit, I've found that you can create the Vec<Box<Any>> by writing:

    fn make_int(of: Vec<i32>) -> Frame {
        let data = of.into_iter().map(|x| Box::new(x) as Box<Any>).collect();
        Frame {
            data: data,
        }
    }
    

    If you are writing the trait only for this conversion, you don't actually need it.