조용한 담장

Rust Common Programming Concepts - Quick Reference 본문

Rust

Rust Common Programming Concepts - Quick Reference

iosroid 2025. 12. 22. 17:33

Rust Common Programming Concepts - Quick Reference

공부한 내용을 빠르게 떠올리기 위한 코드 중심 참조 문서


목차


3.1 Variables and Mutability

핵심 개념

  • 기본값: 불변(immutable)
  • 가변 변수는 mut 키워드 필요
  • 섀도잉(shadowing)으로 타입 변경 가능
  • 상수는 const 키워드 사용

불변 변수 (기본)

fn test_immutable() {
    let x = 5;
    println!("x = {}", x);
    // x = 6;  // error[E0384]: cannot assign twice to immutable variable
}

가변 변수

fn test_mutable() {
    let mut x = 5;
    println!("Before: {}", x);
    x = 6;  // OK
    println!("After: {}", x);

    // 출력:
    // Before: 5
    // After: 6
}

섀도잉 (Shadowing)

fn test_shadowing() {
    // 1. 같은 타입 섀도잉
    let x = 5;
    let x = x + 1;  // 6
    let x = x * 2;  // 12
    println!("x = {}", x);  // 12

    // 2. 다른 타입으로 섀도잉 (타입 변환)
    let spaces = "   ";
    let spaces = spaces.len();  // &str -> usize
    println!("spaces = {}", spaces);  // 3

    // 3. mut는 타입 변경 불가
    let mut count = "   ";
    // count = count.len();  // 에러! 타입이 다름
}

섀도잉 스코프

fn test_shadowing_scope() {
    let x = 5;

    {
        let x = x * 2;  // 내부 스코프에서만 유효
        println!("Inner: x = {}", x);  // 10
    }

    println!("Outer: x = {}", x);  // 5
}

상수 (Constants)

// 전역 상수 - 타입 명시 필수
const MAX_POINTS: u32 = 100_000;
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;

fn test_constants() {
    // 함수 내부 상수
    const THRESHOLD: f64 = 0.5;

    println!("Max points: {}", MAX_POINTS);
    println!("Threshold: {}", THRESHOLD);

    // const는 mut 불가
    // const mut VALUE: i32 = 10;  // 에러
}

실전 팁

mut vs shadowing 선택 기준:

fn practical_examples() {
    // mut 사용: 같은 타입의 값을 계속 변경
    let mut counter = 0;
    for _ in 0..10 {
        counter += 1;
    }

    // shadowing 사용: 타입 변환 파이프라인
    let input = "  42  ";
    let input = input.trim();           // &str
    let input = input.parse::<i32>();   // Result<i32, _>
    let input = input.unwrap();         // i32
    println!("Parsed: {}", input);

    // shadowing 사용: 중간 계산 결과 저장
    let width = 10;
    let width = width * 2;      // 단위 변환
    let width = width + 5;      // 여백 추가
}

성능 고려사항:

fn performance_consideration() {
    // 큰 데이터 구조는 mut가 효율적
    let mut large_vec = vec![0; 1_000_000];
    for i in 0..large_vec.len() {
        large_vec[i] = i;  // in-place 수정
    }

    // 작은 데이터는 불변 + 함수형 스타일도 OK
    let small_vec = vec![1, 2, 3];
    let doubled = small_vec.iter().map(|x| x * 2).collect::<Vec<_>>();
}

3.1 Variables and Mutability - 테스트 코드(Rust Playground)
Gist


3.2 Data Types

핵심 개념

  • 정적 타입 언어 - 컴파일 시 모든 타입 결정
  • 타입 추론 지원 (대부분 명시 불필요)
  • 스칼라 타입: 정수, 부동소수점, 불리언, 문자
  • 복합 타입: 튜플, 배열

정수 타입 (Integer Types)

