# 안전함과 불안전함은 어떻게 상호작용하는가 안전한 러스트와 불안전한 러스트는 어떤 관계일까요? 둘은 어떻게 상호작용할까요? 안전한 러스트와 불안전한 러스트 간의 구분은 `unsafe` 라는 키워드로 제어되는데, 이것은 서로에게 인터페이스 역할을 합니다. 이것이 바로 안전한 러스트는 안전한 언어라고 할 수 있는 이유입니다: 모든 불안전한 부분은 `unsafe` 라는 경계 뒤로 밀리거든요. 원한다면 `#![forbid(unsafe_code)]` 를 코드베이스에 집어넣음으로써 오직 안전한 러스트만 쓴다는 것을 컴파일할 때 보장할 수 있죠. `unsafe` 키워드는 두 가지 용도가 있습니다: 컴파일러가 확인할 수 없는 계약의 존재를 정의할 때 사용하고, 또한 이 계약들이 성립한다는 것을 프로그래머가 확인했다고 선언할 때 사용합니다. _함수들_ 과 _트레잇 정의들_ 에서 확인되지 않은 계약들의 존재를 알리기 위해 `unsafe` 키워드를 쓸 수 있습니다. 함수에서 `unsafe` 는 함수의 사용자들이 함수의 문서를 확인해서, 함수가 요구하는 계약을 지키는 방식으로 사용해야 한다는 것을 의미합니다. 트레잇 정의에서 `unsafe` 는 트레잇의 구현자들이 트레잇 문서를 확인해서 그들의 구현이 트레잇이 요구하는 계약을 지키는 것을 확실히 해야 한다는 것을 뜻합니다. 코드 블럭에도 `unsafe` 를 사용해서 그 안에서 이루어진 모든 불안전한 작업들이 그 작업들의 계약들을 지켰다는 것을 확인했다고 선언할 수 있습니다. 예를 들어, [`slice::get_unchecked`][get_unchecked] 에 넘겨진 인덱스는 범위 안에 있어야 합니다. 트레잇 구현에 `unsafe` 를 사용해서 그 구현이 트레잇의 계약을 지킨다고 선언할 수 있습니다. 예를 들어, [`Send`] 를 구현하는 타입은 정말로 다른 스레드로 안전하게 이동할 수 있어야 합니다. 표준 라이브러리는 다음을 포함한 다수의 불안전한 함수들을 가지고 있습니다: * [`slice::get_unchecked`][get_unchecked] 는 범위를 확인하지 않고 인덱싱을 하기 때문에 메모리 안정성이 자유롭게 침해되도록 허용합니다. * [`mem::transmute`][transmute] 는 어떤 값을 주어진 타입으로 재해석하여 임의의 방식으로 타입 안정성을 건너뜁니다 (자세한 사항은 [변환][conversions] 을 참고하세요). * 사이즈가 정해진 타입의 모든 생(raw)포인터는 [`offset`][ptr_offset] 메서드가 있는데, 이 메서드는 전달된 편차(offset)가 ["범위 안에 있지"][ptr_offset] 않을 경우 미정의 동작을 일으킵니다. * 모든 외부 함수 인터페이스 (FFI) 함수들은 호출하기에 `불안전` 합니다. 이는 다른 언어들이 러스트 컴파일러가 확인할 수 없는 임의의 연산들을 할 수 있기 때문입니다. 러스트 1.48.0 버전에서 표준 라이브러리는 다음의 불안전한 트레잇들을 정의하고 있습니다 (다른 것들도 있지만 아직 안정화되지 않았고, 어떤 것들은 나중에도 안정화되지 않을 것입니다): * [`Send`] 는 이를 구현하는 타입들이 다른 스레드로 이동해도 안전함을 약속하는 표시 트레잇(API가 없는 트레잇)입니다. * [`Sync`] 는 또다른 표시 트레잇으로, 이를 구현하는 타입들을 불변 레퍼런스를 이용해 스레드들이 서로 공유할 수 있음을 약속합니다. * [`GlobalAlloc`] 은 프로그램 전체의 메모리 할당자를 커스터마이징할 수 있게 해 줍니다. * [`SliceIndex`] 는 슬라이스 타입들의 인덱싱을 위한 동작을 정의합니다. Much of the Rust standard library also uses Unsafe Rust internally. These implementations have generally been rigorously manually checked, so the Safe Rust interfaces built on top of these implementations can be assumed to be safe. The need for all of this separation boils down a single fundamental property of Safe Rust, the *soundness property*: **No matter what, Safe Rust can't cause Undefined Behavior.** The design of the safe/unsafe split means that there is an asymmetric trust relationship between Safe and Unsafe Rust. Safe Rust inherently has to trust that any Unsafe Rust it touches has been written correctly. On the other hand, Unsafe Rust cannot trust Safe Rust without care. As an example, Rust has the [`PartialOrd`] and [`Ord`] traits to differentiate between types which can "just" be compared, and those that provide a "total" ordering (which basically means that comparison behaves reasonably). [`BTreeMap`] doesn't really make sense for partially-ordered types, and so it requires that its keys implement `Ord`. However, `BTreeMap` has Unsafe Rust code inside of its implementation. Because it would be unacceptable for a sloppy `Ord` implementation (which is Safe to write) to cause Undefined Behavior, the Unsafe code in BTreeMap must be written to be robust against `Ord` implementations which aren't actually total — even though that's the whole point of requiring `Ord`. The Unsafe Rust code just can't trust the Safe Rust code to be written correctly. That said, `BTreeMap` will still behave completely erratically if you feed in values that don't have a total ordering. It just won't ever cause Undefined Behavior. One may wonder, if `BTreeMap` cannot trust `Ord` because it's Safe, why can it trust *any* Safe code? For instance `BTreeMap` relies on integers and slices to be implemented correctly. Those are safe too, right? The difference is one of scope. When `BTreeMap` relies on integers and slices, it's relying on one very specific implementation. This is a measured risk that can be weighed against the benefit. In this case there's basically zero risk; if integers and slices are broken, *everyone* is broken. Also, they're maintained by the same people who maintain `BTreeMap`, so it's easy to keep tabs on them. On the other hand, `BTreeMap`'s key type is generic. Trusting its `Ord` implementation means trusting every `Ord` implementation in the past, present, and future. Here the risk is high: someone somewhere is going to make a mistake and mess up their `Ord` implementation, or even just straight up lie about providing a total ordering because "it seems to work". When that happens, `BTreeMap` needs to be prepared. The same logic applies to trusting a closure that's passed to you to behave correctly. This problem of unbounded generic trust is the problem that `unsafe` traits exist to resolve. The `BTreeMap` type could theoretically require that keys implement a new trait called `UnsafeOrd`, rather than `Ord`, that might look like this: ```rust use std::cmp::Ordering; unsafe trait UnsafeOrd { fn cmp(&self, other: &Self) -> Ordering; } ``` Then, a type would use `unsafe` to implement `UnsafeOrd`, indicating that they've ensured their implementation maintains whatever contracts the trait expects. In this situation, the Unsafe Rust in the internals of `BTreeMap` would be justified in trusting that the key type's `UnsafeOrd` implementation is correct. If it isn't, it's the fault of the unsafe trait implementation, which is consistent with Rust's safety guarantees. The decision of whether to mark a trait `unsafe` is an API design choice. A safe trait is easier to implement, but any unsafe code that relies on it must defend against incorrect behavior. Marking a trait `unsafe` shifts this responsibility to the implementor. Rust has traditionally avoided marking traits `unsafe` because it makes Unsafe Rust pervasive, which isn't desirable. `Send` and `Sync` are marked unsafe because thread safety is a *fundamental property* that unsafe code can't possibly hope to defend against in the way it could defend against a buggy `Ord` implementation. Similarly, `GlobalAllocator` is keeping accounts of all the memory in the program and other things like `Box` or `Vec` build on top of it. If it does something weird (giving the same chunk of memory to another request when it is still in use), there's no chance to detect that and do anything about it. The decision of whether to mark your own traits `unsafe` depends on the same sort of consideration. If `unsafe` code can't reasonably expect to defend against a broken implementation of the trait, then marking the trait `unsafe` is a reasonable choice. As an aside, while `Send` and `Sync` are `unsafe` traits, they are *also* automatically implemented for types when such derivations are provably safe to do. `Send` is automatically derived for all types composed only of values whose types also implement `Send`. `Sync` is automatically derived for all types composed only of values whose types also implement `Sync`. This minimizes the pervasive unsafety of making these two traits `unsafe`. And not many people are going to *implement* memory allocators (or use them directly, for that matter). This is the balance between Safe and Unsafe Rust. The separation is designed to make using Safe Rust as ergonomic as possible, but requires extra effort and care when writing Unsafe Rust. The rest of this book is largely a discussion of the sort of care that must be taken, and what contracts Unsafe Rust must uphold. [`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html [`GlobalAlloc`]: https://doc.rust-lang.org/std/alloc/trait.GlobalAlloc.html [`SliceIndex`]: https://doc.rust-lang.org/std/slice/trait.SliceIndex.html [conversions]: conversions.html [ptr_offset]: https://doc.rust-lang.org/std/primitive.pointer.html#method.offset [get_unchecked]: https://doc.rust-lang.org/std/primitive.slice.html#method.get_unchecked [transmute]: https://doc.rust-lang.org/std/mem/fn.transmute.html [`PartialOrd`]: https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html [`Ord`]: https://doc.rust-lang.org/std/cmp/trait.Ord.html [`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html