본문 바로가기
Personal-Study/Rust

[Rust 문서 읽기] 5. Using Structs to Structure Related Data (연관된 데이터들을 구조체로 다루기)

by Aaron-Kim 2023. 2. 5.

The Rust Programming Language

- Before Start

    - Struct: custom data type that lets you package together and name multiple related values
                     that make up a meaningful group

  - Structs and enums are the building blocks for creating new types in your program's domain
     to take full advantage of Rust's compile-time type checking

- Defining and Instantiation Structs

  - Like tuples, the pieces of a struct can be different types

  - Unlike tuples, we have to name each piece of data so it's more clear

  - inside curly brackets, we defind the names and types of the pieces of data called fields

  - not rely on the same order of data, just like a general template

  - if we mutate the instance, the entire instance must be mutable, not just only certain fields

  - Using the Field Init Shorthand

    - if function parameter name equals to struct field name, we can make it shorthand (only write once)

  - Creating Instances from Other Instances with Struct Update Syntax

    - The syntax .. specifies that the remaining fields not explicitly set should have the same value
       as the fields in the given instance

  - Using Tuple Structs Without Named Fields to Create Different Types

    - tuple structs

    - useful when we want to give the whole tuple a name
      and make the tuple a different type from other tuples,
      and when naming each field as in a regular struct would be verbose or redundant

  - Unit-Like Structs Without Any Fields

    - Unit-Like Structs: structs that don't have any fields

    - useful when we need to implement a trait on some type but don't have any data
       that we want to store in the type itself

  - Ownership of Struct Data

    - Lifetimes ensure that the data referenced by a struct is valid for as long as the struct is

 

fn main() {
    let mut user1 = User {
        active: true,
        username: String::from("someusername123"),
        email: String::from("someone@example.com"),
        sign_in_count: 1,
    };

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

    println!("{}", user1.username); // compile error
}

struct User {
    active: bool,
    username: String,
    email: String,
    sign_in_count: u64,
}

 

- An Example Program Using Structs

  - Refactoring with Tuples

  - Refactoring with Structs: Adding More Meaning

  - Adding Useful Functionality with Derived Traits

 

fn main() {
    // let width1 = 30;
    // let height1 = 50;

    // let rect1 = (30, 50);

    let rect1 = Rectangle {
        width: dbg!(30 * 2),
        height: 50,
    };

    println!(
        "The area of the rectangle is {} square pixels.",
        // area(width1, height1)

        // area(rect1)
        area(&rect1)
    );

    println!("rect1 is {:#?}", rect1);

    dbg!(&rect1); // if no reference, compile time error at the bottom code

    println!("rect1 is {:#?}", rect1);
}

// fn area(width: u32, height: u32) -> u32 {
//     width * height
// }

// fn area(dimensions: (u32, u32)) -> u32 {
//     dimensions.0 * dimensions.1
// }

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

fn area(rectangle: &Rectangle) -> u32 {
    rectangle.width * rectangle.height
}

 

- Method Syntax

  - Unlike functions, methods are defined within the context of a struct (or an enum or a trait object)

     and their first parameter is always self
     which represents the instance of the struct the method is being called on

  - Defining Methods

    - &self is actually for self: &Self

    - Having a method that takes ownership of the instance by using just Self as the first parameter is rare
      -> usually used when the method transforms self into something else
      and we want to prevent the caller from using the original instance after the transformations

    - Rust does not implement automatically for struct fields for getters.
      (method and field name are same)

    - Where's the -> Operator?

  - Methods with More Parameters

  - Associated Functions

    - not methods

    - often used for constructors that will return a new instance of the struct

  - Multiple impl Blocks

  - Summary

 

fn main() {
    // let rect1 = Rectangle {
    //     width: 30,
    //     height: 50,
    // };

    // println!(
    //     "The area of the rectangle is {} square pixels.",
    //     rect1.area()
    // );

    let rect1 = Rectangle {
        width: 30,
        height: 50,
    };

    let rect2 = Rectangle {
        width: 10,
        height: 40,
    };

    let rect3 = Rectangle {
        width: 60,
        height: 45,
    };

    println!("Can rect1 hold rect2? {}", rect1.can_hold(&rect2));
    println!("Can rect1 hold rect3? {}", rect1.can_hold(&rect3));

    let sq = Rectangle::square(3);

    dbg!(sq);
}

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

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

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

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

 


Using Structs to Structure Related Data (영어)

연관된 데이터들을 구조체로 다루기 (한국어)

반응형

댓글