소유권(Ownership):
빌림(Borrowing):
fn main() {
let s = String::from("Hello, Rust!"); // 소유자 s
let r1 = &s; // r1은 불변 참조
let r2 = &s; // r2도 불변 참조
println!("r1: {}, r2: {}", r1, r2); // 동시에 읽기 가능
}
fn main() {
let mut s = String::from("Hello"); // mut 키워드로 가변 변수 선언
let r1 = &mut s; // r1은 가변 참조
r1.push_str(", Rust!"); // 가변 참조를 통해 s를 수정
println!("{}", r1); // "Hello, Rust!"
}
빌림은 메모리 안전성을 유지하면서도 데이터의 중복 사용을 가능하게 만듭니다. 아래에서 몇 가지 상황을 통해 빌림의 필요성을 이해해 보겠습니다.
fn print_length(s: &String) { // &String은 빌림을 통해 데이터 접근
println!("Length: {}", s.len());
}
fn main() {
let s = String::from("Hello"); // 소유자
print_length(&s); // 빌림
println!("s: {}", s); // 소유권 유지 -> 여전히 s를 사용할 수 있음
}
위 코드에서 print_length 함수가 String을 빌려가기 때문에, 함수가 끝난 뒤에도 s의 소유권은 유지됩니다.
fn main() {
let mut s = String::from("Hello");
let r1 = &s; // 불변 참조
let r2 = &mut s; // 가변 참조 (에러 발생!)
println!("{}, {}", r1, r2);
}
위 코드는 불변 참조(r1)와 가변 참조(r2)가 동시에 존재하기 때문에 에러가 발생합니다. 이는 데이터 경합(Race Condition)을 방지하기 위한 Rust의 설계입니다.
빌림은 스코프가 끝나면 해제됩니다. 즉, 더 이상 참조가 존재하지 않을 때, 새로운 참조를 만들 수 있습니다.
fn main() {
let mut s = String::from("Hello");
{
let r1 = &mut s; // r1이 가변 참조
r1.push_str(", Rust!");
} // r1은 여기서 스코프 종료 -> 빌림 해제
let r2 = &s; // 새로운 불변 참조 가능
println!("{}", r2);
}
빌림의 규칙은 Rust가 안전하고 오류가 적은 코드를 작성하도록 돕는 중요한 원칙입니다. 초반에는 약간 복잡하게 느껴질 수 있지만, 익숙해지면 Rust의 강력함을 느낄 수 있는 핵심 개념입니다!