Learn Rust Coding · 강의

안전하지 않은 Rust와 상호 작용하기

Rust의 안전성 검사를 우회하여 FFI와 저수준 메모리 작업을 수행할 수 있도록 `unsafe` 블록을 사용하는 시점과 방법을 이해합니다.

레슨 3/311개 단계

안전하지 않은 Rust와 상호 작용하기은(는) CoddyKit의 무료 Learn Rust Coding 강의입니다. 이것은 3개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 Learn Rust Coding 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. Learn Rust Coding 강의에는 총 3개의 강의가 포함되어 있습니다.

안전하지 않은 Rust: 소개

안전하지 않은 Rust에 오신 것을 환영합니다! Rust는 메모리 안전성을 보장하는 것으로 유명하지만, 이러한 검사 중 일부를 우회해야 하는 특정 상황도 있습니다.

Rust의 unsafe 키워드를 사용하면 바로 그렇게 할 수 있습니다. 이는 '나쁜' 코드를 작성하는 방법이 아니라, 메모리나 하드웨어를 더 직접적으로 제어해야 하는 고급 사용 사례를 위한 도구입니다.

안전하지 않은 코드의 다섯 가지 특별한 기능

unsafe 블록을 사용하면 Rust 컴파일러가 일반적으로 방지하는 다음 다섯 가지 특수 작업을 수행할 수 있습니다.

  • 원시 포인터 역참조: 메모리 주소에 직접 접근합니다.
  • unsafe 함수 또는 메서드 호출: 컴파일러가 검증할 수 없는 사전 조건이 있는 함수를 실행합니다.
  • unsafe trait 구현: 해당 유형이 특정 불변 조건을 준수한다고 선언합니다.
  • 변경 가능한 정적 variable에 접근하거나 수정: 전역에서 변경 가능한 상태를 공유하므로 데이터 경쟁이 발생할 위험이 있습니다.
  • union의 필드 접근: 현재 데이터에 유효하지 않은 유형일 수도 있는 union에서 읽습니다.

원시 포인터 역참조

원시 포인터는 Rust의 일반적인 안전성 보장이 적용되지 않는 메모리 주소입니다. 참조(&T, &mut T)에서 원시 포인터를 만들 수 있지만, 역참조하려면 unsafe 블록이 필요합니다.

즉, 포인터가 유효하고 할당된 메모리를 가리키는지 확인할 책임은 프로그래머인 여러분에게 있습니다.

fn main() {
  let mut num = 5;
  let r1 = &num as *const i32; // Immutable raw pointer
  let r2 = &mut num as *mut i32; // Mutable raw pointer

  unsafe { // The `unsafe` block starts here
    println!("r1 points to: {}", *r1);
    *r2 = 10; // Modify data through mutable raw pointer
    println!("r2 points to: {}", *r2);
  } // The `unsafe` block ends here

  println!("Num is now: {}", num);
}

안전하지 않은 함수 호출

일부 함수에는 unsafe fn이 표시되어 있습니다. 이는 해당 함수에 Rust 컴파일러가 보장할 수 없는 사전 조건이 있다는 뜻입니다. 예를 들어 함수가 유효한 메모리 주소를 요구할 수 있지만, 이를 검증할 수는 없습니다.

이러한 함수를 호출할 때는 unsafe 블록으로 감싸야 하며, 이는 호출자인 여러분이 모든 사전 조건이 충족되었음을 보장한다는 의미입니다.

unsafe fn dangerous_operation() {
  println!("This operation could be dangerous if preconditions aren't met!");
}

fn main() {
  println!("Attempting a dangerous operation...");
  unsafe { // Calling an unsafe function requires `unsafe`
    dangerous_operation();
  }
  println!("Operation completed.");
}

변경 가능한 정적 variable

Rust는 데이터 경쟁을 방지하기 위해 기본적으로 전역의 변경 가능한 상태를 허용하지 않습니다. 하지만 static mut을 사용해 변경 가능한 정적 variable을 선언할 수 있습니다.

여러 스레드가 동시에 이러한 variable에 접근하려 할 수 있어 정의되지 않은 동작이 발생할 수 있으므로, 접근하거나 수정하는 작업은 unsafe로 간주됩니다. 적절한 동기화는 여러분의 책임입니다.

static mut COUNTER: i32 = 0; // A mutable static variable

fn add_to_counter(inc: i32) {
  unsafe { // Modifying `static mut` requires `unsafe`
    COUNTER += inc;
  }
}

fn main() {
  add_to_counter(5);
  unsafe { // Reading `static mut` also requires `unsafe`
    println!("COUNTER after first add: {}", COUNTER);
  }

  add_to_counter(10);
  unsafe {
    println!("COUNTER after second add: {}", COUNTER);
  }
}

안전하지 않은 trait 구현

Rust의 일부 trait에는 unsafe trait이 표시되어 있습니다. 이는 구현할 때 컴파일러가 검사할 수 없는 특정 불변 조건을 준수해야 한다는 의미입니다.

일반적인 예로 스레드 안전성과 관련된 Send 및 Sync trait이 있습니다. unsafe trait을 직접 구현한다면 unsafe impl Trait for Type을 사용하고 해당 구현의 안전성 속성을 보장해야 합니다.

