Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

데이터 구조와 컬렉션: 효율적인 설계 기법

학습 목표: Rust의 튜플, 배열, 슬라이스, 벡터를 C#의 대응 개념과 비교하며 익힙니다. 특히 C# 클래스와 Rust 구조체의 메모리 배치 차이를 이해하고, 컴파일 타임에 비즈니스 규칙을 강제하는 뉴타입(Newtype) 패턴을 마스터합니다.


1. 튜플과 구조 분해 (Tuples & Destructuring)

C#의 ValueTuple처럼 Rust의 튜플도 여러 타입의 값을 하나로 묶는 가벼운 방법입니다.

  • C#: (int age, string name) = (30, "Alice"); (항상 가변적)
  • Rust: let (age, name) = (30, "Alice"); (기본 불변, 구조 분해로 즉시 사용 가능)
#![allow(unused)]
fn main() {
// [튜플 활용 예시]
fn get_coordinates() -> (i32, i32) { (10, 20) }

let (x, y) = get_coordinates(); // 구조 분해
println!("x: {}, y: {}", x, y);

let result = get_coordinates();
println!("x: {}", result.0); // 인덱스로 접근 가능
}

2. 뉴타입(Newtype) 패턴: 제로 비용 타입 안전성

C#에서 string emailstring address를 실수로 섞어 쓰는 것을 방지하려면 런타임 검사가 필요하지만, Rust는 이를 타입 시스템 차원에서 해결합니다.

#![allow(unused)]
fn main() {
struct Email(String);
struct UserId(u64);

fn send_welcome(id: UserId, email: Email) { /* ... */ }

// Email과 UserId는 컴파일 타임에 엄격히 구분됩니다.
// 하지만 런타임에는 그냥 String과 u64일 뿐이므로 오버헤드가 '0'입니다.
}

3. 배열, 벡터, 슬라이스

데이터의 수명을 누가 관리하느냐에 따라 세 가지로 나뉩니다.

종류C# 대응 개념메모리 위치특징
배열 ([T; N])T[] (고정)스택(Stack)컴파일 타임에 크기가 정해져야 함
벡터 (Vec<T>)List<T>힙(Heap)실행 중 크기 확장 가능, 소유권 가짐
슬라이스 (&[T])Span<T>빌려옴기존 데이터의 일부를 바라보는 뷰(View)

4. 구조체(Struct) vs 클래스(Class)

가장 근본적인 차이는 메모리 레이아웃입니다.

  • C# 클래스: 항상 힙에 존재하며, 객체 헤더와 가상 함수 테이블(VTable) 포인터를 가집니다. (항상 참조 타입)
  • Rust 구조체: 기본적으로 스택에 존재하며, 오직 필드 데이터만 가집니다. 헤더 오버헤드가 없습니다.
#![allow(unused)]
fn main() {
struct Person {
    name: String,
    age: u32,
}

impl Person {
    // 팩토리 메서드 (정적 메서드와 유사)
    fn new(name: &str, age: u32) -> Self {
        Self { name: name.to_string(), age }
    }

    // 인스턴스 메서드 (self를 받음)
    fn celebrate_birthday(&mut self) {
        self.age += 1;
    }
}
}

💡 실무 팁: &[T]를 매개변수로 쓰세요

함수에서 데이터를 읽기만 한다면 &Vec<T> 대신 &[T](슬라이스)를 받으세요. 이렇게 선언하면 Vec 뿐만 아니라 일반 배열, 심지어 벡터의 일부분까지도 모두 인자로 받을 수 있어 유연성이 극대화됩니다.