This post is about creating and using a Vec instance with a specific trait type.
In Java
We would have something like this in Java.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public static void main(String[] args) { // Bounceables List<Bounceable> bounceableList = new ArrayList<>(); bounceableList.add(new BasketBall()); bounceableList.add(new TennisBall()); for (Bounceable b: bounceableList) { b.bounce(); } // Inflatables List<Inflatable> inflatableList = new ArrayList<>(); inflatableList.add(new BasketBall()); // Compilation error! //inflatableList.add(new TennisBall()); for (Inflatable i: inflatableList) { i.inflate(); } } |
This outputs:
1 2 3 | Basket Ball is bouncing Tennis Ball is bouncing Basket Ball has been inflated |
Interfaces and Classes
For the Java interfaces and classes’ definitions, please read Rust – Function that accepts Struct Instance that implements a specific Trait.
In Rust
Now it’s Rust time! Note that the Rust codes on this post are a bit different from Rust – Function that accepts Struct Instance that implements a specific Trait.
Traits
We are using &self instead of just self.
1 2 3 4 5 6 7 | trait Bounceable { fn bounce(&self); } trait Inflatable { fn inflate(&self); } |
Trait Type Implementations
These trait functions are using &self.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | struct BasketBall {} impl Bounceable for BasketBall { fn bounce(&self) { println!("Basket Ball is bouncing"); } } impl Inflatable for BasketBall { fn inflate(&self) { println!("Basket Ball has been inflated"); } } struct TennisBall {} impl Bounceable for TennisBall { fn bounce(&self) { println!("Tennis Ball is bouncing"); } } |
Vec with a specific Trait Type
There are two 2 Vec instances here. The first one is of type Box<dyn Bounceable>, and the other is Box<dyn Inflatable>.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | fn main() { let mut vec_bounceable:Vec<Box<dyn Bounceable>> = Vec::new(); vec_bounceable.push(Box::new(BasketBall{})); vec_bounceable.push(Box::new(TennisBall{})); for o in vec_bounceable.iter() { o.bounce(); } let mut vec_inflatable:Vec<Box<dyn Inflatable>> = Vec::new(); vec_inflatable.push(Box::new(BasketBall{})); // Compilation Error //vec_inflatable.push(Box::new(TennisBall{})); for o in vec_inflatable.iter() { o.inflate(); } } |
This outputs
1 2 3 | Basket Ball is bouncing Tennis Ball is bouncing Basket Ball has been inflated |
Now, why do we need to use Box<dyn Bounceable>> and Box<dyn Inflatable> instead of just dyn Bounceable and dyn Inflatable or just Bounceable and Inflatable?
Trait Sized
A compiler does not know anything about run-time. There’s no way for it to know how a trait behaves at run-time.
Please see https://doc.rust-lang.org/std/marker/trait.Sized.html.
What happens if we do not use Box? We get this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | error[E0277]: the size for values of type `dyn Bounceable` cannot be known at compilation time --> src\main.rs:42:28 | 42 | let mut vec_bounceable:Vec<dyn Bounceable> = Vec::new(); | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `dyn Bounceable` = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait> = note: required by `std::vec::Vec` error[E0277]: the size for values of type `dyn Inflatable` cannot be known at compilation time --> src\main.rs:50:28 | 50 | let mut vec_inflatable:Vec<dyn Inflatable> = Vec::new(); | ^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `dyn Inflatable` = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait> = note: required by `std::vec::Vec` |
Hint: using Box allows us to tap into the heap region of the memory.
We tested the codes using Rust 1.39.0.