본문 바로가기

언어/Rust

[ Rust ] 댕글링 포인터(dangling pointer)에 대해서 알아보기

반응형

러스트(Rust)에서 댕글링 포인터(dangling pointer)는 발생하지 않도록 설계되어 있습니다. 댕글링 포인터는 C나 C++와 같은 언어에서 흔히 발생하는 메모리 안전성 문제로, 특정 메모리 주소를 가리키고 있지만 해당 메모리가 더 이상 유효하지 않은 경우를 말합니다. 이는 주로 객체가 해제된 후에도 포인터가 그 메모리 위치를 참조할 때 발생합니다.

Rust에서는 이러한 댕글링 포인터 문제를 컴파일 타임에 원천적으로 방지하는 메커니즘을 가지고 있습니다. 그 핵심이 바로 소유권(ownership)차용(borrowing)입니다.

Rust의 소유권 시스템

Rust의 소유권 시스템은 세 가지 규칙으로 구성됩니다:

  1. 각 값은 하나의 소유자(owner)만 가질 수 있습니다.
  2. 소유자가 스코프(scope)를 벗어나면, 그 값은 자동으로 메모리에서 해제(drop)됩니다.
  3. 값의 소유권을 이동하지 않고 참조(borrow)할 수 있습니다. 참조는 두 가지로 나뉩니다:
    • 불변 참조(&): 값을 변경할 수 없는 참조.
    • 가변 참조(&mut): 값을 변경할 수 있는 참조. 하지만 동시에 여러 가변 참조를 허용하지 않습니다.

댕글링 포인터 방지

Rust는 이 소유권 규칙을 통해 댕글링 포인터를 방지합니다. 예를 들어, 아래와 같은 상황에서 다른 언어에서는 댕글링 포인터 문제가 발생할 수 있지만, Rust는 이를 컴파일 단계에서 차단합니다.

예시: 댕글링 포인터 발생 가능 상황 (C/C++에서)

int* dangling() {
    int x = 42;
    return &x;  // 'x'는 함수가 끝나면서 사라짐 -> 댕글링 포인터
}

위의 C 코드에서는 x가 함수 dangling의 실행이 끝난 후 사라지므로, x의 주소를 반환하면 댕글링 포인터가 됩니다. 이 포인터를 참조하면 정의되지 않은 동작이 발생할 수 있습니다.

Rust에서 동일한 상황

fn dangling() -> &i32 {
    let x = 42;
    &x  // 컴파일 오류: `x`는 함수가 끝나면 사라짐
}

이 코드는 컴파일되지 않습니다. Rust는 x의 참조가 함수 dangling의 반환값으로 사용될 수 없음을 감지합니다. 이유는 x가 함수의 스코프를 벗어나면 사라지기 때문에, 이를 참조하는 값이 더 이상 유효하지 않기 때문입니다. Rust의 컴파일러는 이를 감지하고, "댕글링 포인터" 문제가 발생하지 않도록 코드를 수정하도록 강제합니다.

Rust의 해결 방법

Rust에서 댕글링 포인터를 방지하는 방법은 참조의 수명(lifetime)소유권 이동(move)입니다.

  1. 참조의 수명(lifetime):
    • Rust는 참조의 유효 기간을 추적하여 참조가 항상 유효한 값을 가리키도록 보장합니다. 참조가 수명이 끝난 값을 가리키는 상황이 발생할 경우 컴파일러가 이를 잡아내고 컴파일 에러를 발생시킵니다.
  2. 소유권 이동(move):
    • 변수를 다른 변수에 할당하면 소유권이 이동합니다. 소유권이 이동된 후 원래 변수를 사용하려고 하면 컴파일 에러가 발생합니다. 이를 통해 Rust는 동일한 메모리 위치에 여러 참조가 남아 있지 않도록 합니다.

예시: 안전한 참조 사용

fn no_dangle() -> String {
    let s = String::from("hello");
    s  // 소유권이 반환되어 문제가 없음
}

fn main() {
    let result = no_dangle();
    println!("{}", result);  // 안전하게 사용 가능
}

위 코드에서는 no_dangle 함수가 String을 반환합니다. 이 경우 소유권이 반환되어 메모리 안전성이 유지됩니다.

결론

Rust는 소유권과 참조의 수명을 엄격하게 관리함으로써 댕글링 포인터가 발생하지 않도록 보장합니다. 이는 Rust의 주요 강점 중 하나로, 메모리 안전성을 타협하지 않고도 성능이 뛰어난 시스템 소프트웨어를 개발할 수 있게 해줍니다. 결과적으로 Rust를 사용하는 개발자는 메모리 관련 오류에 대한 걱정 없이 안정적인 코드를 작성할 수 있습니다.

반응형