fn test_integer_types() {
    // 부호 있음: i8, i16, i32(기본), i64, i128, isize
    let a: i8 = -128;       // -128 ~ 127
    let b: i32 = 42;        // 기본 타입
    let c: i64 = 1_000_000; // 언더스코어로 가독성

    // 부호 없음: u8, u16, u32, u64, u128, usize
    let d: u8 = 255;        // 0 ~ 255
    let e: u32 = 100_000;

    // 아키텍처 의존 타입
    let f: isize = 100;     // 32bit: i32, 64bit: i64
    let g: usize = 100;     // 배열 인덱스 등에 사용

    // 리터럴 표기법
    let decimal = 98_222;
    let hex = 0xff;             // 255
    let octal = 0o77;           // 63
    let binary = 0b1111_0000;   // 240
    let byte = b'A';            // u8만 가능, 65

    println!("hex: {}, octal: {}, binary: {}, byte: {}", 
             hex, octal, binary, byte);
}

정수 오버플로우 처리

fn test_overflow() {
    let mut x: u8 = 255;

    // 디버그 모드: panic
    // 릴리즈 모드: 2의 보수 래핑 (0이 됨)
    // x = x + 1;  // 주의

    // 명시적 오버플로우 처리 메서드

    // 1. wrapping_* : 항상 래핑
    let wrapped = x.wrapping_add(1);
    println!("wrapped: {}", wrapped);  // 0

    // 2. checked_* : Option 반환
    match x.checked_add(1) {
        Some(val) => println!("OK: {}", val),
        None => println!("Overflow detected!"),  // 이 경로 실행
    }

    // 3. saturating_* : 최대/최소값 고정
    let saturated = x.saturating_add(1);
    println!("saturated: {}", saturated);  // 255

    // 4. overflowing_* : (결과, 오버플로우 여부) 반환
    let (result, overflowed) = x.overflowing_add(1);
    println!("result: {}, overflowed: {}", result, overflowed);
    // result: 0, overflowed: true
}

부동소수점 (Floating-Point Types)

fn test_floating_point() {
    let x = 2.0;        // f64 (기본)
    let y: f32 = 3.0;   // f32

    // 연산
    let sum = 5.0 + 10.0;
    let difference = 95.5 - 4.3;
    let product = 4.0 * 30.0;
    let quotient = 56.7 / 32.2;
    let remainder = 43.0 % 5.0;

    // 부동소수점 비교 주의
    let a = 0.1 + 0.2;
    let b = 0.3;
    // a == b  // false! (부동소수점 오차)

    // 올바른 비교
    let epsilon = f64::EPSILON;
    if (a - b).abs() < epsilon {
        println!("거의 같음");
    }

    // 특수값
    let inf = f64::INFINITY;
    let neg_inf = f64::NEG_INFINITY;
    let nan = f64::NAN;

    println!("Is NaN: {}", nan.is_nan());
    println!("Is infinite: {}", inf.is_infinite());
}

불리언 (Boolean Type)

fn test_boolean() {
    let t = true;
    let f: bool = false;

    // 1바이트 크기
    println!("Size of bool: {}", std::mem::size_of::<bool>());  // 1

    // 논리 연산
    let and = t && f;
    let or = t || f;
    let not = !t;

    // 비교 연산 결과
    let is_greater = 5 > 3;  // true
}

문자 (Character Type)

fn test_char() {
    let c = 'z';
    let emoji = '😻';
    let heart = '❤';
    let hangul = '한';

    // 4바이트 유니코드 스칼라 값
    println!("Size: {}", std::mem::size_of::<char>());  // 4

    // 문자열과 다름
    let char_literal = 'a';      // char
    let str_literal = "a";       // &str
    // let wrong = 'ab';         // 에러: 단일 문자만
}

튜플 (Tuple Type)

fn test_tuple() {
    // 1. 기본 선언
    let tup: (i32, f64, u8) = (500, 6.4, 1);

    // 2. 분해 (destructuring)
    let (x, y, z) = tup;
    println!("x: {}, y: {}, z: {}", x, y, z);

    // 3. 인덱스 접근 (점 표기법)
    let five_hundred = tup.0;
    let six_point_four = tup.1;
    let one = tup.2;

    // 4. 빈 튜플 = unit 타입
    let unit: () = ();

    // 5. 단일 요소 튜플
    let single = (5,);  // 쉼표 필수
    // let not_tuple = (5);  // 이건 그냥 정수
}

