대여 알아보기
이전 모듈에서는 값에 소유자를 포함하는 방법을 알아보았습니다. 한 변수에서 다른 변수로 소유권을 이동하여 값의 소유권을 이전합니다. 숫자 같은 단순 값의 경우와 같이 Copy
특성을 구현하는 형식은 소유권을 이전할 수 없습니다.
‘복제’ 프로세스를 사용하여 값을 ‘명시적으로’ 복사할 수도 있습니다. clone
메서드를 호출하고 복사되는 새 값을 가져옵니다. 그러면 원래 값이 이동되지 않고 계속 사용할 수 있습니다.
함수 및 기타 변수가 완전히 소유하지 않고 특정 데이터를 사용하도록 허용할 수 있다면 좋지 않을까요?
이 유형의 기능은 ‘참조’를 사용하여 사용할 수 있습니다. 참조를 사용하면 소유권을 차지하지 않고 값을 “대여”할 수 있습니다.
let greeting = String::from("hello");
let greeting_reference = &greeting; // We borrow `greeting` but the string data is still owned by `greeting`
println!("Greeting: {}", greeting); // We can still use `greeting`
이전 코드에서는 참조 기호(&
)를 사용하여 greeting
을 빌렸습니다. 변수 greeting_reference
가 문자열 참조(&String
) 형식이었습니다. greeting
을 대여만 하고 소유권을 이동하지 않았기 때문에 greeting_reference
를 만든 후에도 greeting
을 계속 사용할 수 있었습니다.
함수의 참조
실제로 greeting_reference
를 아무것에도 사용하지 않으므로 이 예제는 그리 흥미롭지 않습니다. 함수에서 참조를 사용하는 방법을 살펴봅시다.
fn print_greeting(message: &String) {
println!("Greeting: {}", message);
}
fn main() {
let greeting = String::from("Hello");
print_greeting(&greeting); // `print_greeting` takes a `&String` not an owned `String` so we borrow `greeting` with `&`
print_greeting(&greeting); // Since `greeting` didn't move into `print_greeting` we can use it again
}
대여를 사용하면 전체 소유권을 차지하지 않고 값을 사용할 수 있습니다. 그러나 나중에 확인하겠지만, 값을 대여하는 경우 완전히 소유한 값으로 수행할 수 있는 모든 작업이 가능한 것은 아닙니다.
대여한 값 변경
대여한 값을 변경하려고 하면 어떻게 될까요?
fn change(message: &String) {
message.push_str("!"); // We try to add a "!" to the end of our message
}
fn main() {
let greeting = String::from("Hello");
change(&greeting);
}
이 코드는 컴파일되지 않습니다. 대신 다음과 같은 컴파일러 오류가 표시됩니다.
error[E0596]: cannot borrow `*message` as mutable, as it is behind a `&` reference
--> src/main.rs:2:3
|
1 | fn change(message: &String) {
| ------- help: consider changing this to be a mutable reference: `&mut String`
2 | message.push_str("!"); // We try to add a "!" to the end of our message
| ^^^^^^^ `message` is a `&` reference, so the data it refers to cannot be borrowed as mutable
컴파일러 오류를 자세히 살펴보면 형식 매개 변수를 &String
에서 &mut String
으로 변경하여 참조를 ‘변경 가능’한 것으로 변경하는 방법에 관한 힌트를 확인할 수 있습니다. 또한 원래 값을 변경 가능으로 선언해야 합니다.
fn main() {
let mut greeting = String::from("hello");
change(&mut greeting);
}
fn change(text: &mut String) {
text.push_str(", world");
}
‘변경 불가능 대여’라고 하는 &
대여를 사용하면 데이터를 읽을 수만 있고 변경할 수는 없습니다. "변경 가능 대여"라고 하는 &mut
대여를 사용하면 데이터를 읽고 변경할 수 있습니다.
대여 및 변경 가능 참조
이제 Rust의 메모리 관리 스토리의 핵심에 도달했습니다. 변경 불가능 참조와 변경 가능 참조 간에는 Rust 프로그램을 빌드하는 방법에 근본적인 영향을 주는 또 다른 차이점이 있습니다. T
형식의 값을 대여하는 경우 다음 규칙이 적용됩니다.
코드는 동시에 둘 다가 아니라 다음 정의 중 ‘하나’를 구현해야 합니다.
- 하나 이상의 변경 불가능 참조(
&T
) - 정확히 하나의 변경 가능 참조(
&mut T
)
다음 코드에는 허용되는 정의가 없으므로 컴파일이 실패합니다.
fn main() {
let mut value = String::from("hello");
let ref1 = &mut value;
let ref2 = &mut value;
println!("{}, {}", ref1, ref2);
}
error[E0499]: cannot borrow `value` as mutable more than once at a time
--> src/main.rs:5:16
|
4 | let ref1 = &mut value;
| ---------- first mutable borrow occurs here
5 | let ref2 = &mut value;
| ^^^^^^^^^^ second mutable borrow occurs here
6 |
7 | println!("{}, {}", ref1, ref2);
| ---- first borrow later used here
변경 불가능 참조와 변경 가능 참조를 혼합하려 할 수는 있지만 컴파일러에서 경고할 것입니다.
fn main() {
let mut value = String::from("hello");
let ref1 = &value;
let ref2 = &mut value;
println!("{}, {}", ref1, ref2);
}
error[E0502]: cannot borrow `value` as mutable because it is also borrowed as immutable
--> src/main.rs:5:16
|
4 | let ref1 = &value;
| ------ immutable borrow occurs here
5 | let ref2 = &mut value;
| ^^^^^^^^^^ mutable borrow occurs here
6 |
7 | println!("{}, {}", ref1, ref2);
| ---- immutable borrow later used here
처음에는 제한이 너무 엄격해 보일 수도 있지만 이 대여 양상으로 인해 Rust 코드에서 데이터 경합이 발생하지 않는 등 모든 문제가 방지됩니다.