STUDY/Rust
Rust - 23. 고급 기능 (1) Unsafe RUST
sinawi95
2024. 3. 31. 11:13
728x90
얼마 안남았다~~~ 오예~~~ 3월내로 끝낼듯
1. Unsafe Rust
러스트가 보증하는 것의 일부를 거부하고 해당 보증을 수동으로 유지하는 것에 대한 책임을 지는 방법
unsafe 키워드
- 이 키워드를 사용한 블록은 안전하지 않은 코드(안전하지않은 슈퍼파워)를 넣을 수 있다.
- 원시포인터(raw pointer) 역참조
- 안전하지 않은 함수 혹은 메서드 호출
- 가변 정적 변수 접근 및 수정
- 안전하지 않은 트레이트 구현
- union 필드 접근
- 위 다섯 가지 기능 제외한 다른 기능은 안전성 검사를 수행한다.
- 안전하지 않은 코드는 유효한 방식으로 메모리에 접근하도록 보장해야한다.
- unsafe 블록을 작게 만드는 편이 좋다.
- 문제의 원인을 더 쉽게 추적할수 있음
- 안전하지 않은 코드를 분리하려면 추상화 하고 api를 제공하는것이 가장 좋다.
1.1 원시포인터(raw pointer) 역참조
러스트 컴파일러는 참조가 항상 유효한것을 보장함
원시포인터
// *const T: 불변 원시 포인터
// *mut T: 가변 원시 포인터
// 원시 포인터 생성
fn main() {
let mut num = 5;
let r1 = &num as *const i32;
let r2 = &mut un as *mut i32;
}
- 생성할땐 unsafe 코드를 사용하지 않아도됨. 역참조 할 때만 사용
원시 포인터 특징
- 원시 포인터는 대여 규칙을 무시할수 있음. 같은 위치에 대해 불변과 가변 포인터를 동시에 가질수 있거나 여러개의 가변포인터를 가질수 있음
- 소유권 규칙에 따르면 가변 참조자와 불변 참조자를 동시에 허용하지 않음.
- 원시포인터를 사용하면 동시에 사용할수 있으나 데이터 경합을 일으킬수있음.(unsafe)
- 유효한 메모리를 가리키는 것을 보장하지 않음
- null 이 될수 있음
- 자동 메모리 정리를 구현하지 않음
1.2 안전하지 않은 함수 혹은 메서드 호출
// unsafe function
unsafe fn dangerous() {}
// call unsafe function
fn main() {
unsafe {
dangerous()
}
}
- unsafe 함수는 분리된 unsafe 블록 내에서 호출되어야함. unsafe 블록 없으면 에러 발생
안전하지 않은 코드를 감싸는 안전한 추상화
use std::slice;
fn split_at_mut(values: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
let len = values.len();
let ptr = values.as_mut_ptr();
assert!(mid <= len);
unsafe {
(
slice::from_raw_parts_mut(ptr, mid),
slice::from_raw_parts_mut(ptr.add(mid), len - mid),
)
}
}
fn main() {
let mut vector = vec![1, 2, 3, 4, 5, 6];
let (left, right) = split_at_mut(&mut vector, 3);
println!("{:?}", left);
println!("{:?}", right);
}
- 러스트 예제를 그대로 가져왔다.
- 예제는 한 슬라이스에서 겹치지 않는 다른 부분을 접근하고있고 이를 반환해서 두개의 슬라이스를 만든다.
- 러스트는 동일한 슬라이스에 두번 접근하는 것으로 판단해서 에러를 띄우므로 unsafe 블록이 필요하다.
FFI, Foreign Function Interface, 외래 함수 인터페이스
extern 키워드
extern "C" {
fn abs(input: i32) -> i32;
}
fn main() {
unsafe {
println!("Absolute value of -3 according to C: {}", abs(-3));
}
}
- 외부(다른 언어로 쓰여진 프로그램)에서 만든 함수를 사용할수 있게 만듦
- 러스트에서 함수를 정의하고(함수이름, 입력, 출력) 사용할때 unsafe 블록 사용
#![allow(unused)]
fn main() {
#[no_mangle]
pub extern "C" fn call_from_c() {
println!("Just called a Rust function from C!");
}
}
- 반대로 러스트 함수를 외부에서 쓸수 있도록 할수 있음
- 이때는 unsafe 블록이 필요없음
- 하지만 mangling 하지 않도록 #[no_mangle] 을 추가해야함
1.3 가변 정적 변수 접근 및 수정
static mut COUNTER: u32 = 0;
fn add_to_count(inc: u32) {
unsafe {
COUNTER += inc;
}
}
fn main() {
add_to_count(3);
unsafe {
println!("COUNTER: {}", COUNTER);
}
}
- 가변 정적변수(전역변수)를 사용하는 경우에 소유권 규칙 관련한 문제가 생길수 있음
- 데이터경합이 있을수 있으므로 unsafe 블록 필요
- 불변 정적변수는 상관없음
1.4 안전하지 않은 트레이트 구현
unsafe를 사용하여 안전하지 않은 트레이트를 구현할 수 있습니다. 메서드 중 하나 이상에 컴파일러가 확인할 수 없는 불변성 (invariant) 이 있는 경우 그 트레이트는 안전하지 않습니다. 예제 19-11에 표시된 것처럼 trait 앞에 unsafe 키워드를 추가하고 그 트레이트의 구현체도 unsafe로 표시함으로써 트레이트가 unsafe하다고 선언할 수 있습니다.
1.5 union 필드 접근
유니온 필드에 접근하는 것은 저장된 데이터 타입을 보장할수 없으므로 안전하지 않다.
https://doc.rust-kr.org/ch19-00-advanced-features.html
https://doc.rust-kr.org/ch19-01-unsafe-rust.html