Rust에서 참조(reference)와 가변 참조(mutable reference)는 매우 중요한 개념입니다. 참조는 데이터를 소유하지 않으면서도 접근할 수 있게 해주고, 가변 참조는 데이터를 수정할 수 있게 해줍니다. 이 두 개념을 통해 Rust는 메모리 안전성을 보장하고 데이터 경쟁을 방지합니다. 예제 코드를 통해 각각의 개념을 자세히 설명하겠습니다.
참조는 기본적으로 불변(immutable)입니다. 이는 참조를 통해 데이터에 접근할 수 있지만, 수정할 수 없음을 의미합니다.
fn main() {
let s1 = String::from("hello");
let s2 = &s1; // s2는 s1의 불변 참조입니다.
println!("s1: {}", s1);
println!("s2: {}", s2);
}
위 코드에서 s2는 s1에 대한 참조입니다. s1과 s2 모두 "hello"라는 문자열 데이터를 가리키고 있지만, s2를 통해 s1의 데이터를 수정할 수 없습니다.
가변 참조는 데이터를 수정할 수 있게 해줍니다. 단, 가변 참조는 동시에 하나만 존재할 수 있습니다. 이는 데이터 경쟁을 방지하기 위한 Rust의 안전장치입니다.
fn main() {
let mut s1 = String::from("hello");
let s2 = &mut s1; // s2는 s1의 가변 참조입니다.
s2.push_str(", world!"); // s2를 통해 s1의 데이터를 수정합니다.
println!("s2: {}", s2);
// println!("s1: {}", s1); // 이 줄을 활성화하면 컴파일 에러가 발생합니다.
}
위 코드에서 s2는 s1의 가변 참조입니다. s2를 통해 s1의 데이터를 수정할 수 있습니다. 단, s1에 대한 불변 참조가 존재하는 동안에는 가변 참조를 생성할 수 없으며, 가변 참조가 존재하는 동안에는 다른 가변 참조나 불변 참조를 생성할 수 없습니다.
fn main() {
let mut s = String::from("hello");
// 불변 참조 생성
let r1 = &s;
let r2 = &s;
println!("r1: {}, r2: {}", r1, r2);
// r1과 r2의 범위는 여기서 끝납니다.
// 이제 가변 참조를 생성할 수 있습니다.
let r3 = &mut s;
r3.push_str(", world!");
println!("r3: {}", r3);
// 불변 참조를 다시 생성할 수 있습니다.
let r4 = &s;
println!("r4: {}", r4);
}
이 예제에서는 불변 참조 r1과 r2가 먼저 생성되고 사용됩니다. 이 범위가 끝난 후에 가변 참조 r3를 생성하여 데이터를 수정합니다. r3의 범위가 끝난 후 다시 불변 참조 r4를 생성하여 데이터를 읽습니다.
이러한 규칙을 이해하고 준수하면 Rust에서 안전하고 효율적인 메모리 관리를 할 수 있습니다.