또 다른 Rust 소유권 튜토리얼
Source: Dev.to
Rust에서 가장 중요한 개념 중 하나는 소유권(ownership) 과 빌리기(borrowing) 입니다. 이 주제에만 전념한 글이 수없이 많으며, 이 튜토리얼은 예시를 통해 개념을 설명하려고 합니다.
소유권 은 Rust 프로그램이 메모리를 관리하는 방식을 규정하는 일련의 규칙입니다. Rust는 컴파일 시점에 소유권 시스템을 검사합니다; 규칙이 위반되면 프로그램은 컴파일되지 않으며, 런타임 오버헤드도 없습니다.
소유권의 첫 맛보기
Java 버전
public void own(String text) {}
public static void main(String[] args) {
var text = "my text";
own(text);
System.out.println(text);
}
Rust 번역
fn own(_: String) {}
fn main() {
let text: String = String::from("my text"); // 1
own(text);
println!("{}", text);
}
컴파일러가 불평합니다:
error[E0382]: borrow of moved value: `text`
--> src/main.rs:11:20
|
9 | let text: String = String::from("my text");
| ---- move occurs because `text` has type `String`, which does not implement the `Copy` trait
10| own(text);
| ---- value moved here
11| println!("{}", text);
| ^^^^ value borrowed here after move
...
help: consider cloning the value if the performance cost is acceptable
10| own(text.clone());
own 함수는 text 의 소유권을 가져가므로, 이후에 text 를 사용할 수 없습니다.
Clone 과 Copy
컴파일러의 조언을 따라 보면:
fn own(text: String) {}
fn main() {
let text = String::from("my text");
own(text.clone()); // 깊은 복사
println!("{}", text);
}
String 은 Clone 을 구현하고 있어 깊은 복사를 수행합니다. 이는 값에 대해 메모리를 두 배로 사용한다는 의미입니다.
타입이 Copy 도 구현한다면, 값이 암시적으로 복제되며 clone() 을 호출할 필요가 없습니다:
#[derive(Debug, Copy, Clone)]
struct Dummy {}
fn own(_: Dummy) {}
fn main() {
let dummy = Dummy {};
own(dummy); // 자동으로 복사됨
println!("{:?}", dummy); // 여전히 사용 가능
}
Note: 큰 구조체에
Copy를 구현하면 모든 이동이 복사를 일으키기 때문에 메모리를 많이 차지할 수 있습니다.
참조로 전달하기
값을 전달하면 소유권이 이동합니다. 함수가 값을 빌리도록 하려면 & 로 참조를 전달합니다:
#[derive(Debug)]
struct Dummy {}
fn borrow(_: &Dummy) {}
fn main() {
let dummy = Dummy {};
borrow(&dummy); // 빌림, 이동 아님
println!("{:?}", dummy); // 여전히 사용 가능
}
참조를 통해 필드 읽기:
#[derive(Debug)]
struct Dummy {
foo: String,
}
fn borrow(dummy: &Dummy) {
println!("{:?}", dummy.foo);
}
fn main() {
let dummy = Dummy { foo: String::from("Foo") };
borrow(&dummy);
println!("{:?}", dummy); // Dummy { foo: "Foo" }
}
데이터 변경하기
mut 없이 변경 시도
#[derive(Debug)]
struct Dummy {
foo: String,
}
fn main() {
let dummy = Dummy { foo: String::from("Foo") };
dummy.foo = String::from("Bar"); // error
}
컴파일러 오류:
error[E0594]: cannot assign to `dummy.foo`, as `dummy` is not declared as mutable
--> src/main.rs:8:5
|
8 | dummy.foo = String::from("Bar");
| ^^^^^^^^^ cannot assign
|
help: consider changing this to be mutable
7 | let mut dummy = Dummy { foo: String::from("Foo") };
변수를 mutable 로 만들기
#[derive(Debug)]
struct Dummy {
foo: String,
}
fn main() {
let mut dummy = Dummy { foo: String::from("Foo") };
dummy.foo = String::from("Bar");
}
값으로 전달된 매개변수 변경하기
#[derive(Debug)]
struct Dummy {
foo: String,
}
fn mutate(mut dummy: Dummy) {
dummy.foo = String::from("Bar");
println!("{:?}", dummy.foo);
}
fn main() {
let dummy = Dummy { foo: String::from("Foo") };
mutate(dummy);
}
가변 참조를 통한 변경
#[derive(Debug)]
struct Dummy {
foo: String,
}
fn mutate(dummy: &mut Dummy) {
dummy.foo = String::from("Bar");
println!("Inside: {:?}", dummy.foo);
}
fn main() {
let mut dummy = Dummy { foo: String::from("Foo") };
mutate(&mut dummy);
println!("Outside: {:?}", dummy.foo);
}
요약
| 목표 | 달성 방법 |
|---|---|
| 소유권 가져오기 | 값으로 전달하기 |
| 값 복제하기 | Clone 구현하고 clone() 호출 |
| 값 자동 복제하기 | Copy 구현 (Clone 포함) |
| 빌리기 (읽기 전용) | &T 형태의 참조 전달 |
| 로컬 변수 변경하기 | 변수 선언 시 mut 지정 |
| 참조를 통한 변경 | &mut T 사용하고 바인딩을 mut 선언 |
원본은 A Java Geek에 2025년 12월 7일에 게시되었습니다.