튜플 활용 예제

fn swap(pair: (i32, i32)) -> (i32, i32) {
    let (a, b) = pair;
    (b, a)  // 교환된 튜플 반환
}

fn divide(dividend: i32, divisor: i32) -> (i32, i32) {
    (dividend / divisor, dividend % divisor)  // (몫, 나머지)
}

fn test_tuple_usage() {
    let pair = (5, 10);
    let swapped = swap(pair);
    println!("Original: {:?}, Swapped: {:?}", pair, swapped);

    let (quotient, remainder) = divide(17, 5);
    println!("17 / 5 = {} remainder {}", quotient, remainder);
}

배열 (Array Type)

fn test_array() {
    // 1. 기본 선언
    let a = [1, 2, 3, 4, 5];
    let b: [i32; 5] = [1, 2, 3, 4, 5];  // 타입 명시

    // 2. 같은 값으로 초기화
    let zeros = [0; 5];     // [0, 0, 0, 0, 0]
    let threes = [3; 10];   // 10개의 3

    // 3. 인덱스 접근
    let first = a[0];
    let second = a[1];

    // 4. 크기 확인
    println!("Length: {}", a.len());

    // 5. 메모리 크기 (스택 할당)
    println!("Size: {} bytes", std::mem::size_of_val(&a));
    // i32 * 5 = 20 bytes
}

배열 주의사항

fn test_array_bounds() {
    let a = [1, 2, 3, 4, 5];

    // 유효한 인덱스
    let element = a[2];
    println!("Element: {}", element);

    // 범위 초과 - 런타임 패닉
    // let invalid = a[10];  // thread 'main' panicked

    // 안전한 접근
    if let Some(&value) = a.get(2) {
        println!("Safe access: {}", value);
    }

    match a.get(10) {
        Some(&value) => println!("Found: {}", value),
        None => println!("Index out of bounds"),
    }
}

배열 반복

fn test_array_iteration() {
    let a = [10, 20, 30, 40, 50];

    // 1. for 루프로 값 순회
    for element in a {
        println!("Value: {}", element);
    }

    // 2. iter()로 참조 순회
    for element in a.iter() {
        println!("Ref: {}", element);
    }

    // 3. 인덱스와 값 함께
    for (index, &value) in a.iter().enumerate() {
        println!("a[{}] = {}", index, value);
    }

    // 4. 가변 반복 (배열은 고정 크기이므로 드묾)
    let mut b = [1, 2, 3];
    for element in b.iter_mut() {
        *element *= 2;
    }
    println!("{:?}", b);  // [2, 4, 6]
}

배열 vs 벡터

fn test_array_vs_vector() {
    // 배열: 고정 크기, 스택, 컴파일 타임 크기 결정
    let array: [i32; 5] = [1, 2, 3, 4, 5];
    // array.push(6);  // 메서드 없음

    // 벡터: 동적 크기, 힙, 런타임 크기 변경 가능
    let mut vector = vec![1, 2, 3, 4, 5];
    vector.push(6);  // OK
    println!("Vector: {:?}", vector);

    // 배열 -> 슬라이스
    let slice: &[i32] = &array[1..3];
    println!("Slice: {:?}", slice);  // [2, 3]
}

타입 변환

fn test_type_conversion() {
    // 1. as 키워드 (명시적 캐스팅)
    let integer: i32 = 42;
    let float = integer as f64;
    let byte = integer as u8;

    // 2. 문자열 -> 숫자
    let num: i32 = "42".parse().unwrap();
    let num: i32 = "42".parse::<i32>().unwrap();  // 터보피시

    // 3. 숫자 -> 문자열
    let s = 42.to_string();
    let s = format!("{}", 42);

    // 4. From/Into 트레이트
    let s = String::from("hello");
    let s: String = "hello".into();

    // 5. TryFrom (실패 가능한 변환)
    use std::convert::TryFrom;
    match i32::try_from(300u16) {
        Ok(n) => println!("Converted: {}", n),
        Err(e) => println!("Conversion failed: {}", e),
    }
}

타입 선택 가이드

