Step 04: 소유권과 차용 이해하기
core 90 min

소유권과 차용 이해하기

Rust의 핵심인 소유권과 차용 규칙을 마스터합니다.

Execute this step

Run from project root:
cargo run

Step 4: 소유권과 차용 이해하기

학습 목표

  • Rust의 가장 중요한 개념인 소유권 이해하기
  • 차용 규칙을 완전히 마스터하기
  • 메모리 안전성이 어떻게 보장되는지 이해하기

핵심 개념

1. 소유권 규칙

  1. 각 값은 정확히 하나의 **소유자(owner)**를 가진다
  2. 한 번에 하나의 소유자만 존재할 수 있다
  3. 소유자가 스코프를 벗어나면 값이 드롭(drop)된다
1fn main() {
2    // s1이 String의 소유자
3    let s1 = String::from("hello");
4    
5    // s1의 소유권이 s2로 이동 (move)
6    let s2 = s1;
7    
8    // 에러! s1은 더 이상 유효하지 않음
9    // println!("{}", s1);
10    
11    println!("{}", s2); // OK
12} // s2가 스코프를 벗어나면 String이 드롭됨

2. 함수와 소유권

1fn main() {
2    let s = String::from("hello");
3    
4    takes_ownership(s); // s의 소유권이 함수로 이동
5    
6    // 에러! s는 더 이상 유효하지 않음
7    // println!("{}", s);
8}
9
10fn takes_ownership(some_string: String) {
11    println!("{}", some_string);
12} // some_string이 드롭됨

3. 차용 (Borrowing)

소유권을 이동하지 않고 참조만 전달할 수 있습니다.

1fn main() {
2    let s = String::from("hello");
3    
4    // 불변 차용
5    let len = calculate_length(&s);
6    
7    println!("'{}' 길이: {}", s, len); // s 여전히 유효!
8}
9
10fn calculate_length(s: &String) -> usize {
11    s.len()
12} // 차용이 끝나지만 s의 소유권은 원래 소유자에게 있음

4. 가변 차용

1fn main() {
2    let mut s = String::from("hello");
3    
4    change(&mut s);
5    
6    println!("{}", s); // "hello, world"
7}
8
9fn change(s: &mut String) {
10    s.push_str(", world");
11}

5. 차용 규칙 ⭐⭐⭐

중요: 동시에 다음 중 하나만 가능

  1. 여러 개의 불변 참조 (&T)
  2. 또는 정확히 하나의 가변 참조 (&mut T)
1fn main() {
2    let mut s = String::from("hello");
3    
4    // OK: 여러 불변 참조
5    let r1 = &s;
6    let r2 = &s;
7    println!("{} {}", r1, r2);
8    
9    // OK: 불변 참조가 끝난 후 가변 참조
10    let r3 = &mut s;
11    r3.push_str(" world");
12    
13    // 에러: 가변 참조와 불변 참조를 동시에 가질 수 없음
14    // let r4 = &s;
15    // println!("{} {}", r3, r4);
16}

소스 코드: 소유권 데모

