홈강좌Rust 입문·중급 47편 중 16편째
Rust clone 과 Copy — 복사해서 주인을 둘로 만든다
Rust 에서 String 하나를 두 변수가 함께 쓰려면 .clone() 으로 값을 복사한다. 정수·실수·bool·char 처럼 작은 값은 Copy 타입이라, let y = x; 를 해도 이동하지 않고 저절로 복사된다. String 이 하나라도 든 튜플은 Copy 가 아니어서 이동한다.
01 clone 으로 복사한다
15편에서 컴파일러가 권한 대로 .clone() 을 붙여 보자.
fn main() {
let a = String::from("hello");
let b = a.clone();
println!("a = {a}, b = {b}");
} Compiling copy v0.1.0 (~/projects/copy)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.17s
Running `target/debug/copy`
a = hello, b = helloa.clone() 은 a 의 문자열을 통째로 복사한 새 값을 만든다. b 는 그 새 값의 주인이다. 값이 둘이고 주인도 둘이니 규칙(주인은 하나)을 어기지 않는다. 둘 중 하나를 고쳐도 다른 쪽은 그대로다.
복사에는 대가가 있다. 문자열이 길면 그만큼 메모리를 새로 잡고 글자를 옮겨 적는다. 15편 오류의 help 가 "성능 비용을 감수할 수 있다면(if the performance cost is acceptable)" 이라고 단서를 단 이유다. 복사 없이 함께 쓰는 법은 18편의 참조다.
02 숫자는 저절로 복사된다
그런데 3편에서 let 으로 숫자를 이리저리 넣을 때는 아무 문제가 없었다.
fn main() {
let x = 5;
let y = x;
println!("x = {x}, y = {y}");
let point = (3, 4);
let other = point;
println!("{point:?} {other:?}");
} Compiling copy v0.1.0 (~/projects/copy)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.17s
Running `target/debug/copy`
x = 5, y = 5
(3, 4) (3, 4)let y = x; 뒤에도 x 를 쓸 수 있다. 정수는 Copy 타입이라서 이동하는 대신 저절로 복사되기 때문이다. 정수 하나는 크기가 작고 정해져 있어서 복사하는 비용이 거의 없다. 그래서 Rust 는 굳이 이동시키지 않는다.
Copy 다 (저절로 복사) | Copy 가 아니다 (이동) |
|---|---|
정수 · 실수 · bool · char | String |
Copy 타입만 든 튜플·배열 — (3, 4) | String 이 든 튜플 — (1, String) |
두 번째 줄의 (3, 4) 도 정수 둘짜리 튜플이라 Copy 다. {point:?} 는 9편에서 본 대로 통째로 찍는 표시다.
03 실습 — String 이 든 튜플은 이동한다
튜플에 String 을 하나 넣으면 어떻게 될까. 돌리기 전에 짐작해 보자.
fn main() {
let item = (1, String::from("apple"));
let other = item;
println!("{item:?} {other:?}");
}error[E0382]: borrow of moved value: `item`
--> src/main.rs:4:16
|
2 | let item = (1, String::from("apple"));
| ---- move occurs because `item` has type `(i32, String)`, which does not implement the `Copy` trait
3 | let other = item;
| ---- value moved here
4 | println!("{item:?} {other:?}");
| ^^^^ value borrowed here after move
|
help: consider cloning the value if the performance cost is acceptable
|
3 | let other = item.clone();
| ++++++++15편과 같은 E0382 다. 2번째 줄의 밑줄이 이유를 말한다. item 의 타입이 (i32, String) 이고, 이 타입은 Copy 를 갖추지 않았다(does not implement the Copy trait). 정수는 복사할 수 있어도 String 이 끼어 있으니 튜플 전체가 이동한다.
고치는 법은 두 가지다. help: 대로 item.clone() 으로 통째로 복사하거나, 두 변수를 다 쓸 필요가 없다면 other 만 쓴다. 다음 편에서는 함수에 값을 넘길 때도 이동이 일어난다는 것을 본다.