fn type_selection_guide() {
    // 정수 기본: i32 (성능과 범위 균형)
    let default_int = 42;

    // 배열 인덱스, 크기: usize
    let arr = [1, 2, 3, 4, 5];
    for i in 0..arr.len() {  // len()은 usize 반환
        println!("{}", arr[i]);
    }

    // 바이트 데이터: u8
    let bytes: Vec<u8> = vec![0x48, 0x65, 0x6C, 0x6C, 0x6F];

    // 고정 크기 필요: 배열
    let weekdays: [&str; 7] = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];

    // 동적 크기 필요: Vec
    let mut dynamic_list = Vec::new();
    dynamic_list.push(1);
    dynamic_list.push(2);
}

3.2 Data Types - 테스트 코드(Rust Playground)
Gist


3.3 Functions

핵심 개념

  • fn 키워드로 정의
  • snake_case 명명 규칙
  • 매개변수는 반드시 타입 명시
  • 반환 타입은 -> 뒤에 명시
  • 표현식 기반: 마지막 표현식이 반환값

기본 함수 선언

fn main() {
    println!("Hello from main!");
    another_function();
    function_with_params(5, 10);
}

fn another_function() {
    println!("Another function");
}

fn function_with_params(x: i32, y: i32) {
    println!("x: {}, y: {}", x, y);
}

함수 반환값

// 1. 표현식으로 반환 (세미콜론 없음)
fn five() -> i32 {
    5  // 표현식
}

fn add(a: i32, b: i32) -> i32 {
    a + b  // 마지막 표현식이 반환값
}

// 2. 명시적 return
fn subtract(a: i32, b: i32) -> i32 {
    return a - b;  // 일찍 반환할 때 유용
}

// 3. 여러 return 경로
fn abs_difference(a: i32, b: i32) -> i32 {
    if a > b {
        return a - b;  // 일찍 반환
    }
    b - a  // 마지막 표현식
}

fn test_returns() {
    assert_eq!(five(), 5);
    assert_eq!(add(2, 3), 5);
    assert_eq!(subtract(10, 3), 7);
    assert_eq!(abs_difference(10, 5), 5);
    assert_eq!(abs_difference(5, 10), 5);
}

표현식 vs 문

fn test_expression_vs_statement() {
    // 문(Statement): 값을 반환하지 않음
    let y = 6;  // 값 바인딩은 문

    // let x = (let y = 6);  // 에러: let은 값이 없음

    // 표현식(Expression): 값을 평가
    let x = 5;  // 5는 표현식
    let y = {
        let x = 3;
        x + 1  // 4를 반환 (세미콜론 없음)
    };
    println!("y: {}", y);  // 4

    // 세미콜론이 있으면 ()를 반환
    let z = {
        let x = 3;
        x + 1;  // 세미콜론 -> 문
    };  // z의 타입은 ()
}

세미콜론의 중요성

// 잘못된 반환
fn wrong_return() -> i32 {
    let x = 5;
    x + 1;  // 세미콜론 때문에 () 반환
            // error: mismatched types
}

// 올바른 반환
fn correct_return() -> i32 {
    let x = 5;
    x + 1  // 세미콜론 없음 -> 표현식
}

// 명시적 return 사용
fn explicit_return() -> i32 {
    let x = 5;
    return x + 1;  // 세미콜론 있어도 OK
}

함수 매개변수 패턴

// 1. 튜플 분해
fn print_coordinates((x, y): (i32, i32)) {
    println!("x: {}, y: {}", x, y);
}

// 2. 참조 매개변수
fn calculate_length(s: &String) -> usize {
    s.len()
}

// 3. 가변 참조
fn change(s: &mut String) {
    s.push_str(", world");
}

fn test_parameters() {
    print_coordinates((10, 20));

    let s = String::from("hello");
    let len = calculate_length(&s);
    println!("Length: {}", len);

    let mut s = String::from("hello");
    change(&mut s);
    println!("{}", s);  // "hello, world"
}

다중 반환값

// 튜플로 여러 값 반환
fn swap(pair: (i32, i32)) -> (i32, i32) {
    let (a, b) = pair;
    (b, a)
}