1fn main() {
2    println!("=== 소유권 데모 ===\n");
3    
4    // 1. 소유권 이동
5    println!("1. 소유권 이동");
6    let s1 = String::from("hello");
7    let s2 = s1; // move
8    // println!("{}", s1); // 에러!
9    println!("s2 = {}\n", s2);
10    
11    // 2. Clone으로 깊은 복사
12    println!("2. Clone으로 깊은 복사");
13    let s3 = String::from("world");
14    let s4 = s3.clone(); // 명시적 복사
15    println!("s3 = {}, s4 = {}\n", s3, s4); // 둘 다 OK
16    
17    // 3. Copy 트레이트 (스택 데이터)
18    println!("3. Copy 트레이트 (정수 등)");
19    let x = 5;
20    let y = x; // copy (이동이 아님!)
21    println!("x = {}, y = {}\n", x, y); // 둘 다 OK
22    
23    // 4. 함수와 소유권
24    println!("4. 함수로 소유권 전달");
25    let s5 = String::from("function");
26    takes_ownership(s5);
27    // println!("{}", s5); // 에러!
28    println!();
29    
30    // 5. 참조로 차용
31    println!("5. 참조로 차용");
32    let s6 = String::from("borrow");
33    borrows_value(&s6);
34    println!("s6 여전히 유효: {}\n", s6);
35    
36    // 6. 가변 참조
37    println!("6. 가변 참조");
38    let mut s7 = String::from("mutable");
39    println!("변경 전: {}", s7);
40    mutates_value(&mut s7);
41    println!("변경 후: {}\n", s7);
42    
43    // 7. 참조 규칙 데모
44    demonstrate_borrow_rules();
45}
46
47fn takes_ownership(s: String) {
48    println!("  함수가 소유권 가져감: {}", s);
49} // s 드롭
50
51fn borrows_value(s: &String) {
52    println!("  함수가 차용만 함: {}", s);
53} // 차용 끝, 소유자는 여전히 유효
54
55fn mutates_value(s: &mut String) {
56    s.push_str("!");
57    println!("  함수가 값 변경: {}", s);
58}
59
60fn demonstrate_borrow_rules() {
61    println!("7. 차용 규칙");
62    let mut s = String::from("hello");
63    
64    // OK: 여러 불변 참조
65    println!("  여러 불변 참조 OK:");
66    let r1 = &s;
67    let r2 = &s;
68    let r3 = &s;
69    println!("    r1={}, r2={}, r3={}", r1, r2, r3);
70    // r1, r2, r3 여기서 더 이상 사용 안 함
71    
72    // OK: 불변 참조가 끝난 후 가변 참조
73    println!("  불변 참조 후 가변 참조 OK:");
74    let r4 = &mut s;
75    r4.push_str(" world");
76    println!("    r4={}", r4);
77    
78    // 에러 예제 (주석 처리)
79    // let r5 = &s;     // 불변 참조
80    // let r6 = &mut s; // 에러! 불변과 가변 동시에 불가
81    // println!("{} {}", r5, r6);
82}

Library 코드에서의 차용

1impl Library {
2    // 불변 참조 반환
3    fn find_book(&self, id: u32) -> Option<&Book> {
4        self.books.get(&id)
5    }
6    
7    // 가변 참조 반환
8    fn find_book_mut(&mut self, id: u32) -> Option<&mut Book> {
9        self.books.get_mut(&id)
10    }
11    
12    // 사용 예
13    fn example(&mut self) {
14        // 불변 차용 - 여러 번 가능
15        let book1 = self.find_book(1);
16        let book2 = self.find_book(2);
17        
18        // 가변 차용 - 하나만 가능
19        if let Some(book) = self.find_book_mut(1) {
20            book.borrow_book(String::from("사용자"), String::from("2026-02-20"));
21        }
22        // 여기서 가변 차용이 끝남
23        
24        // 이제 다시 차용 가능
25        let book3 = self.find_book(1);
26    }
27}

메모리 안전성

소유권 시스템 덕분에 Rust는 컴파일 타임에 다음을 방지합니다:

  1. 댕글링 포인터: 해제된 메모리를 가리키는 포인터
  2. 이중 해제: 같은 메모리를 두 번 해제
  3. 데이터 레이스: 동시에 여러 곳에서 데이터 수정
1// 이런 코드는 컴파일되지 않음!
2fn dangling() -> &String {
3    let s = String::from("hello");
4    &s  // 에러! s가 함수 끝나면 드롭되는데 참조를 반환하려고 함
5}

실행

cargo run

체크리스트

  • [ ] 소유권 이동(move)을 이해했습니다
  • [ ] Clone과 Copy의 차이를 알게 되었습니다
  • [ ] 불변 참조(&T)와 가변 참조(&mut T)를 사용했습니다
  • [ ] 차용 규칙을 이해했습니다
  • [ ] 함수에 소유권/참조를 전달하는 방법을 알게 되었습니다
  • [ ] 메모리 안전성이 어떻게 보장되는지 이해했습니다

다음 단계

Step 5에서는 벡터와 반복자를 사용하여 여러 책을 효율적으로 처리합니다.

Did you find this helpful? Give it a cheer!