Rust 의 안전하지 않은 연산 탐색

Beginner

This tutorial is from open-source community. Access the source code

소개

이 실습에서는 Rust 에서의 안전하지 않은 연산을 탐구합니다. 이러한 연산은 컴파일러 보호를 우회하는 데 사용되며, 일반적으로 원시 포인터 역참조, 안전하지 않은 함수 호출, 정적 가변 변수 접근 또는 수정, 그리고 안전하지 않은 트레이트 구현에 사용됩니다. 코드베이스에서 이러한 연산은 안전성을 보장하기 위해 최소화해야 합니다.

참고: 실습에서 파일 이름을 지정하지 않으면 원하는 파일 이름을 사용할 수 있습니다. 예를 들어 main.rs를 사용하고 rustc main.rs && ./main으로 컴파일 및 실행할 수 있습니다.

안전하지 않은 연산

이 섹션에 대한 소개로, 공식 문서에서 "코드베이스에서 안전하지 않은 코드의 양을 최소화해야 한다"는 점을 인용하면서 시작하겠습니다. 이를 염두에 두고 시작해 보겠습니다! Rust 에서 안전하지 않은 주석은 컴파일러가 설정한 보호 기능을 우회하는 데 사용됩니다. 구체적으로, 안전하지 않은 연산은 다음 네 가지 주요 목적으로 사용됩니다.

  • 원시 포인터 역참조
  • unsafe로 선언된 함수 또는 메서드 호출 (FFI 를 통해 함수를 호출하는 경우 포함, [책의 이전 장 참조])
  • 정적 가변 변수 접근 또는 수정
  • 안전하지 않은 트레이트 구현

원시 포인터

원시 포인터 *와 참조 &T는 유사한 기능을 수행하지만, 참조는 항상 안전합니다. 이는 보류 검사기 (borrow checker) 로 인해 유효한 데이터를 가리키는 것으로 보장되기 때문입니다. 원시 포인터를 역참조하려면 항상 안전하지 않은 블록을 사용해야 합니다.

fn main() {
    let raw_p: *const u32 = &10;

    unsafe {
        assert!(*raw_p == 10);
    }
}

안전하지 않은 함수 호출

일부 함수는 unsafe로 선언될 수 있습니다. 이는 컴파일러가 아닌 프로그래머가 정확성을 보장해야 함을 의미합니다. 이러한 예 중 하나는 [std::slice::from_raw_parts]입니다. 이 함수는 첫 번째 요소의 포인터와 길이를 받아 슬라이스를 생성합니다.

use std::slice;

fn main() {
    let some_vector = vec![1, 2, 3, 4];

    let pointer = some_vector.as_ptr();
    let length = some_vector.len();

    unsafe {
        let my_slice: &[u32] = slice::from_raw_parts(pointer, length);

        assert_eq!(some_vector.as_slice(), my_slice);
    }
}

slice::from_raw_parts의 경우, 반드시 준수해야 하는 가정 중 하나는 전달된 포인터가 유효한 메모리를 가리키고, 가리키는 메모리가 올바른 형식임을 보장하는 것입니다. 이러한 불변식이 준수되지 않으면 프로그램의 동작이 정의되지 않으며 어떤 일이 발생할지 알 수 없습니다.

요약

축하합니다! 안전하지 않은 연산 실습을 완료했습니다. LabEx 에서 더 많은 실습을 통해 기술을 향상시킬 수 있습니다.