Dynamic Dispatch in Rust Using the dyn Keyword
Understanding dyn in Rust
In Rust, the keyword dyn is used to indicate a dynamically dispatched trait object. Unlike static dispatch, where the compiler determines the type at compile time, dynamic dispatch allows for runtime polymorphism. This is particularly useful when working with types that implement a common trait but differ in implementation.
Using dyn enables flexibility by storing and operating on different types that share a trait through a common interface. However, this comes with a trade-off in performance due to runtime indirection.
Syntax
Code:
fn example(param: &dyn TraitName);
Here:
- &dyn TraitName is a reference to a trait object that implements TraitName.
Example Code and Explanation
Code:
// Define a trait with a common behavior
trait Shape {
fn area(&self) -> f64; // Define an area method
}
// Implement the Shape trait for a Rectangle struct
struct Rectangle {
width: f64,
height: f64,
}
impl Shape for Rectangle {
fn area(&self) -> f64 {
self.width * self.height // Calculate the rectangle's area
}
}
// Implement the Shape trait for a Circle struct
struct Circle {
radius: f64,
}
impl Shape for Circle {
fn area(&self) -> f64 {
3.14159 * self.radius * self.radius // Calculate the circle's area
}
}
// A function that accepts a trait object
fn print_area(shape: &dyn Shape) {
println!("The area is: {}", shape.area()); // Call the trait's method
}
fn main() {
// Create a rectangle and a circle
let rect = Rectangle { width: 5.0, height: 10.0 };
let circ = Circle { radius: 3.0 };
// Pass references to the function that accepts a trait object
print_area(&rect); // Prints: The area is: 50.0
print_area(&circ); // Prints: The area is: 28.27431
}
Explanation
1. Trait Definition: The Shape trait defines a method, area, that all implementing types must define.
2. Struct Implementations: Both Rectangle and Circle provide their own versions of the area method.
3. Dynamic Dispatch: The function print_area accepts a &dyn Shape, meaning any type implementing the Shape trait can be passed. The exact method implementation to call is resolved at runtime.
4. Runtime Polymorphism: Despite rect and circ being different types, they can be used interchangeably in the print_area function.
Advantages of dyn
1. Flexibility: Operate on multiple types via a common interface.
2. Code Reusability: Write functions or methods that work for any type implementing a trait.
Limitations of dyn
1. Performance Overhead: Runtime dispatch involves a slight performance hit compared to static dispatch.
2. Type Erasure: Some type information is lost, requiring dynamic checks or downcasting in complex scenarios.
When to Use dyn
- When you need polymorphism and can afford the runtime cost.
- When working with heterogeneous collections of objects implementing the same trait.
Advanced Features
Trait Objects in Collections
Code:
fn main() {
let shapes: Vec<Box<dyn Shape>> = vec![
Box::new(Rectangle { width: 4.0, height: 5.0 }), // Store a rectangle
Box::new(Circle { radius: 3.0 }), // Store a circle
];
for shape in shapes {
println!("Area: {}", shape.area()); // Call the appropriate area method
}
}
Here, Box<dyn Shape> stores trait objects on the heap, allowing a heterogeneous collection of Shape objects.
Rust Language Questions, Answers, and Code Snippets Collection.
- Weekly Trends and Language Statistics
- Weekly Trends and Language Statistics