RUST 20일 과정

Day 3: Ownership & Borrowing

@thiskorea 2025. 1. 6. 13:36
Day 3: Ownership & Borrowing

Day 3: Ownership & Borrowing

1. 수업 개요

  • 주제: Rust의 핵심 철학인 Ownership(소유권)과 Borrowing(빌려 쓰기) 개념 이해
  • 학습 목표:
    • Rust에서의 스택과 힙, 메모리 구조를 파악한다.
    • Ownership 규칙을 이해하고, Move/Copy가 어떻게 작동하는지 예제를 통해 확인한다.
    • Borrowing(&, &mut)을 통해 변수(메모리)를 안전하게 공유하는 방식을 익힌다.
    • 문자열 슬라이스 등 Slice 개념을 이해하고 실습해본다.

2. 이론

스택(Stack)과 힙(Heap)

  • 스택: 함수 호출 시 빠르게 할당/해제되는 정적 메모리 구역
  • 힙: 동적 메모리 할당이 이루어지는 공간 (Box, String, Vec 등은 힙 메모리를 사용)

Ownership(소유권) 규칙

  • 규칙 1: 각 값(value)은 오직 하나의 Owner(소유자) 변수만 가진다.
  • 규칙 2: Owner(소유자)가 스코프를 벗어나면, 그 값은 자동으로 메모리 해제된다(Drop).
  • 예시:
  • {
        let s = String::from("Hello");
        // 여기서 s는 "Hello" 문자열의 소유자
    } 
    // 스코프 밖으로 나가면 s는 Drop되어 메모리가 해제됨

Move(이동)와 Copy(복사)

  • Move: 힙 데이터를 가진 변수를 다른 변수에 할당할 때, 소유권이 넘어간다.
  • let s1 = String::from("Rust");
    let s2 = s1; 
    // s1 -> s2 로 소유권이 이동(Move)
    // 이제 s1은 유효하지 않음
  • Copy: 스택 데이터(정수, 부동소수점, 불리언, 문자 등)는 할당 시 복사가 일어남.
  • let x = 5;
    let y = x; 
    // x와 y 모두 유효

Borrowing(대여, 참조)

  • 불변 참조(&): 읽기 전용 접근
  • 가변 참조(&mut): 읽고/쓰기가 모두 가능하지만, 특정 시점에 오직 하나만 허용
  • fn main() {
        let mut s = String::from("Hello");
        // 불변 참조
        let r1 = &s;         
        let r2 = &s;         
        // 가변 참조
        let r3 = &mut s;     
        // 불변 참조와 가변 참조를 동시에 사용하는 것은 컴파일 에러
    }
  • 규칙:
    • 동시에 여러 불변 참조는 가능
    • 가변 참조는 한 번에 하나만 가능 (데이터 레이스 방지)
    • 참조는 유효 범위 내에서만 사용 가능 (Owner 스코프가 끝나면 무효)

Slice(슬라이스) 기초

  • 문자열이나 배열의 특정 구간을 참조하는 방법
  • &str, [T] 등의 슬라이스
  • let greeting = String::from("Hello Rust");
    let hello = &greeting[0..5];  // "Hello"
    let rust = &greeting[6..];    // "Rust"
    // greeting이 스코프를 벗어나면 hello, rust도 사용 불가

3. 실습

Move, Copy 개념 실습

  • String 타입 변수를 선언하고, 다른 변수에 대입해본다.
  • 소유권이 이동한 뒤 원래 변수를 사용하면 컴파일 에러가 발생함을 확인한다.
  • 정수형 변수를 복사할 땐 문제가 없음을 비교해본다.

Borrowing 실습

fn main() {
    let s1 = String::from("Borrow me");
    let len = calculate_length(&s1);  
    println!("'{}'의 길이는 {}", s1, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

문자열 슬라이스 실습

  • 문자열의 특정 구간을 슬라이스로 추출해 출력
  • 슬라이스로 반환하는 함수를 작성해보기:
  • fn first_word(s: &String) -> &str {
        // 공백이나 특정 문자를 만나기 전까지의 단어를 슬라이스로 반환
    }

4. 마무리 및 과제

오늘 배운 내용 정리

  • Ownership: 소유권과 스코프가 연동되어 메모리 관리가 자동화됨
  • Move/Copy: 힙 데이터 vs 스택 데이터
  • Borrowing: 불변 참조 & 가변 참조 사용 시 주의 사항
  • Slice(슬라이스) 개념과 문자열 슬라이스 활용

과제(실습 문제)

  1. 다음 코드에서 발생하는 에러를 해결해보자:
  2. fn main() {
        let mut text = String::from("Day3");
        let ref1 = &text;
        let ref2 = &mut text;
        println!("{}", ref1);
        println!("{}", ref2);
    }
  3. 문자열 슬라이스를 이용해 문장에서 첫 단어만 추출하는 함수를 완성하고, 테스트
  4. Move와 Copy가 각각 어떻게 다른지, 예시 코드를 작성해 GitHub에 올리기(가능하다면).

학습 팁

  • Rust 컴파일러의 에러 메시지를 꼼꼼히 읽으면, Ownership & Borrowing에서 발생하는 문제를 빠르게 찾을 수 있음.
  • 문자열 슬라이스(&str)를 잘 활용하면, 데이터를 복사하지 않고도 특정 부분을 효율적으로 처리 가능.
  • 이 개념들을 이해해두면 이후에 Struct, Enum 등 더 복잡한 데이터 구조를 다룰 때 훨씬 수월해진다.

5. 수업 진행 예시 타임라인 (총 50분 수업 기준)

  • 10분: 이론: 스택/힙, Ownership & Borrowing 개념 설명
  • 20분: 실습: Move/Copy, 참조 & 가변 참조, 슬라이스 예제 코딩 & 테스트
  • 10분: 에러 메시지 분석 및 디버깅(에러 예시 코드 함께 수정해보기)
  • 5분: 수업 정리, 질의응답
  • 5분: 과제 안내 및 마무리

정리: Rust의 Ownership & Borrowing은 런타임에 비용을 추가하지 않고도 메모리 안전성을 보장하는 핵심 메커니즘입니다.