|
| 1 | +# Safety comments |
| 2 | + |
| 3 | +Using [`unsafe`] blocks is often required in the Rust compiler or standard |
| 4 | +library, but this is not done without rules: each `unsafe` block should have |
| 5 | +a `SAFETY:` comment explaining why the block is safe, which invariants are |
| 6 | +used and must be respected. Below are some examples taken from the standard |
| 7 | +library: |
| 8 | + |
| 9 | +[`unsafe`]: https://doc.rust-lang.org/stable/std/keyword.unsafe.html |
| 10 | + |
| 11 | +## Inside `unsafe` elements |
| 12 | + |
| 13 | +This one shows how an `unsafe` function can pass the requirements through to its |
| 14 | +caller with the use of documentation in a `# Safety` section while still having |
| 15 | +more invariants needed that are not required from callers. `clippy` has a |
| 16 | +lint for `# Safety` sections by the way. |
| 17 | + |
| 18 | +[See the example on github][as_bytes_mut] |
| 19 | + |
| 20 | +```rust |
| 21 | +/// Converts a mutable string slice to a mutable byte slice. |
| 22 | +/// |
| 23 | +/// # Safety |
| 24 | +/// |
| 25 | +/// The caller must ensure that the content of the slice is valid UTF-8 |
| 26 | +/// before the borrow ends and the underlying `str` is used. |
| 27 | +/// |
| 28 | +/// Use of a `str` whose contents are not valid UTF-8 is undefined behavior. |
| 29 | +/// |
| 30 | +/// ... |
| 31 | +pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] { |
| 32 | + // SAFETY: the cast from `&str` to `&[u8]` is safe since `str` |
| 33 | + // has the same layout as `&[u8]` (only libstd can make this guarantee). |
| 34 | + // The pointer dereference is safe since it comes from a mutable reference which |
| 35 | + // is guaranteed to be valid for writes. |
| 36 | + unsafe { &mut *(self as *mut str as *mut [u8]) } |
| 37 | +} |
| 38 | +``` |
| 39 | + |
| 40 | +This example is for a function but the same principle applies to `unsafe trait`s |
| 41 | +like [`Send`] or [`Sync`] for example, though they have no `# Safety` section |
| 42 | +since their entire documentation is about why they are `unsafe`. |
| 43 | + |
| 44 | +Note that in the Rust standard library, [`unsafe_op_in_unsafe_fn`] is active |
| 45 | +and so each `unsafe` operation in an `unsafe` function must be enclosed in an |
| 46 | +`unsafe` block. This makes it easier to review such functions and to document |
| 47 | +their `unsafe` parts. |
| 48 | + |
| 49 | +[`Send`]: https://doc.rust-lang.org/stable/std/marker/trait.Send.html |
| 50 | +[`Sync`]: https://doc.rust-lang.org/stable/std/marker/trait.Sync.html |
| 51 | +[as_bytes_mut]: https://github.com/rust-lang/rust/blob/a08f25a7ef2800af5525762e981c24d96c14febe/library/core/src/str/mod.rs#L278 |
| 52 | +[`unsafe_op_in_unsafe_fn`]: https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html#unsafe-op-in-unsafe-fn |
| 53 | + |
| 54 | +## Inside *safe* elements |
| 55 | + |
| 56 | +Inside safe elements, a `SAFETY:` comment must not depend on anything from the |
| 57 | +caller beside properly constructed types and values (i.e, if your function |
| 58 | +receives a reference that is unaligned or null, it is the caller fault if it |
| 59 | +fails and not yours). |
| 60 | + |
| 61 | +`SAFETY` comments in *safe* elements often rely on checks that are done before |
| 62 | +the `unsafe` block or on type invariants, like a division by `NonZeroU8` would |
| 63 | +not check for `0` before dividing. |
| 64 | + |
| 65 | +[See the example on github][split_at] |
| 66 | + |
| 67 | +```rust |
| 68 | +pub fn split_at(&self, mid: usize) -> (&str, &str) { |
| 69 | + // is_char_boundary checks that the index is in [0, .len()] |
| 70 | + if self.is_char_boundary(mid) { |
| 71 | + // SAFETY: just checked that `mid` is on a char boundary. |
| 72 | + unsafe { (self.get_unchecked(0..mid), self.get_unchecked(mid..self.len())) } |
| 73 | + } else { |
| 74 | + slice_error_fail(self, 0, mid) |
| 75 | + } |
| 76 | +} |
| 77 | +``` |
| 78 | + |
| 79 | +[split_at]: https://github.com/rust-lang/rust/blob/a08f25a7ef2800af5525762e981c24d96c14febe/library/core/src/str/mod.rs#L570 |
0 commit comments