인용 및 차용
fn main() {
let s1 = String::from("hello");
//
let (s2, len) = calculate_length(s1);
println!("The length of '{}' is {}.", s2, len);
}
fn calculate_length(s: String) -> (String, usize) {
let length = s.len(); // len() 문자열의 길이를 반환합니다.
(s, length)
}
위 코드에서는 계산 길이 함수가 호출된 후에도 문자열을 계속 사용할 수 있도록 호출 함수에 문자열을 반환해야 하는데, 이는 문자열이 계산 길이로 이동했기 때문에 불편할 수밖에 없습니다.
참조
Rust는 참조 ()라고 하는 소유권을 가지지 않고 값을 사용할 수 있는 기능을 제공합니다. 참조는 해당 주소에 저장된 다른 변수의 데이터에 액세스할 수 있는 주소라는 점에서 포인터와 같으며, 참조는 특정 유형의 유효한 값을 가리키도록 보장됩니다.
fn main() {
let s1 = String::from("hello");
let slen = calculate_string(&s1);
println!("The length of '{}' is {}.", s1, slen);
}
fn calculate_string(s: &String) -> usize {
s.len()
}
위의 코드는 참조를 사용하고 있습니다.
문자열 s1을 가리키는 &String s의 메모리 도식입니다:
let s1 = String::from("hello");
let slen = calculate_string(&s1);
s1은 s1에 대한 참조를 생성하며, 참조이기 때문에 소유되지 않습니다. 소유되지 않기 때문에 참조가 더 이상 사용되지 않을 때 참조가 가리키는 값은 버려지지 않습니다.
fn calculate_string(s: &String) -> usize {
s.len()
}
마찬가지로 함수 시그니처에 &String을 사용하면 함수 인자 s가 참조임을 나타냅니다. 참조가 가리키는 데이터는 s를 소유하지 않기 때문에 s가 중단되어도 삭제되지 않습니다. 함수가 실제 값 대신 참조를 인수로 사용하는 경우, 소유권이 없는 값이므로 소유권을 반환하기 위해 값을 반환할 필요가 없습니다.
다른 용도로 빌리다.
참조를 만드는 행위를 차용이라고 합니다.
fn main() {
let s1 = String::from("hello");
change(&s1);
}
fn change(s: &String) {
s.push_str("{}, World!");
}
위의 예에서 빌린 값을 변경하려는 시도가 컴파일 후 작동하지 않는 것으로 나타났습니다.
|
105 | s.push_str("{}, World!");
| ^ `s` is a `&` reference, so the data it refers to cannot be borrowed as mutable
|
help: consider changing this to be a mutable reference
|
104 | fn change(s: &mut String) {
| +++
변수가 불변인 것처럼 참조도 불변이며, 참조 값의 수정은 기본적으로 허용되지 않습니다.
변경 가능한 참조
기본적으로 참조 값을 변경할 수 없으므로 참조 값을 변경할 수 있는 방법이 있습니다.
fn main() {
let mut s1 = String::from("hello");
change(&mut s1);
}
fn change(s: &mut String) {
s.push_str("{}, World!");
}
변수 s1을 변경 가능하게 만든 다음 변경 함수가 호출되는 &mut s1에 대한 변경 가능한 참조를 생성하고, 변경 함수가 s: &mut 문자열에 대한 변경 가능한 참조를 받아들일 수 있다는 사실을 업데이트하면 변경 함수가 차용하는 값이 변경된다는 것을 나타냅니다.
차용한 값을 수정할 수 있도록 허용하는 것을 변경 가능한 참조라고 합니다.
변수 참조에는 제한이 있습니다. 해당 변수에 대한 변수 참조가 있는 경우 해당 변수에 대한 다른 참조를 만들 수 없습니다.
let mut s = String::from("Hello");
let r1 = &mut s;
let r2 = &mut s;
println!("{}, {}", r1, r2);
|
101 | let r1 = &mut s;
| ------ first mutable borrow occurs here
102 | let r2 = &mut s;
| ^^^^^^ second mutable borrow occurs here
103 |
104 | println!("{}, {}", r1, r2);
| -- first borrow later used here
이 오류는 여러 변수 차용을 동시에 생성할 수 없음을 나타냅니다. 첫 번째 변수 차용은 r1에 있으며, 이는 println까지 지속됩니다! 그러나 r1 변수 참조를 생성하고 사용하는 사이에 다른 변수 참조가 생성되었습니다.
동일한 데이터에 대한 여러 변경 가능한 참조를 동시에 방지하면 컴파일 시 데이터 경합을 피할 수 있다는 장점이 있습니다.
데이터 경쟁은 세 가지 행동으로 인해 발생할 수 있습니다:
- 동시에 동일한 데이터에 액세스하는 둘 이상의 포인터
- 데이터를 쓰는 데 하나 이상의 포인터가 사용됩니다.
- 데이터 액세스를 동기화하는 메커니즘 없음
데이터 경합은 런타임에 추적하기 어려운 정의되지 않은 동작으로 이어질 수 있는데, Rust는 데이터 경합 없이 코드를 컴파일하기 때문에 이를 방지합니다!
중괄호를 사용하여 변경 가능한 참조를 여러 개 가질 수 있도록 새 범위를 만들 수 있지만 "동시에"는 허용하지 않습니다:
let mut s = String::from("hello");
{
let r1 = &mut s;
} // r1 여기에서는 범위를 벗어나므로 새 참조를 만들 수 있습니다.
let r2 = &mut s;
불변 참조를 다시 사용하면 어떤 문제가 발생하나요?
let mut s = String::from("Hello");
let r1 = &s; // 변경 불가능한 참조
let r2 = &s; // 변경 불가능한 참조
let r3 = &mut s; // 변경 가능한 참조
println!("{}, {}, {}", r1, r2, r3);
오류는 다음과 같습니다:
|
101 | let r1 = &s; // 변경 불가능한 참조
| -- immutable borrow occurs here
102 | let r2 = &s; // 변경 불가능한 참조
103 | let r3 = &mut s; // 변경 가능한 참조
| ^^^^^^ mutable borrow occurs here
104 |
105 | println!("{}, {}, {}", r1, r2, r3);
| -- immutable borrow later used here
변경 가능한 참조를 가지면서 불변 참조를 가질 수도 없습니다! 여러 개의 불변 참조는 괜찮습니다!
let mut s = String::from("hello");
let r1 = &s; // 변경 불가능한 참조
let r2 = &s; // 변경 불가능한 참조
println!("{} and {}", r1, r2);
// 이 위치 이후에는 r1과 r2가 더 이상 사용되지 않습니다.
let r3 = &mut s; // 변경 가능한 참조
println!("{}", r3);
참조의 범위는 선언된 시점부터 마지막으로 사용될 때까지 지속됩니다. 따라서 위의 코드는 컴파일할 수 있습니다.
댕글링 참조
포인터를 사용하는 언어에서는 메모리를 해제할 때 포인터를 유지하여 매달린 포인터를 잘못 생성하기 쉽습니다.
댕글링 포인터는 이미 다른 소유자에게 할당되었을 수 있는 메모리를 가리키는 참조입니다. Rust에서 컴파일러는 참조에 댕글링 포인터가 발생하지 않도록 보장합니다.
fn main() {
let value = dangling_pointer();
}
fn dangling_pointer() -> &String {
let s = String::from("hello");
&s
}
다음 오류가 발생합니다:
|
101 | fn dangling_pointer() -> &String {
| ^ expected named lifetime parameter
|
= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
help: consider using the `'static` lifetime
|
101 | fn dangling_pointer() -> &'static String {
이 오류는 dangling_pointer 함수가 찾을 수 없는 변경 불가능한 참조를 반환했음을 나타냅니다.
왜 그럴까요?
fn main() {
let value = dangling_pointer();
}
fn dangling_pointer() -> &String {
let s = String::from("hello"); // 댕글링된 변수_pointer 메서드를 생성하는 메서드는
&s // 문자열 s에 대한 변경 불가능한 참조를 반환합니다.
} // 여기서 범위가 끝나면 변수 s가 삭제되고 해당 메모리가 해제됩니다.
변수 s는 dangling_pointer 메서드에서 생성되었으므로 해당 메서드의 실행이 완료되면 변수 s도 해제됩니다. 그러면 잘못된 문자열을 가리키는 참조가 반환됩니다!
해결책은 그냥 문자열을 반환하는 것입니다.
fn dangling_pointer() -> String {
let s = String::from("hello");
s
}
인용문에 대한 요약은 다음과 같습니다:
- 가변 참조와 불변 참조는 동시에 존재할 수 없으며, 동일한 데이터에 대한 가변 참조를 여러 개 만들 수 없다는 점에 유의하세요.
- 참조는 항상 유효하며, 유효하지 않은 참조는 매달린 참조를 유발합니다.





