홈강좌Rust 입문·중급 47편 중 15편째
Rust 소유권과 이동 — 값의 주인은 하나다 (E0382)
Rust 의 값에는 주인(변수)이 하나뿐이다. let b = a; 처럼 String 을 다른 변수에 넣으면 주인이 b 로 옮겨 가고(이동, move), 그 뒤에 a 를 쓰면 E0382 로 컴파일이 멈춘다. 주인이 블록을 벗어나면 값은 저절로 치워진다.
01 규칙은 셋이다
Rust 에는 가비지 컬렉터가 없다. 대신 아래 세 규칙을 컴파일러가 따져서, 메모리를 언제 치울지 정한다.
- 값마다 주인(owner)인 변수가 있다.
- 주인은 한 번에 하나뿐이다.
- 주인이 블록을 벗어나면 값은 치워진다.
이 편부터는 숫자 대신 String 으로 예를 든다. String::from("hello") 는 늘이거나 고칠 수 있는 문자열을 만든다(자세한 것은 20편). 숫자와 달리 String 은 이 규칙이 눈에 보이게 드러난다. 숫자가 왜 다른지는 다음 편에서 본다.
02 다른 변수에 넣으면 주인이 바뀐다
fn main() {
let a = String::from("hello");
let b = a;
println!("{a} {b}");
} Compiling owner v0.1.0 (~/projects/owner)
error[E0382]: borrow of moved value: `a`
--> src/main.rs:4:16
|
2 | let a = String::from("hello");
| - move occurs because `a` has type `String`, which does not implement the `Copy` trait
3 | let b = a;
| - value moved here
4 | println!("{a} {b}");
| ^ value borrowed here after move
|
help: consider cloning the value if the performance cost is acceptable
|
3 | let b = a.clone();
| ++++++++
For more information about this error, try `rustc --explain E0382`.
error: could not compile `owner` (bin "owner") due to 1 previous error- 무엇 — 이동한 값(moved value)
a를 썼다. - 어디 — 밑줄 셋이 차례로 이야기를 들려준다. 2번째 줄에서
a가String이라 이동하는 타입이고, 3번째 줄에서 값이b로 옮겨 갔고(value moved here), 4번째 줄에서 옮겨 간 뒤에a를 썼다. - 고치는 법 —
help:가.clone()으로 복사하라고 한다. 복사는 다음 편에서 다룬다.
let b = a; 뒤로 문자열 "hello" 의 주인은 b 하나다. a 는 더는 아무것도 갖고 있지 않다. 그래서 b 만 쓰면 문제없다.
fn main() {
let a = String::from("hello");
let b = a;
println!("{b}");
} Compiling owner v0.1.0 (~/projects/owner)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.20s
Running `target/debug/owner`
hello주인이 둘이면 둘 다 블록을 벗어날 때 같은 값을 두 번 치우려 한다. 이동은 그런 일을 처음부터 막는다.
03 주인이 블록을 나가면 값은 치워진다
규칙 3 을 보자. 블록 안에서 만든 note 를 블록 밖에서 써 본다.
fn main() {
{
let note = String::from("inside");
println!("{note}");
}
println!("{note}");
}error[E0425]: cannot find value `note` in this scope
--> src/main.rs:6:16
|
6 | println!("{note}");
| ^^^^
|
help: the binding `note` is available in a different scope in the same function
--> src/main.rs:3:13
|
3 | let note = String::from("inside");
| ^^^^무엇 줄이 "이 범위(scope)에서 note 를 찾을 수 없다" 고 말하고, help: 가 "note 는 같은 함수의 다른 범위에 있다" 며 3번째 줄을 짚는다. note 는 블록이 끝나는 5번째 줄의 } 에서 사라졌고, 그 순간 문자열도 치워졌다. 치우는 코드를 따로 쓰지 않아도 된다.
04 실습 — 이동한 변수에 새 값 넣기
이동한 변수는 비어 있을 뿐 없어진 것이 아니다. mut 라면 새 값을 넣어 다시 쓸 수 있다.
fn main() {
let mut a = String::from("first");
let b = a;
a = String::from("second");
println!("a = {a}, b = {b}");
} Compiling owner v0.1.0 (~/projects/owner)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.17s
Running `target/debug/owner`
a = second, b = first"first" 는 b 로 옮겨 갔고, 빈 a 에는 새로 만든 "second" 가 들어갔다. 주인은 여전히 값마다 하나다. a = String::from("second"); 줄을 지우고 돌려 보면 처음과 같은 E0382 가 난다.
"그럼 a 와 b 를 둘 다 쓰고 싶으면?" 이 다음 질문이다. 다음 편에서 값을 복사하는 clone 과, 숫자가 이동하지 않는 이유인 Copy 를 다룬다.