[0]

Rust에는 memory safety를 컴파일 타임에 보장하기 위해 다양한 제약들이 존재한다. 이러한 제약들은 보수적이기 때문에, 때로는 안전한 코드에 대해서도 컴파일 에러를 발생하는 경우가 생길 수 있다. 하지만 Rust는 기본적으로 시스템 프로그래밍을 목적으로 한 언어이기 때문에, 필연적으로 low level의 하드웨어 접근이 필요하게 된다. 이러한 경우에 Rust의 unsafe block을 사용할 수 있다.

 

[1]

unsafe rust가 모든 safety 체크를 해제하는 것은 아니다. unsafe는 제한적이고 memory error를 일으킬 수 있는 몇 가지 권한을 더 부여할 뿐이다. 그 외에 borrow checker 등은 그대로 유지된다. 따라서 unsafe block은 언어가 아니라 개발자가 아래의 항목들에 대해 safety를 보장하여 코드를 짜야한다. unsafe에서 부여하는 권한들은 다음과 같다.

 

  1. raw pointer를 역참조할 수 있다.
  2. unsafe function이나 method를 호출할 수 있다.
  3. mutable static variable에 접근하거나 수정할 수 있다.
  4. unsafe trait을 implement할 수 있다.
  5. union의 멤버에 접근할 수 있다.

2와 4는 명백하므로 나머지 권한들의 의미에 대해서 알아보자. 아래에서 그냥 Rust라고 하면 unsafe를 사용하지 않은 일반적인 rust를 지칭한다.

 

[2]

Raw pointer는 c의 포인터와 같다. 즉, 어떤 메모리 공간이든 가리킬 수 있고(물론 가상 메모리 공간이다) 어떤 값이든 쓸 수가 있다. 이걸 허용하게 되면 대단히 많은 메모리 취약점을 발생시킬 수 있기 때문에 Rust에서는 역참조가 금지되어 있다. 하지만 low level의 시스템 프로그래밍에서는 반드시 필요한 기능이다. 또한 여전히 방대한 c 라이브러리와의 ffi를 위해서도 필수적인 기능이다. 때문에 unsafe에서는 허용된다.

 

사실상 [1]의 5가지 권한중에서 가장 핵심적인 기능이다. 동시에 가장 많은 취약점을 발생시킬 수 있는 기능이기도 하다. C와 유사하게, Rust에서도 is_null() 메소드 등을 통해 역참조 전에 체크하는 것이 필요하다. 또한 디버깅을 어렵게 만드는 주된 요인이 될 수 있으므로, 가능한 한 unsafe block을 작게 유지하여야 한다.

 

[3]

3번 항목을 보자. 여기서 static variable은 전역 변수를 의미한다고 봐도 무방하다. 그리고 전역 변수는 그 특성상, Rust의 ownership 추적이 불가능하다. 그래서 Rust에서는 static variable에 mutable한 접근은 unsafe로 지정된다. Ownership이 방지해주는 data race의 가능성이 생기기 때문이다.

 

[4]

Union은 C와의 ffi를 위해 필요하다. 즉 C와 소통할게 아니라면 필요하지는 않은 기능이다. Union에 저장된 변수의 type을 컴파일타임에 미리 알 수 없기 때문에 unsafe이다.

 

[5]

이제 unsafe를 어떻게 활용해야 할지에 대해 알아보자. 우선 중요한 점을 먼저 짚고 넘어가야겠다. low level의 프로그래밍, c와의 ffi를 제외하고는 unsafe를 키워드를 사용할 일은 대단히 드물다. 그럼에도 반드시 unsafe 기능이 필요하다면, 이를 직접 사용하는 것을 지양하고, 이미 추상화된 라이브러리를 사용해야 한다. copy_on_slice의 예시를 보자.

    pub fn copy_from_slice(&mut self, src: &[T])
    where
        T: Copy,
    {
        assert_eq!(self.len(), src.len(), "destination and source slices have different lengths");
        unsafe {
            ptr::copy_nonoverlapping(src.as_ptr(), self.as_mut_ptr(), self.len());
        }
    }

copy_from_slice()는 src의 모든 원소들을 self로 memcpy하는 메소드다. copy_from_slice()는 내부적으로 unsafe block에서 unsafe함수인 copy_nonoverlapping()을 호출한다.(이름처럼 memcpy인데 src와 dest가 겹치지 않음을 보장하는 경우 사용한다.) 즉, slice에서 copy하는 기능을 원한다면 unsafe인 copy_nonoverlapping()이 아니라, safe인 copy_from_slice()를 호출해야한다. 이미 라이브러리에서 보장하는 함수이기 때문이다. 

 

물론 dependency없는 임베디드 환경에서 개발을 한다거나, 직접 라이브러리를 개발하고 싶을 수 있다. 이런 경우에도 copy_from_slice의 경우와 마찬가지로, 유저에게는 safe api를 제공하고, 내부에서 unsafe를 사용하는 방식을 고수하여야 한다. 

 

 

 

 

[참고]

doc.rust-lang.org/book/ch19-01-unsafe-rust.html

 

Unsafe Rust - The Rust Programming Language

All the code we’ve discussed so far has had Rust’s memory safety guarantees enforced at compile time. However, Rust has a second language hidden inside it that doesn’t enforce these memory safety guarantees: it’s called unsafe Rust and works just l

doc.rust-lang.org

smallcultfollowing.com/babysteps/blog/2016/05/23/unsafe-abstractions/

 

Unsafe abstractions

The unsafe keyword is a crucial part of Rust’s design. For those not familiar with it, the unsafe keyword is basically a way to bypass Rust’s type checker; it essentially allows you to write something more like C code, but using Rust syntax. The existe

smallcultfollowing.com

 

'Computer Science > Rust' 카테고리의 다른 글

Rust의 Cell와 RefCell 코드 분석  (0) 2020.12.06
Rust의 trait object  (0) 2020.08.02
Rust의 async/await와 Future  (0) 2020.07.21
Rust의 Copy trait와 Clone trait  (2) 2020.06.30
Rust의 스마트 포인터  (0) 2020.05.20

+ Recent posts