fn divide_with_remainder(dividend: i32, divisor: i32) -> (i32, i32) {
    (dividend / divisor, dividend % divisor)
}

fn analyze_number(n: i32) -> (bool, bool, i32) {
    let is_positive = n > 0;
    let is_even = n % 2 == 0;
    let absolute = n.abs();
    (is_positive, is_even, absolute)
}

fn test_multiple_returns() {
    let (a, b) = swap((1, 2));
    println!("Swapped: ({}, {})", a, b);

    let (quotient, remainder) = divide_with_remainder(17, 5);
    println!("17 / 5 = {} R {}", quotient, remainder);

    let (positive, even, abs_val) = analyze_number(-42);
    println!("Positive: {}, Even: {}, Abs: {}", positive, even, abs_val);
}

재귀 함수

fn factorial(n: u32) -> u32 {
    if n == 0 {
        1
    } else {
        n * factorial(n - 1)
    }
}

fn fibonacci(n: u32) -> u32 {
    match n {
        0 => 0,
        1 => 1,
        _ => fibonacci(n - 1) + fibonacci(n - 2),
    }
}

fn test_recursion() {
    println!("5! = {}", factorial(5));  // 120
    println!("fib(10) = {}", fibonacci(10));  // 55
}

함수 설계 팁

// 작고 명확한 책임
fn is_even(n: i32) -> bool {
    n % 2 == 0
}

fn is_prime(n: u32) -> bool {
    if n < 2 { return false; }
    for i in 2..=(n as f64).sqrt() as u32 {
        if n % i == 0 { return false; }
    }
    true
}

// 조기 반환으로 가독성 향상
fn process_value(value: Option<i32>) -> i32 {
    // None 체크를 먼저
    let val = match value {
        Some(v) => v,
        None => return 0,
    };

    // 음수 체크
    if val < 0 {
        return -val;
    }

    // 일반 케이스
    val * 2
}

// 타입 별칭으로 명확성
type Point = (i32, i32);
type Result<T> = std::result::Result<T, String>;

fn distance(p1: Point, p2: Point) -> f64 {
    let (x1, y1) = p1;
    let (x2, y2) = p2;
    (((x2 - x1).pow(2) + (y2 - y1).pow(2)) as f64).sqrt()
}

3.4 Comments

핵심 개념

  • 일반 주석: // (한 줄), /* */ (여러 줄)
  • 문서 주석: /// (외부), //! (내부)
  • 문서 주석은 Markdown 지원
  • cargo doc으로 문서 생성

일반 주석

fn test_comments() {
    // 한 줄 주석
    let x = 5;  // 코드 뒤 주석도 가능

    /*
     * 여러 줄 주석
     * 두 번째 줄
     */
    let y = 10;

    /* 중첩 /* 주석 */ 가능 */

    // 코드 주석 처리
    // let unused = 42;
}

문서 주석 - 외부

/// 두 수를 더합니다.
///
/// # Examples
///
/// ```
/// let result = add(2, 3);
/// assert_eq!(result, 5);
/// ```
///
/// # Arguments
///
/// * `a` - 첫 번째 숫자
/// * `b` - 두 번째 숫자
///
/// # Returns
///
/// 두 숫자의 합
fn add(a: i32, b: i32) -> i32 {
    a + b
}

/// 숫자가 짝수인지 확인합니다.
///
/// # Panics
///
/// 이 함수는 패닉하지 않습니다.
///
/// # Safety
///
/// 이 함수는 안전합니다.
fn is_even(n: i32) -> bool {
    n % 2 == 0
}

문서 주석 - 내부

//! 수학 연산을 위한 유틸리티 모듈
//!
//! 이 모듈은 기본적인 수학 연산 함수들을 제공합니다.
//!
//! # Examples
//!
//! ```
//! use my_crate::math;
//! let sum = math::add(2, 3);
//! ```

pub mod math {
    //! 수학 함수 모듈

    /// 덧셈 수행
    pub fn add(a: i32, b: i32) -> i32 {
        a + b
    }
}

문서 주석 섹션들

