Skip to content

Commit 643f769

Browse files
committed
Significant revisions and clarifications
1 parent c6f3b0d commit 643f769

File tree

1 file changed

+67
-21
lines changed

1 file changed

+67
-21
lines changed

active/0000-unsafe-api-location.md

Lines changed: 67 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ The brief summary is:
1212
* Unsafe APIs should be made into methods or static functions in the same cases
1313
that safe APIs would be.
1414

15-
* `raw` submodules should be used only to provide APIs directly on low-level
15+
* `raw` submodules should be used only to *define* explicit low-level
1616
representations.
1717

1818
# Motivation
@@ -30,10 +30,10 @@ provides:
3030
as well as a `from_utf8` variant that does not actually check for utf8
3131
validity.
3232

33-
The problem is that currently, there is no consistent guideline about which of
34-
these APIs should live as methods/static functions associated with a type, and
35-
which should live in a `raw` submodule. Both forms appear throughout the
36-
standard library.
33+
The problem is that currently, there is no clear/consistent guideline about
34+
which of these APIs should live as methods/static functions associated with a
35+
type, and which should live in a `raw` submodule. Both forms appear throughout
36+
the standard library.
3737

3838
# Detailed design
3939

@@ -42,35 +42,66 @@ The proposed convention is:
4242
* When an unsafe function/method is clearly "about" a certain type (as a way of
4343
constructing, destructuring, or modifying values of that type), it should be a
4444
method or static function on that type. This is the same as the convention for
45-
placement of safe functions/methods.
45+
placement of safe functions/methods. So functions like
46+
`string::raw::from_parts` would become static functions on `String`.
4647

47-
* When an unsafe function/method is specifically for producing or consuming the
48-
underlying representation of a data structure (which is otherwise private),
49-
the API should use `raw` in its name. Specifically, `from_raw_parts` is the
50-
typical name used for constructing a value from e.g. a pointer-based
51-
representation.
48+
* `raw` submodules should only be used to *define* low-level
49+
types/representations (and methods/functions on them). Methods for converting
50+
to/from such low-level types should be available directly on the high-level
51+
types. Examples: `core::raw`, `sync::raw`.
5252

53-
* When an unsafe function/method is an unchecked variant of an otherwise safe
54-
API, it should be marked using an `_unchecked` suffix.
53+
The benefits are:
5554

56-
* `raw` submodules should only be used to provide APIs directly on low-level
57-
representations, separately from functions/methods for converting to/from such
58-
raw representations.
55+
* *Ergonomics*. You can gain easy access to unsafe APIs merely by having a value
56+
of the type (or, for static functions, importing the type).
5957

60-
The benefit to moving unsafe APIs into methods (resp. static functions) is the
61-
usual one: you can gain easy access to these APIs merely by having a value of
62-
the type (resp. importing the type).
58+
* *Consistency and simplicity*. The rules for placement of unsafe APIs are the
59+
same as those for safe APIs.
6360

6461
The perspective here is that marking APIs `unsafe` is enough to deter their use
6562
in ordinary situations; they don't need to be further distinguished by placement
6663
into a separate module.
6764

65+
There are also some naming conventions to go along with unsafe static functions
66+
and methods:
67+
68+
* When an unsafe function/method is an unchecked variant of an otherwise safe
69+
API, it should be marked using an `_unchecked` suffix.
70+
71+
For example, the `String` module should provide both `from_utf8` and
72+
`from_utf8_unchecked` constructors, where the latter does not actually check
73+
the utf8 encoding. The `string::raw::slice_bytes` and
74+
`string::raw::slice_unchecked` functions should be merged into a single
75+
`slice_unchecked` method on strings that checks neither bounds nor utf8
76+
boundaries.
77+
78+
* When an unsafe function/method produces or consumes a low-level representation
79+
of a data structure, the API should use `raw` in its name. Specifically,
80+
`from_raw_parts` is the typical name used for constructing a value from e.g. a
81+
pointer-based representation.
82+
83+
* Otherwise, *consider* using a name that suggests *why* the API is unsafe. In
84+
some cases, like `String::as_mut_vec`, other stronger conventions apply, and the
85+
`unsafe` qualifier on the signature (together with API documentation) is
86+
enough.
87+
88+
The unsafe methods and static functions for a given type should be placed in
89+
their own `impl` block, at the end of the module defining the type; this will
90+
ensure that they are grouped together in rustdoc. (Thanks @kballard for the
91+
suggestion.)
92+
6893
# Drawbacks
6994

7095
One potential drawback of these conventions is that the documentation for a
7196
module will be cluttered with rarely-used `unsafe` APIs, whereas the `raw`
7297
submodule approach neatly groups these APIs. But rustdoc could easily be
73-
changed to either hide or separate out `unsafe` APIs by default.
98+
changed to either hide or separate out `unsafe` APIs by default, and in the
99+
meantime the `impl` block grouping should help.
100+
101+
More specifically, the convention of placing unsafe constructors in `raw` makes
102+
them very easy to find. But the usual `from_` convention, together with the
103+
naming conventions suggested above, should make it fairly easy to discover such
104+
constructors even when they're supplied directly as static functions.
74105

75106
More generally, these conventions give `unsafe` APIs more equal status with safe
76107
APIs. Whether this is a *drawback* depends on your philosophy about the status
@@ -81,7 +112,7 @@ is no reason to penalize its ergonomics given that it's opt-in anyway.*
81112

82113
# Alternatives
83114

84-
There are two main alternatives:
115+
There are a few alternatives:
85116

86117
* Rather than providing unsafe APIs directly as methods/static functions, they
87118
could be grouped into a single extension trait. For example, the `String` type
@@ -93,6 +124,21 @@ There are two main alternatives:
93124
them `unsafe`), and given that rustdoc could easily provide API grouping, it's
94125
unclear exactly what the benefit is.
95126

127+
* ([Suggested by @kballard](https://github.com/rust-lang/rfcs/pull/240#issuecomment-55635468)):
128+
129+
> Use `raw` for functions that construct a value of the type without checking
130+
> for one or more invariants.
131+
132+
The advantage is that it's easy to find such invariant-ignoring functions. The
133+
disadvantage is that their ergonomics is worsened, since they much be
134+
separately imported or referenced through a lengthy path:
135+
136+
```rust
137+
// Compare the ergonomics:
138+
string::raw::slice_unchecked(some_string, start, end)
139+
some_string.slice_unchecked(start, end)
140+
```
141+
96142
* Use `raw` submodules to group together *all* manipulation of low-level
97143
representations. No module in `std` currently does this; existing modules
98144
provide some free functions in `raw`, and some unsafe methods, without a clear

0 commit comments

Comments
 (0)