Rust - Basic - 05 - Struct

構造体

理解

  • 基本構文

    // 类型声明
    struct User {
        active: bool,
        username: String,
        email: String,
        sign_in_count: u64,
    }
    
    // 实例化
    let user1 = User {
        email: String::from("someone@example.com"),
        username: String::from("someusername123"),
        active: true,
        sign_in_count: 1,
    };
    
    // 访问&修改
    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 move 了,因为 user1.username (String 类型) 被 move 为 user2.username
    // 如果初始化 user2 的时候另外赋值 username,则 user1 不会被 move
    
  • メソッド

    struct の impl の function は method または associated function と呼びます。method と field は同名にできます

    複数の impl を同時に定義できます

    #[derive(Debug)]
    struct Rectangle {
        width: u32,
        height: u32,
    }
    
    impl Rectangle {
        fn area(&self) -> u32 {  // method
            self.width * self.height
        }
    		fn width(&self) -> bool {  // method 与 field 可以同名
            self.width > 0
        }
        fn square(size: u32) -> Rectangle {  // 不带 &self 也可以
            Rectangle {
                width: size,
                height: size,
            }
        }
    }
    
    // 可同时存在多个 impl
    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);
        }
    }
    

参照元

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

…

Creating Instances From Other Instances With Struct Update Syntax

古いインスタンスの値の大部分を使いながら一部を変した新しい struct インスタンスを作ると便利なことがあります。struct update syntax を使えば実現できます。

まず、リスト 5-6 では update syntax を使わず、user1 から user2 に新しい User インスタンスを作ります。email だけ新しい値にし、それ以はリスト 5-2 で作った user1 と同じ値を使います。


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

リスト 5-6: user1 の値を使った新しい User インスタンスの作成

struct update syntax を使えば、リスト 5-7 のように、より少ないコードで同じ結果を得られます。.. は、明示的に設定していない残りのフィールドに、指定したインスタンスと同じ値を使うことを示します。


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

リスト 5-7: struct update syntax で User の新しい email 値を設定し、残りの値に user1 を使う

リスト 5-7 のコードは、email だけ異なり、username、active、sign_in_count は user1 と同じ値を持つ user2 を作ります。残りのフィールドを user1 から取得するため、..user1 は最後に置く必要があります。ただし、それ以のフィールドは struct 定義の順序に関係なく、任意の順序で好きな数だけ指定できます。

struct update syntax はデータを move するため、= による代入に似ています。これは 「変数とデータの相互作: Move」 で見たとおりです。この例では、user1 の username フィールドの String が user2 に move されるため、user2 作成後に user1 は使えません。email と username の両方に新しい String 値を user2 へ渡し、user1 から active と sign_in_count だけを使えば、user2 作成後も user1 は有効です。active と sign_in_count は Copy trait を実装する型なので、 「スタックだけに保存されるデータ: Copy」 で説明した動作になります。