Private Felder
Teil des Abschnitts Objektorientierte Programmierung der Rust-Journey von Coddy. Lektion 10 von 61.
In der vorherigen Lektion hast du alles öffentlich gemacht: die Struktur, ihre Felder und ihre Methoden. Echte Kapselung bedeutet jedoch, zu kontrollieren, wie auf deine Daten zugegriffen werden kann. In Rust kannst du eine Struktur öffentlich machen und ihre Felder gleichzeitig privat halten.
Betrachte eine Wallet, die niemals einen negativen balance haben sollte. Wenn das field public ist, könnte jeder es auf einen beliebigen Wert setzen:
// wallet.rs
pub struct Wallet {
balance: f64, // Kein pub = privat!
}
impl Wallet {
pub fn new(initial: f64) -> Wallet {
Wallet { balance: initial }
}
}
Beachte, dass Wallet öffentlich ist, aber balance kein pub-Schlüsselwort besitzt. Das bedeutet, dass Code außerhalb des Moduls mithilfe des Konstruktors ein Wallet erstellen kann, aber das balance-Feld nicht direkt lesen oder ändern kann:
// main.rs
mod wallet;
use wallet::Wallet;
fn main() {
let w = Wallet::new(100.0);
// println!("{}", w.balance); // Fehler! Feld ist privat
// w.balance = -50.0; // Fehler! kann nicht auf privates Feld zugreifen
}
Das ist die Grundlage der Kapselung: Die Struktur kontrolliert ihre eigenen Daten. Externer Code muss die von dir bereitgestellten Methoden verwenden, wodurch du Validierung, Protokollierung oder jede andere Logik hinzufügen kannst. Die Daten bleiben vor ungültigen Zuständen geschützt.
In den kommenden Lektionen lernst du, wie du über Getter- und Setter-Methoden kontrollierten Zugriff auf diese privaten Daten gewährst.
Aufgabe
EinfachErstellen wir ein Temperatursensorsystem, das Kapselung veranschaulicht, indem es sensible Daten innerhalb eines Moduls schützt.
Du erstellst zwei Dateien, um deinen Code zu organisieren:
sensor.rs: Definiere eineTemperatureSensor-Struktur mit zwei Feldern:location(String) undreading(f64). Die Struktur sollte öffentlich sein, damit sie aus anderen Dateien verwendet werden kann, aber halte beide Felder privat: Externer Code sollte nicht direkt auf die Daten des Sensors zugreifen oder sie ändern können. Füge einen öffentlichennew-Konstruktor hinzu, der einen Standortnamen und einen anfänglichen Temperaturwert entgegennimmt.main.rs: Binde dein Sensormodul ein und erstelle mithilfe des Konstruktors einenTemperatureSensor. Da die Felder privat sind, kannst du sie nicht direkt ausgeben. Gib stattdessen eine Bestätigungsnachricht aus, die zeigt, dass der Sensor erfolgreich erstellt wurde.
Diese Herausforderung konzentriert sich auf das grundlegende Konzept privater Felder: Die Struktur ist zugänglich, aber ihre internen Daten sind geschützt. Externer Code kann nur über die von dir bereitgestellten Methoden mit dem Sensor interagieren (in diesem Fall nur über den Konstruktor).
Deine Ausgabe sollte genau diesem Format folgen:
Sensor created for: {location}Um den Standort für die Ausgabe abzurufen, musst du deine new-Funktion diese Nachricht vor der Rückgabe des Sensors ausgeben lassen, da das Feld selbst privat ist.
Wenn du beispielsweise einen Sensor mit dem Standort "Kitchen" und dem Messwert 22.5 erstellst, wäre die Ausgabe:
Sensor created for: KitchenDu erhältst zwei Eingaben: den Standortnamen und den anfänglichen Temperaturwert.
Probier es selbst
mod sensor;
use sensor::TemperatureSensor;
fn main() {
// Eingabe lesen
let mut location = String::new();
std::io::stdin().read_line(&mut location).expect("Failed to read line");
let location = location.trim().to_string();
let mut reading_str = String::new();
std::io::stdin().read_line(&mut reading_str).expect("Failed to read line");
let reading: f64 = reading_str.trim().parse().expect("Failed to parse reading");
// TODO: Erstelle einen TemperatureSensor mit dem Konstruktor
// Die new-Funktion übernimmt das Ausgeben der Bestätigungsnachricht
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
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Übe selbstständig: Online-Rust-Compiler