Step 04: 소유권과 차용 이해하기
0 studying now
core 90 min
소유권과 차용 이해하기
Rust의 핵심인 소유권과 차용 규칙을 마스터합니다.
Execute this step
Run from project root:
cargo runStep 4: 소유권과 차용 이해하기
학습 목표
- Rust의 가장 중요한 개념인 소유권 이해하기
- 차용 규칙을 완전히 마스터하기
- 메모리 안전성이 어떻게 보장되는지 이해하기
핵심 개념
1. 소유권 규칙
- 각 값은 정확히 하나의 **소유자(owner)**를 가진다
- 한 번에 하나의 소유자만 존재할 수 있다
- 소유자가 스코프를 벗어나면 값이 드롭(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. 차용 규칙 ⭐⭐⭐
중요: 동시에 다음 중 하나만 가능
- 여러 개의 불변 참조 (
&T) - 또는 정확히 하나의 가변 참조 (
&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// 이런 코드는 컴파일되지 않음! 2fn dangling() -> &String { 3 let s = String::from("hello"); 4 &s // 에러! s가 함수 끝나면 드롭되는데 참조를 반환하려고 함 5}
실행
cargo run
체크리스트
- [ ] 소유권 이동(move)을 이해했습니다
- [ ] Clone과 Copy의 차이를 알게 되었습니다
- [ ] 불변 참조(&T)와 가변 참조(&mut T)를 사용했습니다
- [ ] 차용 규칙을 이해했습니다
- [ ] 함수에 소유권/참조를 전달하는 방법을 알게 되었습니다
- [ ] 메모리 안전성이 어떻게 보장되는지 이해했습니다
다음 단계
Step 5에서는 벡터와 반복자를 사용하여 여러 책을 효율적으로 처리합니다.