Consider the following Rust code that uses a trait object to call a method on different types:
trait Speak {
fn speak(&self) -> String;
}
struct Dog;
struct Cat;
impl Speak for Dog {
fn speak(&self) -> String {
String::from("Woof")
}
}
impl Speak for Cat {
fn speak(&self) -> String {
String::from("Meow")
}
}
fn animal_speak(animal: &dyn Speak) {
println!("{}", animal.speak());
}
fn main() {
let dog = Dog;
let cat = Cat;
animal_speak(&dog);
animal_speak(&cat);
}Remember that the trait object calls the method implemented for the actual type.
The function animal_speak takes a reference to a trait object &dyn Speak. When called with &dog, it calls Dog's implementation of speak, which returns "Woof". When called with &cat, it calls Cat's implementation, returning "Meow".
Which of the following is a limitation when using trait objects in Rust?
Think about what kind of methods can be called through a trait object.
Trait objects use dynamic dispatch and require the trait to be 'object safe'. Traits with generic methods are not object safe and cannot be used as trait objects.
Examine the code below and identify why it fails to compile:
trait Draw {
fn draw(&self);
fn resize<T>(&self, scale: T);
}
struct Circle;
impl Draw for Circle {
fn draw(&self) {
println!("Drawing a circle");
}
fn resize<T>(&self, scale: T) {
println!("Resizing circle");
}
}
fn paint(shape: &dyn Draw) {
shape.draw();
}
fn main() {
let c = Circle;
paint(&c);
}Check the trait methods and their signatures for object safety rules.
Traits used as trait objects must be object safe. Having a generic method like resize<T> makes the trait not object safe, so &dyn Draw is invalid.
Given the trait Speak and struct Dog implementing it, which code snippet correctly creates a boxed trait object?
trait Speak {
fn speak(&self) -> String;
}
struct Dog;
impl Speak for Dog {
fn speak(&self) -> String {
String::from("Woof")
}
}
fn main() {
// Which line correctly creates a boxed trait object?
}Remember the syntax for trait objects and boxing in Rust.
Trait objects require dyn keyword. Box::new(Dog) creates a boxed instance. Option D uses correct syntax. Option D misses dyn. Option D calls a non-existent constructor. Option D boxes a reference, which is not the intended trait object.
Consider this Rust code that stores different types implementing a trait in a vector of trait objects:
trait Animal {
fn name(&self) -> &str;
}
struct Dog;
struct Cat;
impl Animal for Dog {
fn name(&self) -> &str { "Dog" }
}
impl Animal for Cat {
fn name(&self) -> &str { "Cat" }
}
fn main() {
let mut animals: Vec<Box<dyn Animal>> = Vec::new();
animals.push(Box::new(Dog));
animals.push(Box::new(Cat));
animals.push(Box::new(Dog));
animals.push(Box::new(Cat));
animals.pop();
animals.push(Box::new(Dog));
println!("Number of animals: {}", animals.len());
}Count the pushes and pops carefully to find the final length.
The vector starts empty. It pushes 4 items, then pops 1 (removes last), then pushes 1 more. So total items = 4.