러스트의 참조자 (References) 와 빌림 (Borrowing)이란?
러스트의 소유권 개념은 러스트 소유권 글 링크 를 참조하여 이해하도록 하자.
- 참조자와 빌림은 소유권을 개념을 바탕으로 이해해야 한다.
&기호를 통해 소유권을 단순히 참조하며 빌린다는 표현을 쓸 수 있다.
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("The length of '{}' is {}.", s1, len);
}
fn calculate_length(s: &String) -> usize {
s.len()
}
위의 코드에서 본래 이전에 배웠던 소유권 개념을 통해 이해하면 s1 의 소유권은 calculate_length 라는 함수에 넘어가서 더 이상 사용할 수 없어야 하나, 참조자(&)를 통해 인자를 넘기면 소유권을 완벽히 넘기는 것이 아니고 잠시 함수가 끝날 때까지 소유권을 빌려줄 수 있다.

위의 코드와 같이 함수의 파라미터로 참조자를 이용하는 것을 '빌림' 이라 한다.
참조자로 빌린 값을 변경하려 한다면?
fn main() {
let s = String::from("hello");
change(&s);
}
fn change(some_string: &String) {
some_string.push_str(", world");
}
error: cannot borrow immutable borrowed content `*some_string` as mutable
--> error.rs:8:5
|
8 | some_string.push_str(", world");
| ^^^^^^^^^^^
우리가 빌린 값은 mut 키워드가 붙지 않은 immutable 한 값이다. 그렇기에 변경하려 하면 에러가 난다.
가변 참조자 (Mutable References)
- 참조자로 빌려온 값을 무조건 수정하지 못하는 것은 아니다.
&mut기호를 사용하는 가변 참조자를 이용하면 수정이 가능하다.
fn main() {
let mut s = String::from("hello");
change(&mut s);
println!("the value of s is {}", s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
위의 코드는 가변 참조자를 사용했기에 문제 없이 "the value of s is hello, world" 라는 문자열이 잘 출력된다.
댕글링 참조자 (Dangling References)
포인터가 있는 언어에서는 댕글링 포인터 (Dangling Pointer) 라는 용어가 종종 쓰이는데, 여러 포인터가 같은 메모리 주소를 참조하다, 참조하는 포인터 중 하나가 다른 포인터가 아직 사용할지 모르는 해당 메모리 영역을 해제하여 해제된 메모리 주소를 참조하고 있는 포인터를 말한다.
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() -> &String {
let s = String::from("hello");
&s
}
위 코드가 정상적으로 실행된다면, dangle() 함수는 종료되며 s 의 메모리 영역을 해제한다. 반환된 &s 는 해제된 메모리 영역을 가리키게 된다. 하지만 러스트에서는 이렇게 댕글링 참조자를 만드는 코드를 오류 메세지를 통해 알려주어 미연에 오류를 방지한다.
error[E0106]: missing lifetime specifier
--> dangle.rs:5:16
|
5 | fn dangle() -> &String {
| ^^^^^^^
|
= help: this function's return type contains a borrowed value, but there is no
value for it to be borrowed from
= help: consider giving it a 'static lifetime
error: aborting due to previous error
도움 메세지에서 리턴 값이 빌려온 값을 포함하는데, 빌려온 값이 비어있다고 알려주고 있다. 또한
static lifetime을 주는 것을 고려해보라고 하고 있는데, 이는 나중에 라이프타임 섹션에서 배우게 되니 서두를 필요 없다.
해석 주석이 함께 달린 코드
fn dangle() -> &String { // dangle은 String의 참조자를 반환합니다
let s = String::from("hello"); // s는 새로운 String입니다
&s // 우리는 String s의 참조자를 반환합니다.
} // 여기서 s는 스코프를 벗어나고 버려집니다. 이것의 메모리는 사라집니다.
// 위험하군요!
댕글링 포인터가 생기지 않도록, 참조자가 아닌 소유권을 직접 반환하기
fn no_dangle() -> String {
let s = String::from("hello");
s
}
문제 없이 동작한다.
참조자의 규칙
하나의 가변 참조자혹은임의 개수의 불변 참조자들중 하나만 가질 수 있다.- 참조자는 항상 유효해야 한다.
- 댕글링 참조자가 발생하는 코드는 에러가 난다.
레퍼런스
https://rinthel.github.io/rust-lang-book-ko/ch04-02-references-and-borrowing.html
'러스트 (Rust)' 카테고리의 다른 글
| 러스트 (Rust) 구조체 정의하고 생성하는 방법 (0) | 2022.11.03 |
|---|---|
| 러스트 (Rust) 의 슬라이스 (Slice) 개념 (0) | 2022.11.02 |
| 러스트 (Rust) 의 소유권 (ownership) 개념 (0) | 2022.11.02 |
| 러스트 (Rust) 의 반복문 정리 (0) | 2022.11.01 |
| 러스트 (Rust) 의 제어문 문법 정리 (0) | 2022.11.01 |