Rust 소유권 시스템과 메모리 안정성
배경
최근 Node.js로 작성된 데이터 처리 서버에서 메모리 누수 문제가 발생했다. 대용량 파일을 스트림 처리하는 과정에서 이벤트 리스너가 제대로 해제되지 않아 메모리가 계속 증가하는 이슈였다. 이를 해결하면서 GC에 의존하지 않고 메모리를 관리하는 방식에 관심이 생겨 Rust를 살펴보게 되었다.
Ownership 개념
Rust의 가장 큰 특징은 소유권(Ownership) 시스템이다. 각 값은 정확히 하나의 소유자를 가지며, 소유자가 스코프를 벗어나면 자동으로 메모리가 해제된다.
fn main() {
let s1 = String::from("hello");
let s2 = s1; // s1의 소유권이 s2로 이동
// println!("{}", s1); // 컴파일 에러
println!("{}", s2); // 정상 동작
}
소유권 이동(move) 후에는 이전 변수를 사용할 수 없다. 이 규칙을 컴파일 타임에 강제하기 때문에 런타임에서 발생할 수 있는 메모리 에러를 원천적으로 차단한다.
Borrowing과 References
값을 이동시키지 않고 참조만 빌려쓰려면 borrowing을 사용한다.
fn calculate_length(s: &String) -> usize {
s.len()
}
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("{}, {}", s1, len); // s1 여전히 사용 가능
}
불변 참조는 여러 개 가질 수 있지만, 가변 참조는 한 번에 하나만 허용된다. 이 규칙 덕분에 데이터 레이스가 컴파일 타임에 방지된다.
실무 적용 가능성
당장 프로덕션에 적용하기는 어렵지만, 시스템 레벨 도구나 성능이 중요한 CLI 도구를 만들 때 고려해볼 만하다. 러닝 커브가 가파르지만 메모리 안정성을 보장받으면서도 C++ 수준의 성능을 낼 수 있다는 점이 매력적이다.
개인적으로는 작은 프로젝트부터 시작해서 감을 익혀볼 계획이다.