Victoree's Blog

[4] 구조체(Struct)와 열거형(Enum) 본문

Rust

[4] 구조체(Struct)와 열거형(Enum)

victoree 2023. 2. 23. 01:42
728x90

1. 구조체

구조체의 구성요소들은 각각 이름을 명명할 수 있고, 각자 다른 타입을 지닐 수 있음.
구조체의 인스턴스는 반드시 변경가능(mutable) 해야한다.

fn main() {
struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}

fn build_user(email: String, username: String) -> User {
    User {
        email: email,
        username: username,
        active: true,
        sign_in_count: 1,
    }
}

// 변수명과 구조체의 필드명이 같다면, 필드 초기화 축약법(field init shorthand) 을 이용할 수 있습니다
// User {
//        email,
//        username,
//        active: true,
//        sign_in_count: 1,
// }
}

// 구조체 갱신법을 기반으로 아래 방식으로 객체를 생성할 수 있음
let user2 = User {
    email: String::from("another@example.com"),
    username: String::from("anotherusername567"),
    ..user1
};

구조체 출력

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

let rect1 = Rectangle { length: 50, width: 30 };
println!("rect1 is {}", rect1);

구조체는 Display에 대한 기본 제공되는 구현체를 가지고 있지 않는다. 고로 println!("rect1 is {:?}", rect1);과 같이 :?를 이용해 인스턴스를 출력할 수 있다.
단, 러스트는 디버깅 정보를 출력하는 기능을 포함하고 있는 것이 맞지만, 우리 구조체에 대하여 해당 기능을 활성화하도록 명시적인 사전동의를 해주어야 한다. 그러기 위해서, 구조체 정의부분 바로 전에 #[derive(Debug)] 어노테이션을 추가함으로 이를 해결할 수 있다.

메소드 문법

메소드는 함수와 달리 구조체의 내용 안에 정의되며, (혹은 열거형이나 트레잇 객체안에 정의됨) 첫번째 파라미터가 언제나 self인데, 이는 메소드가 호출되고 있는 구조체의 인스턴스를 나타낸다.

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

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

메소드 리시버

메소드는 self의 소유권을 가져갈 수도, 여기서처럼 self를 변경 불가능하게 빌릴 수도, 혹은 다른 파라미터와 비슷하게 변경이 가능하도록 빌려올 수도 있다.

  • &self: 호출자로부터 공유가능한 불변 참조 방식으로 객체를 빌려옴을 나타냅니다. 객체는 메소드 호출 뒤에도 사용될 수 있습니다.
  • &mut self: 호출자로부터 유일한 가변 참조 방식으로 객체를 빌려옴을 나타냅니다. 객체는 메소드 호출 뒤에도 사용될 수 있습니다.
  • self: 호출자로부터 객체의 소유권을 가져오고 객체는 호출자로부터 메소드로 이동됩니다. 메소드가 객체를 소유하게 되며 따라서 명시적으로 소유권을 다른 곳으로 전달하지 않는다면 메서드 종료와 함께 객체는 drop(해제)됩니다.
  • mut self: 위와 동일하지만 메서드가 객체의 소유권을 가지면서 동시에 객체를 수정할 수도 있습니다. 소유권을 가지는 것이 수정할 수 있음을 의미하는 것은 아닙니다.
  • 리시버 없음(ex.생성자): 구조체의 정적 메서드가 됩니다. 주로 생성자를 만들때 사용하게 되며, 생성자는 흔히 new라고 이름붙입니다.

연관함수

impl 블록은 self 파라미터를 갖지 않는 함수도 impl 내에 정의하는 것이 허용된다. 해당 함수는 해당 구조체와 연관이 되어있기 때문에, 연관 함수라 불리우고 String::from 과 같이 ::문법을 이용하여 호출할 수 있다.

2. 열거형(Enum)

enum CoinFlip {
    Heads,
    Tails,
}

enum WebEvent {
    PageLoad,                 // Variant without payload
    KeyPress(char),           // Tuple struct variant
    Click { x: i64, y: i64 }, // Full struct variant
}

enum IpAddr {
    V4(String),
    V6(String),
}
let home = IpAddr::V4(String::from("127.0.0.1"));
// 열거형 variant에 직접 데이터를 붙임으로써 구조체를 사용할 필요가 없어짐.

enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(String),
}
// V4와 V6의 타입이 서로 다를 때 struct에서는 각각의 구조체를 작성해야 가능하지만, 열거형은 위와같이 해결가능함

variant에 데이터(payload)를 포함시키기도 하며, 각 variant에 담긴 데이터는 match문을 이용해 추출한다.

패턴 매칭

match 키워드는 값을 여러 형태의 패턴과 매치시킬 수 있다. C/C++의 switch와 비슷하게 값을 패턴으로 사용할 수 있다.
_ 패턴은 어떤 값과도 매칭되는 와일드카드이다.

match input {
        'q'                   => println!("Quitting"),
        'a' | 's' | 'w' | 'd' => println!("Moving around"),
        '0'..='9'             => println!("Number input"),
        _                     => println!("Something else"),
}
728x90
Comments