/// 복잡한 계산을 수행합니다.
///
/// # Arguments
///
/// * `input` - 입력값
///
/// # Returns
///
/// 계산 결과
///
/// # Examples
///
/// ```
/// let result = complex_calculation(42);
/// assert_eq!(result, 84);
/// ```
///
/// # Panics
///
/// `input`이 음수일 때 패닉합니다.
///
/// # Errors
///
/// 이 함수는 에러를 반환하지 않습니다.
///
/// # Safety
///
/// 이 함수는 unsafe 블록을 포함하지 않습니다.
fn complex_calculation(input: i32) -> i32 {
    assert!(input >= 0, "Input must be non-negative");
    input * 2
}

주석 작성 팁

// 나쁜 주석: 코드를 그대로 설명
// x에 5를 할당
let x = 5;

// 좋은 주석: 왜 이렇게 했는지 설명
// 성능 최적화를 위해 버퍼 크기를 미리 할당
let mut buffer = Vec::with_capacity(1000);

// 복잡한 로직 설명
// Boyer-Moore 알고리즘을 사용하여 검색 성능 향상
// O(n) -> O(n/m) 복잡도 개선
fn search_pattern(text: &str, pattern: &str) -> Option<usize> {
    // implementation
    None
}

// TODO, FIXME, HACK 표시
// TODO: 에러 처리 추가 필요
// FIXME: 메모리 누수 가능성
// HACK: 임시 방편, 나중에 리팩토링 필요

3.5 Control Flow

핵심 개념

  • if, else if, else - 조건 분기
  • loop - 무한 루프
  • while - 조건 루프
  • for - 컬렉션 순회
  • 모든 제어문이 표현식 (값을 반환할 수 있음)

if 표현식

fn test_if() {
    let number = 6;

    // 1. 기본 if
    if number < 5 {
        println!("true");
    } else {
        println!("false");
    }

    // 2. else if
    if number % 4 == 0 {
        println!("divisible by 4");
    } else if number % 3 == 0 {
        println!("divisible by 3");
    } else if number % 2 == 0 {
        println!("divisible by 2");
    } else {
        println!("not divisible by 4, 3, or 2");
    }

    // 3. if를 표현식으로 사용
    let condition = true;
    let value = if condition { 5 } else { 6 };
    println!("value: {}", value);

    // 4. 모든 분기가 같은 타입 반환해야 함
    // let wrong = if condition { 5 } else { "six" };  // 에러
}

if를 이용한 패턴

fn test_if_patterns() {
    let number = 7;

    // 1. 범위 체크
    let category = if number < 0 {
        "negative"
    } else if number == 0 {
        "zero"
    } else if number < 10 {
        "single digit"
    } else {
        "multiple digits"
    };

    // 2. 조기 반환
    fn check_positive(n: i32) -> Result<i32, String> {
        if n <= 0 {
            return Err("Not positive".to_string());
        }
        Ok(n * 2)
    }

    // 3. if let 패턴 (Option/Result 처리)
    let some_value = Some(7);
    if let Some(x) = some_value {
        println!("Got: {}", x);
    }
}

loop - 무한 루프

fn test_loop() {
    let mut counter = 0;

    // 1. 기본 무한 루프
    loop {
        counter += 1;
        if counter == 5 {
            break;
        }
    }
    println!("Counter: {}", counter);

    // 2. loop에서 값 반환
    let result = loop {
        counter += 1;
        if counter == 10 {
            break counter * 2;  // 20 반환
        }
    };
    println!("Result: {}", result);

    // 3. 레이블로 중첩 루프 제어
    'outer: loop {
        println!("Outer loop");

        'inner: loop {
            println!("Inner loop");
            break 'outer;  // 외부 루프 탈출
        }

        println!("This will never print");
    }
}

while 루프

fn test_while() {
    let mut number = 3;

    // 1. 기본 while
    while number != 0 {
        println!("{}!", number);
        number -= 1;
    }
    println!("LIFTOFF!");

    // 2. while let 패턴
    let mut stack = vec![1, 2, 3];
    while let Some(top) = stack.pop() {
        println!("Popped: {}", top);
    }

    // 3. 조건이 복잡한 경우
    let mut x = 0;
    while x < 100 && x * x < 500 {
        x += 1;
    }
    println!("x: {}", x);
}