Rust가 이러한 작업을 자동으로 처리하거나 안전한 추상화를 통해 제공하는 경우가 많으므로, 대부분의 초보자는 이 기능을 자주 접하지 않습니다.

Union 필드에 접근하기

union은 주어진 시점에 여러 variant 중 하나의 값을 저장할 수 있지만, 모든 variant가 같은 메모리 위치를 공유하는 특수한 유형입니다. C의 union과 비슷합니다.

컴파일러는 현재 어떤 필드가 활성 상태인지 알 수 없으므로 union의 필드에 접근하려면 unsafe 블록이 필요합니다. 비활성 필드에서 읽으면 정의되지 않은 동작이나 데이터의 잘못된 해석이 발생할 수 있습니다.

union Data {
  integer: u32,
  float: f32,
}

fn main() {
  let mut d = Data { integer: 42 }; // Initialize with an integer

  unsafe {
    // Accessing 'integer' is safe here
    println!("Integer value: {}", d.integer);

    // Accessing 'float' is unsafe, as 'integer' was initialized.
    // This would interpret the integer's bits as a float.
    // println!("Float value (unsafe): {}", d.float);
  }

  d.float = 3.14; // Now initialize with a float
  unsafe {
    // Accessing 'float' is safe now
    println!("Float value: {}", d.float);
  }
}

안전하지 않은 코드의 실제 사용 사례

unsafe는 신중하게 사용해야 하지만, 다음과 같은 여러 고급 프로그래밍 작업에 필수적입니다.

  • 외부 함수 인터페이스(FFI): 다른 언어로 작성된 코드(예: C/C++ 라이브러리)와 상호 작용합니다.
  • 성능 최적화: 경우에 따라 Rust의 검사를 우회하면 중요한 부분에서 성능이 조금 향상될 수 있습니다(단, 먼저 측정해야 합니다!).
  • 저수준 시스템 프로그래밍: 운영 체제, 장치 드라이버 또는 임베디드 시스템을 작성합니다.
  • 안전한 추상화 구축: 안전하지 않은 기본 요소 위에 안전한 자료 구조나 API를 구현합니다(예: Vec가 구축되는 방식).

안전하지 않은 코드에 대한 책임

unsafe를 사용하면 Rust의 안전성 보장을 직접 유지할 책임을 지게 됩니다. 이는 다음을 의미합니다.

  • 메모리가 유효하고 올바르게 정렬되어 있는지 확인합니다.
  • 공유된 변경 가능 상태를 다룰 때 데이터 경쟁을 방지합니다.
  • 댕글링 포인터나 해제 후 사용 오류를 방지합니다.
  • 모든 함수의 사전 조건이 충족되었는지 확인합니다.

unsafe를 잘못 사용하면 정의되지 않은 동작이 발생할 수 있습니다. 이는 가장 심각한 종류의 버그이며 디버깅하기도 매우 어렵습니다.

안전하지 않은 코드 지식 확인

다음 작업 중 Rust에서 unsafe 블록이 필요한 것은 무엇일까요?

복습: 안전하지 않은 Rust

특정 고급 상황에서 Rust의 컴파일 시 안전성 검사를 우회할 수 있게 해 주는 강력한 기능인 안전하지 않은 Rust를 배웠습니다.

unsafe가 대여 검사기를 완전히 끄는 것은 아닙니다. 대신 메모리 안전성과 불변 조건을 보장할 책임을 전적으로 프로그래머인 여러분에게 넘깁니다. 일반적으로 FFI, 저수준 최적화 또는 안전한 추상화 구축에 사용할 때에도 신중하고 각별히 주의해서 사용하세요.

무료로 시작

AI 튜터와 함께 Rust을(를) 배우세요 — 무료

브라우저에서 실제 코드를 작성하고 실행하며, 24/7 AI 튜터로부터 즉각적인 도움을 받고, 웹이나 앱에서 중단한 부분부터 계속 학습하세요.

코스
39
레슨
144

자주 묻는 질문

“안전하지 않은 Rust와 상호 작용하기” 강의는 무료인가요?

네 — “안전하지 않은 Rust와 상호 작용하기” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 Learn Rust Coding 강의 전체를 잠금 해제할 수 있습니다. Learn Rust Coding 강의에는 총 3개의 강의가 포함되어 있습니다.

“안전하지 않은 Rust와 상호 작용하기”에서 뭘 배우나요?

Rust의 안전성 검사를 우회하여 FFI와 저수준 메모리 작업을 수행할 수 있도록 `unsafe` 블록을 사용하는 시점과 방법을 이해합니다. 브라우저에서 직접 실행하는 실습 코드로 Learn Rust Coding을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

Learn Rust Coding을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 Learn Rust Coding은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 3개 중 3번째 강의입니다.

“안전하지 않은 Rust와 상호 작용하기” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 Learn Rust Coding 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 Learn Rust Coding 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 선언적 매크로(`macro_rules!`)
  2. 절차적 매크로: Derive, Function
  3. 안전하지 않은 Rust와 상호 작용하기
← Learn Rust Coding(으)로 돌아가기