Rust

[Rust] Struct

llHoYall 2022. 8. 4. 23:24

A struct is a custom data type that lets you package together and name multiple related values that make up a meaningful group.

Create Struct

We define a struct using the struct keyword.

And inside curly brackets, we define the names and types of fields.

struct Person {
    name: String,
    age: u8,
}

Instantiate Struct

We create an instance of that struct by specifying concrete values for each of the fields to use the struct.

let person1 = Person {
    name: String::from("HoYa"),
    age: 18,
};

We can use dot notation to access fields of structs.

If the instance is mutable, we can change a value.

let mut person1 = Person {
    name: String::from("HoYa"),
    age: 18,
};
person1.name = String::from("Kim");

If the parameter names and the struct field names are the same, we can use the field init shorthand syntax.

fn create_person(name: String, age: u8) -> Person {
    Person { name, age }
}
let person1 = create_person(String::from("HoYa"), 18);

Using struct update syntax, we can create a new instance from an existing instance.

The .. operator must come last to specify that any remaining fields should get their values from the corresponding fields in a given instance.

let person2 = Person {
    name: String::from("Park"),
    ..person1
};

Tuple Struct without Named Fields

To define a tuple struct, start with the struct keyword and the struct name followed by the types in the tuple.

struct Color(i32, i32, i32);

fn main() {
    let black = Color(0, 0, 0);
}

Unit-LIke Struct without Any Fields

The unit-like struct doesn't have any fields.

It behaves similarly to ().

struct Nothing;

fn main() {
    let test = Nothing;
}

Define Method

Methods are defined within impl block and their first parameter is always self.

struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

fn main() {
    let rect1 = Rectangle {
        width: 3,
        height: 5,
    };

    println!("{}", rect1.area());
}

Method with Parameters

Methods can have more parameters after self.

They can be inside the same impl block or separated impl block.

impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }

    fn is_big(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}

// --- OR --- //
// impl Rectangle {
//     fn area(&self) -> u32 {
//         self.width * self.height
//     }
// }
//
// impl Rectangle {
//     fn is_big(&self, other: &Rectangle) -> bool {
//         self.width > other.width && self.height > other.height
//     }
// }

fn main() {
    let rect1 = Rectangle {
        width: 3,
        height: 5,
    };
    let rect2 = Rectangle {
        width: 1,
        height: 2,
    };

    println!("{}", rect1.is_big(&rect2));
}

Associated Functions

All functions defined within an impl block are called associated functions because they're associated with the type named after the impl.

They don't have a self parameter.

Associated functions that aren't methods are often used for constructors that will return a new instance of the struct.

impl Rectangle {
    fn square(size: u32) -> Rectangle {
        Rectangle {
            width: size,
            height: size,
        }
    }
}

fn main() {
    let rect1 = Rectangle::square(5);

    println!("{}, {}", rect1.width, rect1.height);
}