capypad
0 Tage Serie
rust / intermediate
Snippet

Trait-Objekte und Dynamische Dispensation

Trait-Objekte ermöglichen heterogene Sammlungen, bei denen verschiedene Typen dasselbe Trait implementieren. Das dyn-Schlüsselwort zeigt dynamische Dispensation zur Laufzeit an und bestimmt, welche Implementierung basierend auf dem tatsächlichen Typ aufgerufen wird. Dies tauscht Kompilierzeit-Optimierung gegen Flexibilität. Die Verwendung von &dyn Drawable erzeugt eine Referenz auf ein Trait-Objekt, während Box<dyn Drawable> die Daten heap-allokiert besitzt. Der vtable-Mechanismus verfolgt, welche Implementierung jedes Objekt verwendet, und ermöglicht polymorphes Verhalten ohne Generics.

snippet.rs
rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
trait Drawable {
fn draw(&self);
fn area(&self) -> f64;
}
 
struct Circle { radius: f64 }
struct Rectangle { width: f64, height: f64 }
 
impl Drawable for Circle {
fn draw(&self) { println!("Circle r={}", self.radius); }
fn area(&self) -> f64 { std::f64::consts::PI * self.radius * self.radius }
}
 
impl Drawable for Rectangle {
fn draw(&self) { println!("Rectangle {}x{}", self.width, self.height); }
fn area(&self) -> f64 { self.width * self.height }
}
 
fn render_all(items: &[&dyn Drawable]) {
for item in items {
item.draw();
}
}
 
fn main() {
let shapes: Vec<Box<dyn Drawable>> = vec![
Box::new(Circle { radius: 2.0 }),
Box::new(Rectangle { width: 3.0, height: 4.0 }),
];
let total_area: f64 = shapes.iter().map(|s| s.area()).sum();
println!("Total area: {:.2}", total_area);
let refs: Vec<&dyn Drawable> = shapes.iter().map(|s| s.as_ref()).collect();
render_all(&refs);
}
Erklärung
1
&[&dyn Drawable]
Slice von Trait-Objekt-Referenzen an Funktion übergeben
2
Vec<Box<dyn Drawable>>
Vector besitzt Trait-Objekte auf dem Heap
3
Box::new(Circle { radius: 2.0 })
Heap-Allokation für dynamische Dispensation erforderlich
4
s.as_ref()
Konvertiert owned Box zu geborgter Referenz
5
s.area()
Virtueller Aufruf durch vtable-Lookup