Unsafe 연산
이 섹션을 시작하며 공식 문서의 말을 빌리자면, “코드베이스에서 unsafe 코드의 양을 최소화하도록 노력해야 합니다.” 이 점을 염두에 두고 시작해 봅시다! Rust에서 Unsafe 어노테이션은 컴파일러가 제공하는 보호 기능을 우회하는 데 사용됩니다. 구체적으로, unsafe는 다음과 같은 네 가지 주요 작업에 사용됩니다:
- raw 포인터 역참조하기
unsafe함수나 메서드 호출하기 (FFI를 통한 함수 호출 포함, 이 책의 이전 장 참조)- 정적 가변(static mutable) 변수에 접근하거나 수정하기
- unsafe 트레이트 구현하기
Raw 포인터
Raw 포인터 *와 참조 &T는 비슷하게 작동하지만, 참조는 빌림 검사기(borrow checker) 덕분에 항상 유효한 데이터를 가리킨다는 것이 보장되므로 항상 안전합니다. Raw 포인터의 역참조는 unsafe 블록을 통해서만 수행될 수 있습니다.
fn main() {
let raw_p: *const u32 = &10;
unsafe {
assert!(*raw_p == 10);
}
}
Unsafe 함수 호출하기
일부 함수는 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의 경우, 반드시 지켜져야 하는 가정 중 하나는 전달된 포인터가 유효한 메모리를 가리키고 가리키는 메모리가 올바른 타입이어야 한다는 것입니다. 이러한 불변성(invariant)이 지켜지지 않으면 프로그램의 동작은 정의되지 않으며 어떤 일이 일어날지 알 수 없습니다.