Rust - Grundlagen - 05 - Structs

Structs

Verständnis

  • Grundlegende Syntax

    // Typdeklaration
    struct User {
        active: bool,
        username: String,
        email: String,
        sign_in_count: u64,
    }
    
    // Instanziierung
    let user1 = User {
        email: String::from("someone@example.com"),
        username: String::from("someusername123"),
        active: true,
        sign_in_count: 1,
    };
    
    // Zugriff und Änderung
    let mut user1 = User {
        email: String::from("someone@example.com"),
        username: String::from("someusername123"),
        active: true,
        sign_in_count: 1,
    };
    user1.email = String::from("anotheremail@example.com");
    
    // field init shorthand
    fn build_user(email: String, username: String) -> User {
        User {
            email,
            username,
            active: true,
            sign_in_count: 1,
        }
    }
    
    // struct update
    let user2 = User {
        email: String::from("another@example.com"),
        ..user1
    }
    // user1 wurde verschoben, weil user1.username (Typ String) zu user2.username verschoben wurde.
    // Wenn username bei der Initialisierung von user2 ebenfalls gesetzt wird, wird user1 nicht verschoben.
    
  • Methoden

    Eine Funktion innerhalb eines impl-Blocks heißt Methode oder zugehörige Funktion; Methode und Feld dürfen denselben Namen haben.

    Mehrere impl-Blöcke können gleichzeitig existieren.

    #[derive(Debug)]
    struct Rectangle {
        width: u32,
        height: u32,
    }
    
    impl Rectangle {
        fn area(&self) -> u32 {  // Methode
            self.width * self.height
        }
    		fn width(&self) -> bool {  // Methode und Feld dürfen denselben Namen haben
            self.width > 0
        }
        fn square(size: u32) -> Rectangle {  // auch ohne &self möglich
            Rectangle {
                width: size,
                height: size,
            }
        }
    }
    
    // Mehrere impl-Blöcke können gleichzeitig existieren
    impl Rectangle {
        fn can_hold(&self, other: &Rectangle) -> bool {
            self.width > other.width && self.height > other.height
        }
    }
    
    fn main() {
        let rect1 = Rectangle {
            width: 30,
            height: 50,
        };
    
        println!(
            "The area of the rectangle is {} square pixels.",
            rect1.area()
        );
    
    		if rect1.width() {
            println!("The rectangle has a nonzero width; it is {}", rect1.width);
        }
    }
    

Quelle

https://doc.rust-lang.org/book/ch05-00-structs.html

…

Instanzen aus anderen Instanzen mit Struct-Update-Syntax erzeugen

Oft ist es nützlich, eine neue Struct-Instanz zu erzeugen, die die meisten Werte einer alten Instanz übernimmt, aber einige ändert. Dafür kann man die Struct-Update-Syntax verwenden.

Zunächst zeigt Listing 5-6, wie wir ohne Update-Syntax eine neue User-Instanz in user2 erzeugen. Wir setzen einen neuen Wert für email, verwenden ansonsten aber dieselben Werte aus user1, die wir in Listing 5-2 erstellt haben.


    let user2 = User {
        active: user1.active,
        username: user1.username,
        email: String::from("another@example.com"),
        sign_in_count: user1.sign_in_count,
    };

Listing 5-6: Eine neue User-Instanz unter Verwendung eines Werts aus user1 erzeugen

Mit der Struct-Update-Syntax erreichen wir dasselbe mit weniger Code, wie Listing 5-7 zeigt. Die Syntax .. legt fest, dass die übrigen nicht ausdrücklich gesetzten Felder denselben Wert wie die Felder der angegebenen Instanz erhalten.


    let user2 = User {
        email: String::from("another@example.com"),
        ..user1
    };

Listing 5-7: Mit der Struct-Update-Syntax einen neuen email-Wert setzen und die übrigen Felder von user1 übernehmen

Der Code in Listing 5-7 erzeugt in user2 eine Instanz mit einem anderen email-Wert, aber denselben Werten für die Felder username, active und sign_in_count wie user1. ..user1 muss am Ende stehen, damit die übrigen Felder ihre Werte aus den entsprechenden Feldern von user1 erhalten. Unabhängig von der Reihenfolge der Felder in der Struct-Definition können wir jedoch beliebig viele Felder in beliebiger Reihenfolge setzen.

Beachte, dass die Struct-Update-Syntax wie eine Zuweisung mit = funktioniert, weil sie die Daten verschiebt, wie im Abschnitt „Wie Variablen und Daten interagieren: Move“ beschrieben. In diesem Beispiel kann user1 nach der Erzeugung von user2 nicht mehr verwendet werden, weil der String im Feld username von user1 nach user2 verschoben wurde. Wenn wir für user2 neue String-Werte für email und username angegeben und damit nur die Werte active und sign_in_count aus user1 verwendet hätten, wäre user1 danach weiterhin gültig. Die Typen von active und sign_in_count implementieren den Copy-Trait; daher gilt das im Abschnitt „Daten nur auf dem Stack: Copy“ beschriebene Verhalten.