상세 컨텐츠

본문 제목

러스트 중급 1

Rust 예시

by 러스트코리아 2025. 9. 9. 22:28

본문

반응형

 

제네릭은 위에서 설명한 것처럼 타입을 추상화하여 코드 재사용성 높이기 위한 것입니다.

 

제네릭은 타입을 일반화(generalize)하여 동일한 로직을 여러 타입에 재사용할 수 있게 해주는 기능입니다.
Rust에서 제네릭은 컴파일 타임에 모노모피제이션(monomorphization) 을 통해 구체적인 타입으로 치환되어 런타임 오버헤드 없이 최적화됩니다.

C++의 템플릿, Java/C#의 제네릭과 유사하지만, Rust는 zero-cost abstraction을 지향 → 성능 손실 없음. 

 

1. 함수 제네릭

fn largest<T: PartialOrd>(list: &[T]) -> &T {
    let mut largest = &list[0];
    for item in list {
        if item > largest {
            largest = item;
        }
    }
    largest
}

fn main() {
    let numbers = vec![34, 50, 25, 100, 65];
    let result = largest(&numbers);
    println!("The largest number is {}", result);

    let chars = vec!['y', 'm', 'a', 'q'];
    let result = largest(&chars);
    println!("The largest char is {}", result);
}

결과는 직접 확인해보세요...

 

2.구조체 제네릭

#[derive(Debug)]
struct Point<T> {
    x: T,
    y: T,
}

impl<T> Point<T> {
    fn new(x: T, y: T) -> Self {
        Point { x, y }
    }
}

fn main() {
    let integer = Point::new(5, 10);
    let float = Point::new(1.0, 4.0);
    println!("{:?}", integer); // Point { x: 5, y: 10 }
    println!("{:?}", float);   // Point { x: 1.0, y: 4.0 }
}

결과는 직접 확인해보세요...

 

3. 열거형 제네릭

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

enum Result<T, E> {
    Ok(T),
    Err(E),
}

 

// Option<T>와 Result<T, E>는 이미 Rust 표준 라이브러리에 정의되어 있음
// → 직접 재정의하지 않고, std의 것을 사용 (재정의하면 충돌!)

fn main() {
    // ============ Option<T> 예제 ============
    println!("=== Option<T> Examples ===");

    let some_number: Option<i32> = Some(42);
    let some_string: Option<String> = Some("Hello, Rust!".to_string());
    let none_value: Option<f64> = None;

    println!("some_number: {:?}", some_number);   // Some(42)
    println!("some_string: {:?}", some_string);   // Some("Hello, Rust!")
    println!("none_value: {:?}", none_value);     // None

    // match로 분기 처리
    match some_number {
        Some(x) => println!("Got a number: {}", x),
        None => println!("Got nothing!"),
    }

    match none_value {
        Some(x) => println!("Got a float: {}", x),
        None => println!("Got nothing! (as expected)"),
    }

    // ============ Result<T, E> 예제 ============
    println!("\n=== Result<T, E> Examples ===");

    let ok_result: Result<i32, &str> = Ok(200);
    let err_result: Result<String, i32> = Err(404);
    let another_ok: Result<&str, String> = Ok("Success!");

    println!("ok_result: {:?}", ok_result);       // Ok(200)
    println!("err_result: {:?}", err_result);     // Err(404)
    println!("another_ok: {:?}", another_ok);     // Ok("Success!")

    // match로 분기 처리
    match ok_result {
        Ok(value) => println!("Operation succeeded with value: {}", value),
        Err(e) => println!("Operation failed with error: {}", e),
    }

    match err_result {
        Ok(value) => println!("Got value: {}", value),
        Err(code) => println!("Error code: {}", code),
    }

    // ============ 실제 함수에서 사용 예시 ============
    println!("\n=== Practical Usage ===");

    let result1 = divide(10, 2);
    let result2 = divide(10, 0);

    println!("10 / 2 = {:?}", result1); // Ok(5)
    println!("10 / 0 = {:?}", result2); // Err("Division by zero")

    match result1 {
        Ok(val) => println!("Division result: {}", val),
        Err(e) => println!("Error: {}", e),
    }

    match result2 {
        Ok(val) => println!("Division result: {}", val),
        Err(e) => println!("Error: {}", e),
    }
}

// 예제 함수: Result<T, E> 반환
fn divide(a: i32, b: i32) -> Result<i32, &'static str> {
    if b == 0 {
        Err("Division by zero")
    } else {
        Ok(a / b)
    }
}

결과는 직접 확인해보세요... 코드를 하나씩 따라가보세요...

 

4. 메서드 제네릭

struct Point<T, U> {
    x: T,
    y: U,
}

impl<T, U> Point<T, U> {
    fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
        Point {
            x: self.x,
            y: other.y,
        }
    }
}

fn main() {
    let p1 = Point { x: 5, y: 10.4 };
    let p2 = Point { x: "Hello", y: 'c' };
    let p3 = p1.mixup(p2);
    println!("p3.x = {}, p3.y = {}", p3.x, p3.y); // 5, 'c'
}

결과는 직접 확인해보세요... 코드를 하나씩 따라가보세요...

 

 5. 제네릭 사용 시 주의점
트레이트 바운드 누락 → 컴파일 에러
너무 많은 타입 인스턴스 → 바이너리 크기 증가
라이프타임과의 조합 → 복잡도 증가
where 절을 사용해 가독성 향상

 

6. 메모리 확인

use std::mem;

fn main() {
    println!("Option<i32>: {} bytes", mem::size_of::<Option<i32>>());
    println!("Option<bool>: {} bytes", mem::size_of::<Option<bool>>());
    println!("Option<String>: {} bytes", mem::size_of::<Option<String>>());
}

메모리 타입별 바이트 수 확인

 

공식 문서 및 참고 자료

 

Glossary - The Rust Reference

An ‘abstract syntax tree’, or ‘AST’, is an intermediate representation of the structure of the program when the compiler is compiling it. The alignment of a value specifies what addresses values are preferred to start at. Always a power of two. Ref

doc.rust-lang.org

 

모노모피제이션(monomorphization)

  • Mono- (단일) + -morph (형태) + -ization (과정) → “여러 형태를 단일 형태로 만드는 과정
  • 제네릭이라는 ‘일반화된 형태’를, 사용되는 구체적인 타입으로 치환하여 ‘단형화(하나의형태)’하는 과정

상세한 내용은 참고자료를 보시면 좋습니다.

어렵습니다. 중급부터는 용어도 그렇고 메모리에 어떻게 보여지는 지 그리고 어떤 타입을 해야 등등 개념을 잘 익히시길 바랍니다.

반응형

'Rust 예시' 카테고리의 다른 글

러스트 중급 2  (0) 2025.09.16
러스트 기초편 8  (0) 2025.09.08
러스트 기초편 7  (0) 2025.09.01
러스트 기초편 5  (4) 2025.08.30
러스트 기초편 4  (0) 2025.08.30

관련글 더보기