Skip to content

Commit 949590b

Browse files
committed
improve motivation
1 parent 8cb1b3f commit 949590b

File tree

1 file changed

+18
-18
lines changed

1 file changed

+18
-18
lines changed

text/0000-unsafe-block-in-unsafe-fn.md

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,24 +38,24 @@ However, this neglects the other aspect of `unsafe` that I described above: To
3838
make the programmer aware that they are treading dangerous ground even when they
3939
may not realize they are doing so.
4040

41-
Using some more formal terminology, an `unsafe` block generally comes with a
42-
proof *obligation*: The programmer has to ensure that this code is actually safe
43-
to execute in the current context, because the compiler just trusts the
44-
programmer to get this right. In contrast, `unsafe fn` represents an
45-
*assumption*: As the author of this function, I make some assumptions that I
46-
expect my callees to uphold. (In terms of
47-
[introduction and elimination forms of natural deduction](https://en.wikipedia.org/wiki/Natural_deduction#Introduction_and_elimination),
48-
`unsafe` blocks are where you introduce, and `unsafe fn` is where you
49-
eliminate.)
50-
51-
Making `unsafe fn` also implicitly play the role of an `unsafe` block conflates
52-
these two dual aspects of unsafety (one party making an assumption, another
53-
party having the obligation to prove that assumption). There is no reason to
54-
believe that the assumption made by the `unsafe fn` is the same as the
55-
obligation incurred by unsafe operations inside this function, and hence the
56-
author of the `unsafe fn` should better carefully check that their assumptions
57-
are sufficient to justify the unsafe operations they are performing. This is
58-
what an `unsafe` block would indicate.
41+
In fact, this double role of `unsafe` in `unsafe fn` (making it both unsafe to
42+
call and enabling it to call other unsafe operations) conflates the two *dual*
43+
roles that `unsafe` plays in Rust. On the one hand, there are places that
44+
*define* a proof obligation, these make things "unsafe to call/do" (e.g., the
45+
language definition says that dereferencing a raw pointer requires it not to be
46+
dangling). On the other hand, there are places that *discharge* the proof
47+
obligation, these are "unsafe blocks of code" (e.g., unsafe code that
48+
dereferences a raw pointer has to locally argue why it cannot be dangling).
49+
50+
`unsafe {}` blocks are about *discharging* obligations, but `unsafe fn` are
51+
about *defining* obligations. The fact that the body of an `unsafe fn` is also
52+
implicitly treated like a block has made it hard to realize this duality
53+
[even for experienced Rust developers][unsafe-dual]. (Completing the picture,
54+
`unsafe Trait` also defines an obligation, that is discharged by `unsafe impl`.
55+
Curiously, `unsafe impl` does *not* implicitly make all bodies of this `impl`
56+
unsafe blocks, which is somewhat inconsistent with `unsafe fn`.)
57+
58+
[unsafe-dual]: https://github.com/rust-lang/rfcs/pull/2585#issuecomment-577852430
5959

6060
# Guide-level explanation
6161
[guide-level-explanation]: #guide-level-explanation

0 commit comments

Comments
 (0)