Mehrere Implementierungsblöcke
Teil des Abschnitts Object Oriented Programming der Rust-Journey von Coddy — Lektion 5 von 61.
Rust erlaubt es Ihnen, die Methoden eines Structs auf mehrere impl-Blöcke aufzuteilen. Während alles in einem einzigen Block stehen könnte, kann die Aufteilung Ihren Code organisierter und lesbarer machen.
Hier ist ein Struct, dessen Funktionalität in zwei Implementierungsblöcke aufgeteilt ist:
struct BankAccount {
balance: f64,
}
// Konstruktor und grundlegende Abfragen
impl BankAccount {
fn new(initial: f64) -> BankAccount {
BankAccount { balance: initial }
}
fn balance(&self) -> f64 {
self.balance
}
}
// Transaktionsmethoden
impl BankAccount {
fn deposit(&mut self, amount: f64) {
self.balance += amount;
}
fn withdraw(&mut self, amount: f64) {
self.balance -= amount;
}
}
Beide Blöcke arbeiten nahtlos zusammen – Rust behandelt sie so, als wären sie einer. Sie können jede Methode aufrufen, unabhängig davon, in welchem Block sie definiert ist:
fn main() {
let mut account = BankAccount::new(100.0);
account.deposit(50.0);
println!("{}", account.balance()); // 150
}
Dieses Muster wird besonders nützlich, wenn Ihre Structs wachsen. Sie könnten Konstruktoren gruppieren, schreibgeschützte Methoden von verändernden trennen oder nach Funktionen organisieren. Es gibt keine Begrenzung dafür, wie viele impl-Blöcke ein Struct haben kann.
Aufgabe
EinfachErstellen Sie eine Player-Struktur mit zwei Feldern: name (String) und score (u32).
Organisieren Sie die Methoden der Struktur in zwei separaten Implementierungsblöcken:
Erster impl-Block — Konstruktor und Abfrage:
new— eine assoziierte Funktion, die einenname: Stringentgegennimmt und einenPlayerzurückgibt, dessenscoreauf0gesetzt istget_score— nimmt&selfentgegen und gibt den aktuellen Punktestand alsu32zurück
Zweiter impl-Block — Punktmodifikationen:
add_points— nimmt&mut selfundpoints: u32entgegen und addiert die Punkte zum Punktestandreset_score— nimmt&mut selfentgegen und setzt den Punktestand zurück auf0
Sie erhalten drei Eingaben:
- Erste Zeile: der Name des Spielers (String)
- Zweite Zeile: Punkte, die in der ersten Runde hinzugefügt werden sollen (u32)
- Dritte Zeile: Punkte, die in der zweiten Runde hinzugefügt werden sollen (u32)
Erstellen Sie einen Spieler mit Player::new, fügen Sie die Punkte der ersten Runde hinzu, geben Sie den Punktestand aus, setzen Sie dann den Punktestand zurück, fügen Sie die Punkte der zweiten Runde hinzu und geben Sie den endgültigen Punktestand aus.
Erwartetes Ausgabeformat:
{score_after_first_round}
{score_after_reset_and_second_round}Spickzettel
Rust ermöglicht es, die Methoden eines Structs auf mehrere impl-Blöcke aufzuteilen, um eine bessere Organisation zu erreichen:
struct BankAccount {
balance: f64,
}
// First impl block
impl BankAccount {
fn new(initial: f64) -> BankAccount {
BankAccount { balance: initial }
}
fn balance(&self) -> f64 {
self.balance
}
}
// Second impl block
impl BankAccount {
fn deposit(&mut self, amount: f64) {
self.balance += amount;
}
fn withdraw(&mut self, amount: f64) {
self.balance -= amount;
}
}
Alle Methoden aus verschiedenen impl-Blöcken arbeiten nahtlos zusammen:
let mut account = BankAccount::new(100.0);
account.deposit(50.0);
println!("{}", account.balance()); // 150
Es gibt keine Begrenzung dafür, wie viele impl-Blöcke ein Struct haben kann. Dieses Muster ist nützlich, um zusammengehörige Funktionalitäten zu gruppieren, wie zum Beispiel die Trennung von Konstruktoren und verändernden Methoden.
Probier es selbst
use std::io;
// TODO: Definiere das Player-Struct mit den Feldern name (String) und score (u32)
// TODO: Erster impl-Block - Konstruktor und Abfragemethoden (new, get_score)
// TODO: Zweiter impl-Block - Methoden zur Score-Änderung (add_points, reset_score)
fn main() {
let mut input = String::new();
// Spielername lesen
io::stdin().read_line(&mut input).expect("Failed to read line");
let name = input.trim().to_string();
// Punkte der ersten Runde lesen
input.clear();
io::stdin().read_line(&mut input).expect("Failed to read line");
let first_round: u32 = input.trim().parse().expect("Invalid number");
// Punkte der zweiten Runde lesen
input.clear();
io::stdin().read_line(&mut input).expect("Failed to read line");
let second_round: u32 = input.trim().parse().expect("Invalid number");
// TODO: Erstelle einen Spieler mit Player::new
// TODO: Punkte der ersten Runde hinzufügen und den Score ausgeben
// TODO: Score zurücksetzen
// TODO: Punkte der zweiten Runde hinzufügen und den finalen Score ausgeben
}Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Object Oriented Programming
1Methoden und Verhalten
Einführung in ImplementierungsblöckeDer Self-ParameterVeränderliche MethodenAssoziierte FunktionenMehrere ImplementierungsblöckeMethod ChainingRückblick – Rechteck-Aktionen4Projekt: Virtuelles Haustier
Das Haustier definierenDas Haustier füttern7Standard-Traits
Das Debug-TraitDas Display-TraitClone und CopyGleichheits-TraitsZusammenfassung – Druckbarer Punkt10Projekt: Dokumentensystem
Das Draw-TraitText-Komponente2Kapselung und Module
Grundlagen der ModuleDas Public-SchlüsselwortPrivate FelderGetterSetterWiederholung – Sicherer Tresor5Generics
Generische StructsGenerische MethodenMehrere generische TypenGenerische FunktionenZusammenfassung - Koordinatenpunkt8Traits als Einschränkungen
Syntax für Trait BoundsMehrfache BoundsDie Where-KlauselTypen mit Traits zurückgebenRückblick – Generischer Drucker11Design Patterns in Rust
Newtype-PatternKompositionDas Drop-TraitFrom und IntoZusammenfassung – Smart Pointer Mock3Fortgeschrittene Enums
Enums mit DatenMethoden für EnumsMatching von DatenvariantenDas Option-Enum erneut betrachtetZusammenfassung – Shape-Enum6Definition von Traits
Was ist ein Trait?Traits implementierenStandardimplementierungenStandardimplementierungen überschreibenTraits mit ParameternZusammenfassung – Media Player