카테고리 없음

Rust의 소유권(Ownership) 완전 정복

mystory55801 2025. 5. 29. 04:43

Rust의 소유권(Ownership)이란 무엇인가?

Rust는 다른 프로그래밍 언어와는 달리 가비지 컬렉터(GC) 없이도 메모리 안전성을 보장하는 독특한 방식을 채택하고 있습니다. 그 중심에는 바로 소유권(Ownership)이라는 개념이 있습니다. Rust를 제대로 이해하고 쓰기 위해서는 반드시 소유권 시스템을 정확히 알아야 합니다.

이 글에서는 Rust 소유권의 기본 원칙부터 참조, 빌림, 라이프타임까지 입문자도 이해할 수 있도록 단계적으로 설명합니다.

소유권의 기본 원칙

Rust의 소유권은 세 가지 기본 규칙에 따라 동작합니다:

  1. Rust의 모든 값은 소유자를 가진다.
  2. 한 번에 하나의 소유자만 존재할 수 있다.
  3. 소유자가 범위를 벗어나면, 값은 메모리에서 해제된다.

이 규칙 덕분에 Rust는 메모리를 자동으로 해제할 수 있으며, 이 과정에서 GC가 필요하지 않습니다.

이동(Move) 개념 이해하기

Rust에서는 값이 변수에 할당될 때, 그 값의 소유권이 "이동(Move)"합니다. 예제를 통해 확인해보세요.


let s1 = String::from("hello");
let s2 = s1;

위 코드에서 s1의 소유권은 s2로 이동하며, s1은 더 이상 사용할 수 없습니다. 이를 컴파일러가 오류로 처리합니다.


// 오류: s1은 더 이상 유효하지 않음
println!("{}", s1);

Clone을 통한 값 복제

만약 s1을 계속 사용하고 싶다면, clone() 메서드를 사용해 데이터를 복제해야 합니다.


let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1: {}, s2: {}", s1, s2);

함수와 소유권

함수에 값을 전달하면 소유권이 함수로 이동할 수 있습니다. 함수 호출 후 변수는 더 이상 유효하지 않을 수 있습니다.


fn take_ownership(s: String) {
    println!("{}", s);
}

fn main() {
    let s = String::from("Rust");
    take_ownership(s); // 여기서 소유권 이동
    // println!("{}", s); // 오류 발생
}

빌림(Borrowing)과 참조(Reference)

Rust에서는 소유권을 넘기지 않고 값에 접근하기 위해 참조를 사용할 수 있습니다. 이를 빌림(Borrowing)이라고 합니다.


fn print_length(s: &String) {
    println!("Length is {}", s.len());
}

fn main() {
    let s = String::from("hello");
    print_length(&s);
    println!("s는 여전히 유효합니다: {}", s);
}

& 기호는 참조를 의미하며, 소유권을 이동하지 않고 값을 읽을 수 있게 해줍니다.

가변 참조(Mutable Reference)

가변 참조를 사용하면 참조를 통해 값을 수정할 수 있습니다. 하지만 Rust는 동시에 하나의 가변 참조만 허용합니다.


fn change(s: &mut String) {
    s.push_str(", world!");
}

fn main() {
    let mut s = String::from("Hello");
    change(&mut s);
    println!("{}", s);
}

Rust는 데이터 경쟁 조건(data race)을 방지하기 위해 이러한 규칙을 강제합니다. 즉, 동시에 여러 개의 가변 참조를 허용하지 않음으로써 안전한 병행성을 보장합니다.

라이프타임(Lifetime) 개요

참조가 유효한 범위를 명확히 하기 위해 Rust는 라이프타임이라는 개념을 도입했습니다. 대부분의 경우 Rust 컴파일러가 자동으로 처리하지만, 복잡한 상황에서는 명시적으로 라이프타임을 지정해야 합니다.


fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

이 함수는 두 문자열 중 긴 문자열을 반환하며, 그 결과의 라이프타임은 입력 중 짧은 쪽에 종속됩니다. 이렇게 명시적인 라이프타임 지정은 컴파일 타임에 오류를 방지하는 데 매우 유용합니다.

결론: Rust 소유권 개념은 안전한 코드의 핵심

Rust의 소유권 시스템은 단순히 메모리를 자동으로 해제하는 것을 넘어서, 데이터 무결성과 동시성 안전성까지 보장하는 강력한 설계입니다. 처음에는 낯설고 어려울 수 있지만, 소유권 개념을 이해하고 익히면 Rust는 매우 강력하고 안정적인 언어가 됩니다.

Rust를 배우는 데 있어 소유권은 가장 중요한 기초 중 하나입니다. 앞으로 조건문, 반복문, 구조체와 열거형 등 고급 주제로 넘어가기 전, 이 개념을 충분히 연습하고 익히는 것이 좋습니다.