for 루프

fn test_for() {
    let a = [10, 20, 30, 40, 50];

    // 1. 배열 순회 (소유권 이동)
    for element in a {
        println!("Value: {}", element);
    }

    // 2. 참조로 순회
    for element in a.iter() {
        println!("Ref: {}", element);
    }

    // 3. 범위 순회
    for i in 0..5 {  // 0, 1, 2, 3, 4
        println!("{}", i);
    }

    for i in 0..=5 {  // 0, 1, 2, 3, 4, 5 (inclusive)
        println!("{}", i);
    }

    // 4. 역순
    for i in (1..4).rev() {  // 3, 2, 1
        println!("{}!", i);
    }

    // 5. 인덱스와 값
    for (index, value) in a.iter().enumerate() {
        println!("a[{}] = {}", index, value);
    }

    // 6. 가변 반복자
    let mut v = vec![1, 2, 3];
    for item in v.iter_mut() {
        *item *= 2;
    }
    println!("{:?}", v);  // [2, 4, 6]
}

break와 continue

fn test_break_continue() {
    // 1. continue - 다음 반복으로
    for i in 0..10 {
        if i % 2 == 0 {
            continue;  // 짝수는 건너뛰기
        }
        println!("Odd: {}", i);
    }

    // 2. break - 루프 탈출
    let mut sum = 0;
    for i in 1.. {  // 무한 범위
        sum += i;
        if sum > 100 {
            println!("Stopped at i = {}", i);
            break;
        }
    }

    // 3. 레이블과 함께 사용
    'outer: for x in 0..5 {
        for y in 0..5 {
            if x * y > 10 {
                break 'outer;  // 외부 루프 탈출
            }
            println!("x: {}, y: {}", x, y);
        }
    }
}

제어 흐름 비교

fn compare_control_flow() {
    let data = vec![1, 2, 3, 4, 5];

    // 1. 정확한 횟수 반복 -> for
    for i in 0..5 {
        println!("{}", i);
    }

    // 2. 조건이 명확 -> while
    let mut count = 0;
    while count < 5 {
        println!("{}", count);
        count += 1;
    }

    // 3. 무한 루프, 중간에 break -> loop
    let mut sum = 0;
    loop {
        sum += 1;
        if sum > 100 {
            break;
        }
    }

    // 4. 컬렉션 순회 -> for
    for item in data.iter() {
        println!("{}", item);
    }
}

실전 제어 흐름 패턴

// 1. 입력 검증
fn validate_input(value: i32) -> Result<i32, String> {
    if value < 0 {
        return Err("Value must be non-negative".to_string());
    }
    if value > 100 {
        return Err("Value must be <= 100".to_string());
    }
    Ok(value)
}

// 2. 재시도 로직
fn retry_with_limit<F>(mut operation: F, max_attempts: u32) -> Result<(), String>
where
    F: FnMut() -> Result<(), String>,
{
    for attempt in 1..=max_attempts {
        match operation() {
            Ok(_) => return Ok(()),
            Err(e) if attempt == max_attempts => {
                return Err(format!("Failed after {} attempts: {}", max_attempts, e));
            }
            Err(_) => continue,
        }
    }
    unreachable!()
}

// 3. 조기 반환으로 중첩 제거
fn process_data(data: Option<Vec<i32>>) -> i32 {
    // 중첩된 if
    // if let Some(vec) = data {
    //     if !vec.is_empty() {
    //         if vec[0] > 0 {
    //             return vec[0] * 2;
    //         }
    //     }
    // }

    // 조기 반환
    let vec = match data {
        Some(v) => v,
        None => return 0,
    };

    if vec.is_empty() {
        return 0;
    }

    if vec[0] <= 0 {
        return 0;
    }

    vec[0] * 2
}

// 4. 상태 머신 패턴
enum State { Start, Processing, Done }

