↑Programmieren in Rust
Unter Objektorientierung versteht man eine Zusammenfassung von Konzepten, die dazu dienen, Prozeduren eng an Daten zu binden. Die Daten werden in diesem Zusammenhang als Objekte bezeichnet, ihre Prozeduren als Methoden.
Die Sprache Rust unterstützt nicht jedes Konzept in direkter Weise. Dies gilt vor allem für dynamische Mechanismen wie Reflektion. Um ein Gefühl dafür zu bekommen, inwieweit Objektorientierung ermöglicht wird, wollen wir Umsetzungen in kurzen Beispielen erläutern. Für solche Mechanismen, die nicht direkt möglich sind, soll hierbei der Versuch einer Emulierung in Angriff genommen werden.
Erwähnen möchte ich noch, dass es prinzipell möglich ist, mit Trait-Objekten ein beliebiges Objektsystem zu konstruieren. Allerdings geht hiermit der Verlust von Optimierungen und von statischer Typsicherheit einher.
struct Bird { name: String } impl Bird { fn fly(&self) { println!("{} fliegt.", self.name); } } fn main() { let bird = Bird {name: String::from("Donald")}; bird.fly(); }
Vererbung gibt es in Rust nicht. Sie lässt sich allerdings durch Komposition modellieren.
struct Bird { name: String } impl Bird { fn fly(&self) { println!("{} fliegt.", self.name); } } struct Duck { bird: Bird } impl Duck { fn new(name: String) -> Self { Self{bird: Bird {name}} } fn dive(&self) { println!("{} kann tauchen.", self.bird.name); } } fn main() { let duck = Duck::new("Donald".into()); duck.bird.fly(); duck.dive(); }
use std::any::Any; trait BirdTrait { fn as_any(&self) -> &dyn Any; fn name(&self) -> &str; fn fly(&self); } struct Bird { name: String } impl Bird { fn new(name: &str) -> Self { Self {name: name.into()} } } impl BirdTrait for Bird { fn as_any(&self) -> &dyn Any {self} fn name(&self) -> &str {&self.name} fn fly(&self) { println!("{} fliegt.", self.name); } } struct Duck { bird: Bird } impl Duck { fn new(name: &str) -> Self { Self {bird: Bird {name: name.into()}} } fn dive(&self) { println!("{} kann tauchen.", self.bird.name); } } impl BirdTrait for Duck { fn as_any(&self) -> &dyn Any {self} fn name(&self) -> &str {&self.bird.name} fn fly(&self) { self.bird.fly(); } } fn main() { let birds: Vec<Box<dyn BirdTrait>> = vec![ Box::new(Duck::new("Donald")), Box::new(Bird::new("Bella")) ]; for bird in birds.iter() { bird.fly(); if let Some(duck) = bird.as_any().downcast_ref::<Duck>() { duck.dive(); } else { println!("{} ist keine Ente.", bird.name()); } } }
Klassenvariablen sind Variablen die nicht Speicherplätze von
Objekten sind, sondern von Klassen. Das heißt, eine solche Variable
liegt nur einmal pro Klasse vor. Zur Modellierung konstruiert
man am besten eine Laufzeitumgebung env
, die eine
Laufzeitdarstellung der Klasse enthält. Damit das Programm an allen
Stellen Zugriff auf die Laufzeitumgebung bekommen kann, kommt ein
Zeiger auf die Laufzeitumgebung in eine globale Variable
ENV
. Zur Vereinfachung betrachten wir nur ein
Programm ohne Nebenläufigkeit. Die globale Variable wird daher
Strang-lokal gemacht.
use std::rc::Rc; use std::cell::RefCell; struct DuckClass { name: Rc<str>, quack: Rc<str> } struct Env { duck_class: Rc<RefCell<DuckClass>> } impl Env { fn new() -> Rc<Self> { Rc::new(Self { duck_class: Rc::new(RefCell::new(DuckClass { name: Rc::from("Duck"), quack: Rc::from("quacks") })) }) } fn get() -> Rc<Env> { ENV.with(|env| env.clone()) } } thread_local! { static ENV: Rc<Env> = Env::new(); } struct Duck { class: Rc<RefCell<DuckClass>>, name: String } impl Duck { fn new(env: &Env, name: &str) -> Self { Self {name: name.into(), class: env.duck_class.clone()} } fn class_name(&self) -> Rc<str> { self.class.borrow().name.clone() } fn name(&self) -> &str {&self.name} fn quack(&self) { println!("{} {}.", self.name, self.class.borrow().quack); } } fn load_german(env: &Env) { let mut duck_class = env.duck_class.borrow_mut(); duck_class.name = Rc::from("Ente"); duck_class.quack = Rc::from("quakt"); } fn main() { let env = &Env::get(); load_german(env); let duck = Duck::new(env,"Donald"); duck.quack(); println!("Name von duck ist: {}.", duck.name()); println!("Klasse von duck ist: {}.", duck.class_name()); }