STUDY/Rust

Rust - 7. 구조체, 열거형, 패턴 매칭

sinawi95 2024. 2. 17. 22:33
728x90

c,c++의 구조체(struct)랑 열거형(enum)과 많이 다른가? 튜플과 또 다른점은?


1. 구조체 Struct

정의 및 인스턴트화

서로 연관된 필드 및 데이터를 묶는 방법

struct struct_name {
    value_name: value_type,
    ...,
}
  • c/c++이랑 큰 차이없다. 구조체 선언 하고 구조체 내부 속성에 타입을 설정하면 된다. 값은 "."을 통해서 사용할수 있다.
  • 가변적으로 사용하려면 let mut st: struct_name; 이런식으로 선언하면 된다. 구조체 내 특정 값만 가변적으로 사용할순 없다.

필드 초기화 축약법

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

// 기존 인스턴스 이용
fn test() {
    let user1 = User {/* 생략 */}

    let user2 = User {
        email: String::from("another@example.com"),
        ..user1 // 나머지 값은 user1과 동일
    };
}
  • js 에서도 비슷하게 썼던거같다.
  • 아래 방식으로 기존 인스턴스를 이용해서 새롭게 만드는 경우 소유권이 이전될 수 있다.
    • &str 을 사용해서 소유권을 갖지 않는 데이터를 저장할수 있지만 lifetime을 지정해야 사용할수 있다.
    • -> 이후에 나오므로 어떻게 쓰는지 아직 잘 모른다.

구조체 여러가지 사용법

// tuple structs
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);

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

// unit-like structs
struct AlwaysEqual;

fn main() {
    let subject = AlwaysEqual;
}
  • 필드 이름을 적지 않고 쓰는 경우엔 tuple structs를 사용한다. 
    • RGB나 x,y,z 처럼 묶어서 사용할 때 유용하다. struct Color{R: i32, G:i32, B:i32}; 보다 struct Color(i32,i32,i32);가 좀더 사용하기 편할듯하다. black.R 과 같이 개별로는 못쓰긴 하겠지만 말이다.
  • 필드가 없는 유사 유닛 구조체는 어디에 쓰는지 잘 모르겠다.

 

2. 메서드

impl 이라는 키워드를 사용해서 구조체에서 사용할수 있는 메서드를 구현할수 있다.

  • rust엔 클래스가 없다..! 러스트는 객체지향언어가 아닌듯하다.

 

3. 열거형

enumerations, enums

어떤 값이 여러 개의 가능한 값의 집합 중의 하나

  • 말이 좀 어려운데 선택지를 묶어놓는다고 보면 된다.
fn main() {
    enum IpAddr {
        V4(String),
        V6(String),
    }

    let home = IpAddr::V4(String::from("127.0.0.1"));

    let loopback = IpAddr::V6(String::from("::1"));
}
  • enum 을 정의하고 실제도 인스턴스를 만들때 kind 안에 선택지를 골라서 작성하면된다.

열거형의 장점

  • 각 배리언트는 다른 타입과 다른 양의 연관된 데이터를 가질 수 있다 .데이터가 아예 없어도 되고 구조체가 들어가도된다.
    • 위의 코드에서 V4(u8,u8,u8,u8), V6(String) 으로 설정할 수 있다. 해당 enum 값을 선택할때 데이터만 잘 넣어서 사용하면된다.

Option 

enum Option<T> {
    None,
    Some(T),
}

값이 있거나 없을 수 있는 상황을 나타냄

  • Rust 에선 Option 가 아닌 타입이면 항상 값이 있다고 보장할수 있다.
  • Option<T> 을 일반 타입(T)으로 사용하려면 변환이 필요하다.
  • Swift의 Optional 생각하면 될 듯 하다. 
  • c나 파이썬에선 if (ptr == NULL) 이나 if data == None: 등을 사용해서 값이 없는 예외를 처리해야한다. (해당 변수가 NULL 값인지 항상 확인해야한다.)

 

4. 제어 흐름

match

#[derive(Debug)] // so we can inspect the state in a minute
enum UsState {
    Alabama,
    Alaska,
    // --생략--
}

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),
}
fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter(state) => {
            println!("State quarter from {:?}!", state);
            25
        }
    }
}
  • match는 c의 switch와 비슷하다.
enum Option<T> {
    None,
    Some(T),
}

fn check_option(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,
        Some(i) => {
            // do something
            Some(i+1);
        },
    }
}
  • 위의 Option<T> 도 match를 사용해서 처리할수 있다.

match 마지막에 변수 이름을 적으면 명시하지 않은 값을 처리할수있다.

  • switch의 default와 같다.
  • 변수이름을 _(underbar)로 사용하면 해당 변수를 사용하지 않는다는 뜻이다.

 

if let

하나의 패턴만 매칭시키고 나머지 패턴은 무시하는 방법

// match
fn main() {
    let x = 32;
    match x {
        32 => { println!("hello"); },
        _ => {},
    }
}

// if let
fn main() {
    let x = 32;
    if let 32 = x { println!("hello"); }
}
  • 위와 같이 딱 하나의 패턴만 쓰고 나머지는 필요없는 경우에 사용한다.
  • if let에 else 를 사용해서 match와 똑같이 만들수 있다.

 

생각해 볼 것

// if let 2
fn main() {
    let x = 32;
    if let x = 32 { println!("hello,{}",x); }
    if let x = 99 { println!("hello??,{}",x); }
    if let 32 = x { println!("hello????,{}",x); }
    if let 99 = x { println!("hello??????,{}",x); }
}

// output
hello,32
hello??,99
hello????,32
  • 실행 결과를 분석해보자
    • if let x = 32 { println!("hello,{}",x); }
      • hello, 32가 출력된다.
      • if 문에서 사용할 변수 x(바로 위에서 할당한 x 와 다름)에 32를 할당하고 println 문을 실행한다. 
    • if let x = 99 { println!("hello??,{}",x); }:
      • hello??, 99가 출력된다.
      • if 문에서 사용할 변수 x(위에서 할당한 x 와 다름)에 99를 할당하고 println 문을 실행한다.
    • if let 32 = x { println!("hello????,{}",x); }
      • hello????, 32가 출력된다.
      • if let 문에서 x가 32인지 확인한다. x ==32 이므로 println 문을 실행한다. 
    • if let 99 = x { println!("hello??????,{}",x); }
      • 출력되지 않는다.
      • if let 문에서 x가 99인지 확인한다. x == 32 이므로 println 문을 실행하지 않는다. 
  • 아래 글은 반박 가능한 패턴에 관련한 내용인데 읽어보면 좋을듯하다. 어려우니 나중에 다시 읽어 봐야지 ㅎㅎ

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

https://doc.rust-kr.org/ch06-00-enums.html