Creating a heterogenous type for holding several objects of different types in RUST
Tags: rust, webdev, systems, discuss
Published: 2022-07-05 00:50:10

Type available within Rust supports composition of homogenous types, these are availabe in types such as 
```rust
Vec<T>;  // an vector
[T;N];   // an array
();      // tuple 
```

which can be used depending on various scenarios required ,
though you can use it as a heterogenous type but it is naturally constrained by the [`Sized`](https://doc.rust-lang.org/std/marker/trait.Sized.html) trait which is applied to it during compile time which constrain us to further use it dynamically.

---
**The key thing to note here is we can segregate our types based upon some common relation we can derive from them this will be the key idea to follow along towards achieving so** 

but when it comes to supporting the heterogenous types in rust things get little bit tricky here, to compose a type which can accommodate the objects from several types available using `dyn` trait which points the object in vtable which is behind a pointer
you can read [here](https://doc.rust-lang.org/std/keyword.dyn.html), 

we want our type to be resizable during runtime therefore I will choose to go with `Vec<T>` type which can hold our objects 

since whichever types applies this trait can directly reference a object using two techniques 

**1) using a reference to the object** 

```rust
   Vec<&dyn yourTrait>;
```


**2) placing the object behind a Box smart pointer**

```rust
  Vec<Box<dyn youTrait>>;
```

---
let's say the name of your trait is `Foo` and it declares one method to be implement 
```rust
trait Foo {
  fn call(&self);
}
```

now we will create some random types which will implement this 
are as follows
(for the sake of simplicity I have defined a tuple struct here but your type can be much more complicated than this ... as long as you implement the trait you should be fine)

```rust
#[derive(Debug)]
struct A(i32); 
impl Foo for A {
    fn call(&self) {
        println!("type A: {:?}", self.0);
    }
}

#[derive(Debug)]
struct B(i32); 
impl Foo for B {
    fn call(&self) {
        println!("type B: {:?}", self.0);
    }
}

#[derive(Debug)]
struct C(i32); 
impl Foo for C {
    fn call(&self) {
        println!("type C: {:?}", self.0);
    }
}
```
here you will observe one strange thing which is all the types implement the same exact definition of the trait methods but this can be different depending upon the operations you wanna do with that type.

---
now lets begin declaring a type which can simply hold them 
according the first method

**1) using the direct reference to the object **

#### one important thing to note here is, the reference you gonna provide for your types will either be mutable or immutable only !
which simply mean, it applies to all the trait objects uniformly and u can't have some trait objects passed on as mutably and others as immutably.

```rust
fn main() {
     //some random trait objects declared
    let a = &A(43); 
    let b = &B(73); 
    let c = &C(95); 

     // now we will compose our homogenous type based on the previously types
    let objs: Vec<&dyn Foo> = vec![a, b, c];

    for I in objs.iter() {
       I.call(); 
    }
}
```
since our objects `a, b, c` holds a reference to the types `A, B, C` respectively we can pass them directly here to construct our homogenous type out of them
output : 
```shell
type A: 43
type B: 73
type C: 95
```
---
**2)  Using Box smart pointer**

now we will try out another interesting method, where we will place our objects behind a [Box](https://doc.rust-lang.org/std/boxed/index.html) smart pointer in this case you  don't have to worry about initializing your traits objects  during compile itself, instead you will place them behind a pointer pointing to the memory location on heap
since in this technique the rules of borrow doesn't apply there it is quite convinence to work with
also the plus side is our objects can be init. during runtime which is a added advantage

```rust


fn main() {
     
    //here we place our objects behind a box pointer 
    let a = Box::new(A(43)); 
    let b = Box::new(B(73)); 
    let c = Box::new(C(95)); 

    //our type hold a boxed trait object as type to accept
    let mut objs: Vec<Box<dyn Foo>> = vec![a,b,c] ;

    for I in objs.iter() {
        I.call();
    }

}
```
here the output will be exactly same as the previous one but with more features of box pointer:
```shell
type A: 43
type B: 73
type C: 95
```
there you .. go congratulations you successfully learned how to create a new type >> 
---
Thank you 😊 so much for reading till here 🥂🎉🎊!

if you have any comments, suggestion's, doubt or advice feel free  to post in comment section