Rust 기초부터 마스터까지 — 1화
소유권과 빌림으로 메모리를 안전하게 다루기
본 블로그 작성 글들은, 강의를 목표로 하고 제작되었습니다. 또한 작성된 글의 정보도 틀렸을 수 있으니, 주의해주세요.
Rust는 Garbage Collector 없이 메모리 안전성을 지키는 것을 목표로 하는 시스템 프로그래밍 언어다. 성능이 중요한 프로그램을 만들면서 잘못된 포인터와 데이터 경쟁을 컴파일 단계에서 줄이려 한다.
값에는 소유자가 있다
Rust의 값은 기본적으로 하나의 소유자를 가진다. 소유자가 유효 범위를 벗어나면 값이 자동으로 정리된다.
fn main() {
let name = String::from("Rose");
print_name(name);
// name의 소유권은 함수로 이동했다.
}
fn print_name(value: String) {
println!("{value}");
}
String을 함수에 값으로 전달하면 소유권이 이동한다. 호출한 쪽에서도 계속 사용하고 싶다면 값을 빌려준다.
참조로 값을 빌린다
fn main() {
let name = String::from("Rose");
print_name(&name);
println!("{name}");
}
fn print_name(value: &String) {
println!("{value}");
}
&String은 값을 소유하지 않고 잠시 읽는 참조다. 값을 변경하려면 &mut String처럼 변경 가능한 참조를 사용해야 한다.
같은 시점에 변경 가능한 참조를 여러 개 만들지 못하게 하는 규칙은 처음에는 까다롭다. 대신 실행 중에 생길 수 있는 잘못된 메모리 접근과 경쟁 상태를 미리 막아준다.
실패 가능성을 타입으로 표현한다
Rust는 값이 없을 수 있으면 Option<T>, 작업이 실패할 수 있으면 Result<T, E>를 자주 사용한다.
fn divide(a: i32, b: i32) -> Result<i32, String> {
if b == 0 {
return Err(String::from("0으로 나눌 수 없습니다."));
}
Ok(a / b)
}
호출한 코드는 성공과 실패를 명시적으로 처리해야 한다. 오류를 무시하기 어렵게 만드는 것도 Rust의 안전성에 포함된다.
Rust 프로젝트는 cargo new, cargo run, cargo test 같은 Cargo 명령으로 관리한다. 처음에는 소유권 규칙을 전부 외우기보다 작은 명령줄 프로그램을 만들고 컴파일러 메시지를 따라가며 빌림의 범위를 확인하는 것이 좋다.