『TRPL Ch 5. Using Structs to Structure Related Data』

  • C의 구조체와 비슷하다.
    struct User {
        username: String,
        email: String,
        sign_in_count: u64,
        active: bool,
    }
    
  • User struct의 인스턴스를 만들 수 있다.
    let user1 = User {
        email: String::from("someone@example.com"),
        username: String::from("someusername123"),
        active: true,
        sign_in_count: 1,
    };
    
  • 인스턴스가 가변적이라면 속성에 dot notation으로 접근해 값을 바꿀 수 있다.
  • 이름 없는 필드로 이뤄진 tuple struct를 만들 수 있다.
    struct Color(i32, i32, i32);
    struct Point(i32, i32, i32);
    
    let black = Color(0, 0, 0);
    let origin = Point(0, 0, 0);
    

Derived traits

  • struct를 그대로 출력하려고 하면 오류가 발생한다.
    • 일단 포맷 스트링은 {:?} 또는 {:#?}를 사용해야 한다.
    • 그리고 std::fmd::Debug를 implement해야 한다. 러스트는 디버깅 정보를 출력하도록 지원하기 때문이다.
    • 실제로 구현하지 않고 struct 정의 위에 annotation #[derive(Debug)]를 적어주면 된다.
    #[derive(Debug)]
    struct Rectangle {
        width: u32,
        height: u32,
    }
    
    fn main() {
        let rect1 = Rectangle { width: 30, height: 50 };
        println!("rect1 is {:?}", rect1); //  rect1 is Rectangle { width: 30, height: 50 }
    }
    

Method

  • struct는 메소드를 가질 수 있으며, impl 키워드를 사용해 정의한다.
    impl Rectangle {
        fn area(&self) -> u32 {
            self.width * self.height
        }
    }
    
    • 호출할 때는 rect1.area()와 같이 한다.
    • 메소드가 인스턴스 자기 자신을 사용하고 싶으면 &self를 파라미터로 사용한다. 호출부에서는 무시된다.
  • 한 struct에 대한 impl 블록이 여러개일 수 있다.

Enums and Pattern Matching

  • enum 키워드로 enum을 정의할 수 있다.
    enum Color {
        red,
        blue,
    }
    
  • ::으로 접근한다: let red = Color::red
  • 타입을 지정할 수도 있다: enum Color { red(String), blue(String) }
  • 할당: let red = Color::red(String::from("red"))

Option Enum

  • 표준 라이브러리에 Option 타입이 있다.
  • 러스트에는 Null이 없다. null 값의 문제는 null 값을 not-null 값으로 사용하려 할 때 발생한다. 이는 에러를 일으키기 쉽다
  • 하지만 null은 유용한 점이 있는 것이 사실이다. 그래서 러스트는 null 대신 Option<T>라는 enum을 사용하기로 했다.
    enum Option<T> {
        Some(T),
        None,
    }
    
    • SomeNoneOption:: 없이 직접 사용할 수 있다.
    • <T> 문법은 제네릭 타입 파라미터다.
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;
  • Some은 어떤 값이든 담을 수 있다.
  • None을 할당할 때는 변수의 Option<T> 타입을 명시해야 한다.
    • None은 사실상 null과 같다.
    • null보다 나은 점은 Option<T>T가 다른 타입이라는 점이다. i8 타입과 Option<i8> 타입을 연산하면 에러가 발생한다.

The match operator

  • 러스트에는 match라는 강력한 연산자가 있다. match는 패턴에 따라 값을 비교해 일치하는 것을 실행한다.
    enum Coin {
        Penny,
        Nickel,
        Dime,
        Quarter,
    }
    
    fn value_in_cents(coin: Coin) -> u8 {
        match coin {
            Coin::Penny => 1,
            Coin::Nickel => 5,
            Coin::Dime => 10,
            Coin::Quarter => 25,
        }
    }
    
    • if로도 할 수 있지만, if는 불리언 값을 반환하는 반면 위 코드는 coin 타입을 반환한다.
    • match arm은 패턴과 실행할 코드 두 개의 부분으로 나뉜다. Coin::Penny는 패턴이다. =>는 패턴과 코드를 분리하는 연산자다. 뒤이어 나오는 1은 코드다.
    • 코드 블록을 쓸 수도 있다.
    Coin::Penny => {
        println!("Lucky penny!");
        1
    },
    

Matching with Option<T>

  • None을 매칭해 필터링할 수 있다.
fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,
        Some(i) => Some(i + 1),
    }
}
  • 만약 matchOption<T> 타입을 비교한다면 반드시 None도 비교해야 한다.
  • match에는 _ placeholder를 사용할 수 있다. switch문의 default와 같다.
    let some_value = 0u8;
    match some_value {
        1 => println!("one"),
        2 => println!("two"),
        _ => (),
    }
    

if let

  • iflet을 합친 문법이다.
  • match를 이렇게 쓰는걸:
let some_u8_value = Some(0u8);
match some_u8_value {
    Some(3) => println!("three"),
    _ => (),
}
  • 이렇게 줄일 수 있다:
if let Some(3) = some_u8_value {
    println!("three");
}

이 문서를 인용한 문서