fn state_machine() {
    let mut state = State::Start;
    let mut count = 0;

    loop {
        state = match state {
            State::Start => {
                println!("Starting...");
                State::Processing
            }
            State::Processing => {
                count += 1;
                println!("Processing... {}", count);
                if count >= 3 {
                    State::Done
                } else {
                    State::Processing
                }
            }
            State::Done => {
                println!("Done!");
                break;
            }
        };
    }
}

종합 예제

FizzBuzz (다양한 구현)

// 1. if-else 버전
fn fizzbuzz_if(n: u32) {
    for i in 1..=n {
        if i % 15 == 0 {
            println!("FizzBuzz");
        } else if i % 3 == 0 {
            println!("Fizz");
        } else if i % 5 == 0 {
            println!("Buzz");
        } else {
            println!("{}", i);
        }
    }
}

// 2. match 버전
fn fizzbuzz_match(n: u32) {
    for i in 1..=n {
        match (i % 3, i % 5) {
            (0, 0) => println!("FizzBuzz"),
            (0, _) => println!("Fizz"),
            (_, 0) => println!("Buzz"),
            _ => println!("{}", i),
        }
    }
}

// 3. 함수형 스타일
fn fizzbuzz_functional(n: u32) {
    (1..=n).for_each(|i| {
        let output = match (i % 3, i % 5) {
            (0, 0) => "FizzBuzz".to_string(),
            (0, _) => "Fizz".to_string(),
            (_, 0) => "Buzz".to_string(),
            _ => i.to_string(),
        };
        println!("{}", output);
    });
}

소수 판별 및 생성

fn is_prime(n: u32) -> bool {
    if n <= 1 {
        return false;
    }
    if n <= 3 {
        return true;
    }
    if n % 2 == 0 || n % 3 == 0 {
        return false;
    }

    let mut i = 5;
    while i * i <= n {
        if n % i == 0 || n % (i + 2) == 0 {
            return false;
        }
        i += 6;
    }

    true
}

fn primes_up_to(limit: u32) -> Vec<u32> {
    (2..=limit).filter(|&n| is_prime(n)).collect()
}

fn nth_prime(n: usize) -> u32 {
    let mut count = 0;
    let mut num = 2;

    loop {
        if is_prime(num) {
            count += 1;
            if count == n {
                return num;
            }
        }
        num += 1;
    }
}

계산기

fn calculator(a: f64, b: f64, op: char) -> Result<f64, String> {
    match op {
        '+' => Ok(a + b),
        '-' => Ok(a - b),
        '*' => Ok(a * b),
        '/' => {
            if b == 0.0 {
                Err("Division by zero".to_string())
            } else {
                Ok(a / b)
            }
        }
        _ => Err(format!("Unknown operator: {}", op)),
    }
}

fn test_calculator() {
    let operations = vec![
        (10.0, 5.0, '+'),
        (10.0, 5.0, '-'),
        (10.0, 5.0, '*'),
        (10.0, 5.0, '/'),
        (10.0, 0.0, '/'),
    ];

    for (a, b, op) in operations {
        match calculator(a, b, op) {
            Ok(result) => println!("{} {} {} = {}", a, op, b, result),
            Err(e) => println!("{} {} {} -> Error: {}", a, op, b, e),
        }
    }
}

실행 테스트

모든 예제를 실행하는 테스트 파일:

fn main() {
    println!("=== 3.1 Variables and Mutability ===");
    test_immutable();
    test_mutable();
    test_shadowing();
    test_constants();

    println!("\n=== 3.2 Data Types ===");
    test_integer_types();
    test_overflow();
    test_floating_point();
    test_tuple();
    test_array();

    println!("\n=== 3.3 Functions ===");
    test_returns();
    test_expression_vs_statement();
    test_recursion();

    println!("\n=== 3.4 Comments ===");
    // 주석 테스트는 컴파일만 확인

    println!("\n=== 3.5 Control Flow ===");
    test_if();
    test_loop();
    test_while();
    test_for();

    println!("\n=== Comprehensive Examples ===");
    fizzbuzz_match(15);
    println!("Is 17 prime? {}", is_prime(17));
    println!("Primes up to 20: {:?}", primes_up_to(20));
    test_calculator();
}

3.3 Functions & 3.5 Control Flow - 테스트 코드
Gist


참고 자료

Comments