러스트의 참조자 (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 |