본문 바로가기

Rust

Rust 구조체

[1. 개요]

연관된 여러 값들을 묶어서 의미있는 데이터 단위로 정의한다.

연관함수 정의 방법을 정리한다.

 

 

[2. 구조체 정의 및 생성]

struct User {
    sign_in_count: u64,
    active: bool,
}

fn main() {
    let user1 = User {
        active: true,
        sign_in_count: 1,
    };

    let user2 = user1;
    println!("{}", user2.sign_in_count);
    println!("{}", user1.sign_in_count); // 구조체 user1 은 Copy trait 을 구현하지 않아서
                                         // 소유권이 이동하여, 컴파일에러가 발생한다.
}

구조체는 mut 를 통해 멤버 변경이 가능하나,

특정 필드만 변경할 수 있도록 허용하진 않는다.

 

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

fn build_user(email: String, username: String) -> User {
    User {
        email, // 구조체 필드와 변수 이름이 같은 경우, 필드 생략이 가능하다.
        username,
        active: true,
        sign_in_count: 1,
    }
}

위와 같이 인스턴스 생성 시 구조체 필드와 변수 이름이 같은 경우,

필드 생략이 가능하다.

 

 

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

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

    let user2 = User {
        email: user1.email,
        username: String::from("anotherusername567"),
        active: user1.active,
        sign_in_count: user1.sign_in_count,
    };
    
    let user3 = User {
        email: String::from("another@example.com"),
        username: String::from("anotherusername789"),
        ..user2 // 구조체 갱신법
    };

    println!{"{}", user1.email}; // email 은 string type 이므로, 값이 이동되어 컴파일 에러가 발생한다.
    println!("{}, {}, {}", user1.username, user2.username, user3.username); // 값을 복사하여, 소유권 문제가 없다.
}

구조체간의 대입연산은 소유권 문제가 있으나,

구조체 멤버 간 대입은 스택에 저장되는 데이터는 소유권 문제 없이 복사된다.

 

구조체 갱신법은 입력으로 주어진 인스턴스와 변화하지 않는 필드들을 명시적으로 할당하지 않기 위해

.. 구문을 사용한다.

=> 일일히 다 입력하지 않아도 된다.

=> comma 는 없어야 한다.

=> 변하는 값을 먼저 작성하고 사용해야 한다.

 

 

[3. 튜플 구조체]

struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

let black = Color(0, 0, 0);
let origin = Point(0, 0, 0);

튜플의 타입은 같지만, 구조체 명이 다르므로,

black 과 origin 은 서로 다른 타입이다.

=> 두 변수 간 소유권을 이동 할 수 없다.

 

 

[4. 유사 유닛 구조체]

어떤 필드도 없는 구조체 정의가 가능하다.

이는 유닛 타입인 () 와 비슷하게 동작하여, 유사 유닛 구조체라 부른다.

이 구조체는 특정한 타입의 trait 을 구현해야하지만,

타입 자체에 데이터를 저장하지 않는 경우에 유용하다.

 

 

[5. 데이터 소유권]

struct User {
    username: &'static str, // 참조자의 Lifetime 을 명시하지 않으면 컴파일 에러가 발생한다.
    email: &'static str,
    sign_in_count: u64,
    active: bool,
}

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

    println!("{}, {}", user1.username, user1.email);
}

 

 

[6. 파생 트레잇]

println! 매크로는 다양한 종류의 포맷을 출력할 수 있으며, 

기본적으로 {}은 println!에게 Display라고 알려진 포맷팅을 이용하라고 전달한다.

 

그러나 구조체를 사용하는 경우, println!이 출력을 형식화하는 방법은 덜 명확하여, 컴파일 에러가 발생한다.

 

그래서 {} 내 :?  (또는 {:#?} ) 를 삽입하여, println!  에게 Debug 라는 출력 포맷을 사용할 것을 명시한다.

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

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

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

Rust 는 derive 어노테이션을 이용한 여러 트렛잇을 제공한다.

 

 

[7. 메소드 정의]

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

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

    fn update(&mut self) {
        self.length += 1;
        self.width += 1;
    }

    fn shrink(self) -> u32 {
        self.width + self.length
    }
}

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

    rect1.update(); // rect1 이 mut 가 아니므로, 컴파일 에러가 발생한다.

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

    rect1.shrink(); // 호출 시 rect1 의 소유권이 이동하므로, 아래 코드의 컴파일 에러가 발생한다.

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

 

Rust 는 C++ 에서의 -> 연산자와 동치인 연산자를 갖지 않으며,

자동 참조 및 역참조 라는 기능을 갖고 있다.

 

이는 어떤 object의 메소드를 호출 했을 때,

Rust 가 자동으로 & 나 &mut 혹은 * 를 붙여서 object 가 해당 메소드의 시그니처와 맞도록 한다.

그리고 이것은 메소드가 명확한 수신자, self 의 type 을 갖고 있기 때문에 동작한다.

 

 

[8. 연관 함수]

impl 블록 내 self 파라미터를 갖지 않는 함수

=> (C++ 기준, class 내 static 와 비슷?)

 

연관 함수는 새로운 구조체의 인스턴스를 반환해주는 생성자로서 자주 사용된다.

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

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

let sq = Rectanble::square(3);

 

'Rust' 카테고리의 다른 글

Rust mod, pub, use  (0) 2022.08.24
Rust 열거형과 패턴 매칭  (0) 2022.08.20
슬라이스  (0) 2022.08.18
참조자와 빌림  (0) 2022.08.18
소유권  (0) 2022.08.17