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
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