Rust에서는 데이터 경합과 dangling 참조를 방지하기 위해 빌림 규칙을 따른다.
아래의 코드는 하나의 가변 참조를 만들고, 이어서 불변 참조(가변 참조도 마찬가지)를 또 만들려고 했기 때문에 에러가 발생한다.
fn main() {
let mut vec_1 = vec![4,5,6];
let ref1 = &mut vec_1;
let ref2 = &vec_1;
println!("ref1: {:?}, ref2: {:?}", ref1, ref2);
}
Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `vec_1` as immutable because it is also borrowed as mutable
--> src/main.rs:4:16
|
3 | let ref1 = &mut vec_1;
| ---------- mutable borrow occurs here
4 | let ref2 = &vec_1;
| ^^^^^^ immutable borrow occurs here
5 | println!("ref1: {:?}, ref2: {:?}", ref1, ref2);
| ---- mutable borrow later used here
For more information about this error, try `rustc --explain E0502`.
error: could not compile `playground` (bin "playground") due to 1 previous error
하지만 위의 코드에서 println!()을 지우면 (혹은 ref1에 대한 출력만 지워줘도) 성공적으로 컴파일된다. 왜일까? 러스트 컴파일러는 내부적으로 참조의 scope를 계산하는데, 이 scope는 참조가 발생한 줄 부터 사용된 마지막 줄까지를 의미한다. 첫 번째 가변 참조인 ref1이 생성된 이후에 사용되지 않았기 때문에 ref1에 대한 참조 scope가 해당 줄에서 끝났고, 때문에 두번째 생성된 참조인 ref2의 scope와 겹치지 않는다. 따라서 문제없이 컴파일되는 것이다. 참조 관련 이슈는 참조 scope로 판단하면 편하다.
println!()을 다시 넣으면 ref1에 대한 참조 scope와 ref2에 대한 참조 scope가 겹치기 때문에 컴파일 에러가 발생한다.
아래 코드는 여러개의 불변 참조를 생성하는 예시로, 문제 없이 컴파일 된다.
fn main() {
let mut vec_1 = vec![4,5,6];
let ref1 = &vec_1;
let ref2 = &vec_1;
println!("ref1: {:?}, ref2: {:?}", ref1, ref2);
}
하지만 아래 코드는 불변 참조 중에 가변 참조를 생성하려 했기 때문에 컴파일 되지 않는다. 참조 scope 관점으로 얘기하자면 ref1과 ref2의 참조 scope가 가변 참조인 ref3의 scope와 겹치기 때문에 에러가 발생한다.
fn main() {
let mut vec_1 = vec![4,5,6];
let ref1 = &vec_1;
let ref2 = &vec_1;
let ref3 = &mut vec_1;
println!("ref1: {:?}, ref2: {:?}, ref3: {:?}", ref1, ref2, ref3);
}
Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `vec_1` as mutable because it is also borrowed as immutable
--> src/main.rs:5:16
|
3 | let ref1 = &vec_1;
| ------ immutable borrow occurs here
4 | let ref2 = &vec_1;
5 | let ref3 = &mut vec_1;
| ^^^^^^^^^^ mutable borrow occurs here
6 | println!("ref1: {:?}, ref2: {:?}, ref3: {:?}", ref1, ref2, ref3);
| ---- immutable borrow later used here
For more information about this error, try `rustc --explain E0502`.
error: could not compile `playground` (bin "playground") due to 1 previous error
당연하게도 아래와 같이 수정하면 문제없이 컴파일된다. 각 참조의 scope가 겹치지 않기 때문
fn main() {
let mut vec_1 = vec![4,5,6];
let ref1 = &vec_1;
let ref2 = &vec_1;
println!("ref1: {:?}, ref2: {:?}", ref1, ref2);
let ref3 = &mut vec_1;
}
보통 여러개의 데이터 참조가 있을때 데이터 경합이 발생하는데(가령 여러개 중 하나가 데이터들 업데이트 했을 때), rust는 이러한 빌림 규칙에 의해 경합을 방지할 수 있다.
아래의 코드를 실행하면 컴파일 에러가 발생한다.
fn main() {
let vec1 = {
let vec2 = vec![1,2,3];
&vec2
};
}
Compiling playground v0.0.1 (/playground)
error[E0597]: `vec2` does not live long enough
--> src/main.rs:4:6
|
2 | let vec1 = {
| ---- borrow later stored here
3 | let vec2 = vec![1,2,3];
| ---- binding `vec2` declared here
4 | &vec2
| ^^^^^ borrowed value does not live long enough
5 | };
| - `vec2` dropped here while still borrowed
For more information about this error, try `rustc --explain E0597`.
warning: `playground` (bin "playground") generated 1 warning
error: could not compile `playground` (bin "playground") due to 1 previous error; 1 warning emitted
이유는 vec1의 선언 블록이 끝나는 시점에 vec2가 drop(힙에서 메모리 해제) 되는데, 반환값으로 drop된 vec2의 참조를 vec1에 반환하려 했기 때문이다. rust는 언어 자체적으로 dangling 참조 문제를 해결한다.
함수의 반환 값으로 참조를 반환하지 말고 소유값 자체를 반환하자. 왜냐면 참조 대상이 함수 끝나는 시점에 drop될 여지가 있기 때문에..