Skip to content

Commit 595e192

Browse files
committed
unsafe keyword: trait examples and unsafe_op_in_unsafe_fn update
1 parent 8a497b7 commit 595e192

File tree

1 file changed

+120
-32
lines changed

1 file changed

+120
-32
lines changed

library/std/src/keyword_docs.rs

+120-32
Original file line numberDiff line numberDiff line change
@@ -1867,11 +1867,15 @@ mod type_keyword {}
18671867
/// Code or interfaces whose [memory safety] cannot be verified by the type
18681868
/// system.
18691869
///
1870-
/// The `unsafe` keyword has two uses: to declare the existence of contracts the
1871-
/// compiler can't check (`unsafe fn` and `unsafe trait`), and to declare that a
1872-
/// programmer has checked that these contracts have been upheld (`unsafe {}`
1873-
/// and `unsafe impl`, but also `unsafe fn` -- see below). They are not mutually
1874-
/// exclusive, as can be seen in `unsafe fn`.
1870+
/// The `unsafe` keyword has two uses:
1871+
/// - to declare the existence of contracts the compiler can't check (`unsafe fn` and `unsafe
1872+
/// trait`),
1873+
/// - and to declare that a programmer has checked that these contracts have been upheld (`unsafe
1874+
/// {}` and `unsafe impl`, but also `unsafe fn` -- see below).
1875+
///
1876+
/// They are not mutually exclusive, as can be seen in `unsafe fn`: the body of an `unsafe fn` is,
1877+
/// by default, treated like an unsafe block. The `unsafe_op_in_unsafe_fn` lint can be enabled to
1878+
/// change that.
18751879
///
18761880
/// # Unsafe abilities
18771881
///
@@ -1914,12 +1918,12 @@ mod type_keyword {}
19141918
/// - `unsafe impl`: the contract necessary to implement the trait has been
19151919
/// checked by the programmer and is guaranteed to be respected.
19161920
///
1917-
/// `unsafe fn` also acts like an `unsafe {}` block
1921+
/// By default, `unsafe fn` also acts like an `unsafe {}` block
19181922
/// around the code inside the function. This means it is not just a signal to
19191923
/// the caller, but also promises that the preconditions for the operations
1920-
/// inside the function are upheld. Mixing these two meanings can be confusing
1921-
/// and [proposal]s exist to use `unsafe {}` blocks inside such functions when
1922-
/// making `unsafe` operations.
1924+
/// inside the function are upheld. Mixing these two meanings can be confusing, so the
1925+
/// `unsafe_op_in_unsafe_fn` lint can be enabled to warn against that and require explicit unsafe
1926+
/// blocks even inside `unsafe fn`.
19231927
///
19241928
/// See the [Rustnomicon] and the [Reference] for more information.
19251929
///
@@ -1987,13 +1991,15 @@ mod type_keyword {}
19871991
///
19881992
/// ```rust
19891993
/// # #![allow(dead_code)]
1994+
/// #![deny(unsafe_op_in_unsafe_fn)]
19901995
/// /// Dereference the given pointer.
19911996
/// ///
19921997
/// /// # Safety
19931998
/// ///
19941999
/// /// `ptr` must be aligned and must not be dangling.
19952000
/// unsafe fn deref_unchecked(ptr: *const i32) -> i32 {
1996-
/// *ptr
2001+
/// // SAFETY: the caller is required to ensure that `ptr` is aligned and dereferenceable.
2002+
/// unsafe { *ptr }
19972003
/// }
19982004
///
19992005
/// let a = 3;
@@ -2003,35 +2009,118 @@ mod type_keyword {}
20032009
/// unsafe { assert_eq!(*b, deref_unchecked(b)); };
20042010
/// ```
20052011
///
2006-
/// Traits marked as `unsafe` must be [`impl`]emented using `unsafe impl`. This
2007-
/// makes a guarantee to other `unsafe` code that the implementation satisfies
2008-
/// the trait's safety contract. The [Send] and [Sync] traits are examples of
2009-
/// this behaviour in the standard library.
2012+
/// ## `unsafe` and traits
2013+
///
2014+
/// The interactions of `unsafe` and traits can be surprising, so let us contrast the
2015+
/// two combinations of safe `fn` in `unsafe trait` and `unsafe fn` in safe trait using two
2016+
/// examples:
20102017
///
20112018
/// ```rust
2012-
/// /// Implementors of this trait must guarantee an element is always
2013-
/// /// accessible with index 3.
2014-
/// unsafe trait ThreeIndexable<T> {
2015-
/// /// Returns a reference to the element with index 3 in `&self`.
2016-
/// fn three(&self) -> &T;
2019+
/// /// # Safety
2020+
/// ///
2021+
/// /// `make_even` must return an even number.
2022+
/// unsafe trait MakeEven {
2023+
/// fn make_even(&self) -> i32;
20172024
/// }
20182025
///
2019-
/// // The implementation of `ThreeIndexable` for `[T; 4]` is `unsafe`
2020-
/// // because the implementor must abide by a contract the compiler cannot
2021-
/// // check but as a programmer we know there will always be a valid element
2022-
/// // at index 3 to access.
2023-
/// unsafe impl<T> ThreeIndexable<T> for [T; 4] {
2024-
/// fn three(&self) -> &T {
2025-
/// // SAFETY: implementing the trait means there always is an element
2026-
/// // with index 3 accessible.
2027-
/// unsafe { self.get_unchecked(3) }
2028-
/// }
2026+
/// // SAFETY: Our `make_even` always returns something even.
2027+
/// unsafe impl MakeEven for i32 {
2028+
/// fn make_even(&self) -> i32 {
2029+
/// self << 1
2030+
/// }
2031+
/// }
2032+
///
2033+
/// fn use_make_even(x: impl MakeEven) {
2034+
/// if x.make_even() % 2 == 1 {
2035+
/// // SAFETY: this can never happen, because all `MakeEven` implementations
2036+
/// // ensure that `make_even` returns something even.
2037+
/// unsafe { std::hint::unreachable_unchecked() };
2038+
/// }
2039+
/// }
2040+
/// ```
2041+
///
2042+
/// Note how the safety contract of the trait is upheld by the implementation, and is itself used to
2043+
/// uphold the safety contract of the unsafe function `unreachable_unchecked` called by
2044+
/// `use_make_even`. `make_even` itself is a safe function because its *callers* do not have to
2045+
/// worry about any contract, only the *implementation* of `MakeEven` is required to uphold a
2046+
/// certain contract. `use_make_even` is safe because it can use the promise made by `MakeEven`
2047+
/// implementations to uphold the safety contract of the `unsafe fn unreachable_unchecked` it calls.
2048+
///
2049+
/// It is also possible to have `unsafe fn` in a regular safe `trait`:
2050+
///
2051+
/// ```rust
2052+
/// # #![feature(never_type)]
2053+
/// #![deny(unsafe_op_in_unsafe_fn)]
2054+
///
2055+
/// trait Indexable {
2056+
/// const LEN: usize;
2057+
///
2058+
/// /// # Safety
2059+
/// ///
2060+
/// /// The caller must ensure that `idx < LEN`.
2061+
/// unsafe fn idx_unchecked(&self, idx: usize) -> i32;
2062+
/// }
2063+
///
2064+
/// // The implementation for `i32` doesn't need to do any contract reasoning.
2065+
/// impl Indexable for i32 {
2066+
/// const LEN: usize = 1;
2067+
///
2068+
/// unsafe fn idx_unchecked(&self, idx: usize) -> i32 {
2069+
/// debug_assert_eq!(idx, 0);
2070+
/// *self
2071+
/// }
2072+
/// }
2073+
///
2074+
/// // The implementation for arrays exploits the function contract to
2075+
/// // make use of `get_unchecked` on slices and avoid a run-time check.
2076+
/// impl Indexable for [i32; 42] {
2077+
/// const LEN: usize = 42;
2078+
///
2079+
/// unsafe fn idx_unchecked(&self, idx: usize) -> i32 {
2080+
/// // SAFETY: As per this trait's documentation, the caller ensures
2081+
/// // that `idx < 42`.
2082+
/// unsafe { *self.get_unchecked(idx) }
2083+
/// }
2084+
/// }
2085+
///
2086+
/// // The implementation for the never type declares a length of 0,
2087+
/// // which means `idx_unchecked` can never be called.
2088+
/// impl Indexable for ! {
2089+
/// const LEN: usize = 0;
2090+
///
2091+
/// unsafe fn idx_unchecked(&self, idx: usize) -> i32 {
2092+
/// // SAFETY: As per this trait's documentation, the caller ensures
2093+
/// // that `idx < 0`, which is impossible, so this is dead code.
2094+
/// unsafe { std::hint::unreachable_unchecked() }
2095+
/// }
20292096
/// }
20302097
///
2031-
/// let a = [1, 2, 4, 8];
2032-
/// assert_eq!(a.three(), &8);
2098+
/// fn use_indexable<I: Indexable>(x: I, idx: usize) -> i32 {
2099+
/// if idx < I::LEN {
2100+
/// // SAFETY: We have checked that `idx < I::LEN`.
2101+
/// unsafe { x.idx_unchecked(idx) }
2102+
/// } else {
2103+
/// panic!("index out-of-bounds")
2104+
/// }
2105+
/// }
20332106
/// ```
20342107
///
2108+
/// This time, `use_indexable` is safe because it uses a run-time check to discharge the safety
2109+
/// contract of `idx_unchecked`. Implementing `Indexable` is safe because when writing
2110+
/// `idx_unchecked`, we don't have to worry: our *callers* need to discharge a proof obligation
2111+
/// (like `use_indexable` does), but the *implementation* of `get_unchecked` has no proof obligation
2112+
/// to contend with. Of course, the implementation of `Indexable` may choose to call other unsafe
2113+
/// operations, and then it needs an `unsafe` *block* to indicate it discharged the proof
2114+
/// obligations of its callees. (We enabled `unsafe_op_in_unsafe_fn`, so the body of `idx_unchecked`
2115+
/// is not implicitly an unsafe block.) For that purpose it can make use of the contract that all
2116+
/// its callers must uphold -- the fact that `idx < LEN`.
2117+
///
2118+
/// Formally speaking, an `unsafe fn` in a trait is a function with extra
2119+
/// *preconditions* (such as `idx < LEN`), whereas an `unsafe trait` can declare
2120+
/// that some of its functions have extra *postconditions* (such as returning an
2121+
/// even integer). If a trait needs a function with both extra precondition and
2122+
/// extra postcondition, then it needs an `unsafe fn` in an `unsafe trait`.
2123+
///
20352124
/// [`extern`]: keyword.extern.html
20362125
/// [`trait`]: keyword.trait.html
20372126
/// [`static`]: keyword.static.html
@@ -2043,7 +2132,6 @@ mod type_keyword {}
20432132
/// [nomicon-soundness]: ../nomicon/safe-unsafe-meaning.html
20442133
/// [soundness]: https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#soundness-of-code--of-a-library
20452134
/// [Reference]: ../reference/unsafety.html
2046-
/// [proposal]: https://github.com/rust-lang/rfcs/pull/2585
20472135
/// [discussion on Rust Internals]: https://internals.rust-lang.org/t/what-does-unsafe-mean/6696
20482136
mod unsafe_keyword {}
20492137

0 commit comments

Comments
 (0)