Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

메모리 안전성 심층 분석: 참조와 수명

학습 목표: Rust의 참조(&)가 C#의 포인터나 참조 타입과 어떻게 다른지 심층적으로 분석합니다. 특히 **수명(Lifetime)**의 기초 개념을 익히고, 왜 컴파일 타임의 안전성 증명이 C#의 런타임 체크(경계 검사, Null 가드 등)보다 강력하고 효율적인지 이해합니다.


1. 참조 vs 포인터

C#의 unsafe 블록에서 쓰던 포인터는 위험하지만, Rust의 참조는 항상 안전함이 보장됩니다.

  • C# 포인터: 메모리 주소를 직접 가리키며, 유효성 검사는 개발자의 몫입니다.
  • Rust 참조: 빌림 검사기가 유효성을 추적하며, 잘못된 메모리 접근은 컴파일조차 되지 않습니다.
#![allow(unused)]
fn main() {
// [Rust는 안전이 기본입니다]
fn safe_swap(a: &mut i32, b: &mut i32) {
    let temp = *a;
    *a = *b;
    *b = temp;
}
// ❌ Null 체크나 경계 검사 코드가 없어도 컴파일러가 안전을 보증합니다.
}

2. 수명(Lifetime): 데이터가 얼마나 살아야 하는가?

C# 개발자에게 가장 낯선 개념 중 하나입니다. 수명이란 **"어떤 참조가 가리키는 실제 데이터가 메모리에 살아있는 기간"**을 뜻합니다.

  • C#: GC가 알아서 데이터를 살려둡니다. 하지만 이로 인해 메모리 사용량이 늘고 성능이 희생됩니다.
  • Rust: 데이터가 사라진 뒤에 그 자리를 가리키는 '허공에 뜬 참조(Dangling Reference)'를 컴파일 타임에 차단합니다.
#![allow(unused)]
fn main() {
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}
// 💡 'a는 "반환되는 참조는 입력된 x, y 중 짧은 수명만큼만 유효하다"는 명시적 약속입니다.
}

3. 런타임 체크 vs 컴파일 타임 증명

특징C# (Runtime Checks)Rust (Compile-time Proof)
경계 검사배열 접근 시마다 매번 체크 (IndexOutOfRange)안전함이 증명되면 체크 코드 자체를 제거
Null 관리실행 중 Null 체크 필수 (NullReferenceException)Null 자체가 존재하지 않음 (Option으로 대체)
데이터 경합락(Lock) 없이 접근 시 런타임 오류 가능빌림 규칙으로 원천 차단
성능 결과체크 오버헤드 발생네이티브 수준의 속도

💡 실무 팁: 반복 중 수정 버그 (Concurrent Modification)

C#에서 리스트를 foreach로 돌리다가 요소를 삭제하면 런타임 에러(InvalidOperationException)가 납니다. Rust는 이를 컴파일 타임에 잡아냅니다. 반복자가 데이터를 읽는 중(&)에는 누구도 데이터를 수정(&mut)할 수 없다는 빌림 규칙 덕분입니다.