Skip to content

Commit 6a42b44

Browse files
committed
Add a section about making encapsulation of unsafety more explicit
1 parent 1f5a3ec commit 6a42b44

File tree

1 file changed

+32
-0
lines changed

1 file changed

+32
-0
lines changed

proposals/nnnn-strict-memory-safety.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,38 @@ This approach reduces the annotation burden in unsafe code, but makes it much ha
448448

449449
Rust's `unsafe` functions have this behavior, where an `unsafe fn` in Rust implies an `unsafe { ... }` block around the entire function body. [Rust RFC #2585](https://rust-lang.github.io/rfcs/2585-unsafe-block-in-unsafe-fn.html) argues for Rust to remove this behavior; the motivation there generally applies to Swift as well.
450450

451+
### Making "encapsulation" of unsafe behavior explicit
452+
453+
In the proposed design, a function with no unsafe types in its signature is considered safe unless the programmer explicitly marked it `@unsafe`. The implementation may contain any amount of unsafe code, so long as it is covered by an `unsafe` expression:
454+
455+
```swift
456+
extension Array<Int> {
457+
// this function is considered safe
458+
func sum() -> Int {
459+
unsafe withUnsafeBufferPointerSimplified { buffer in
460+
unsafe c_library_sum_function(buffer.baseAddress, buffer.count, 0)
461+
}
462+
}
463+
}
464+
```
465+
466+
This differs somewhat from the way in which throwing and asynchronous functions work. A function that has a `try` or `await` in the body needs to be `throws` or `async`, respectively. Essentially, the effect from the body has to also be reflected in the signature. With unsafe code, this could mean that having `unsafe` expressions in the function body requires you to either make the function `@unsafe` or use some other suppression mechanism to acknowledge that you are using unsafe constructs to provide a safe interface.
467+
468+
There are several options for such a suppression mechanism. An attribute form, `@safe(unchecked)`, is described below as an alternative to the `unsafe` expression. Another approach would be to provide an `unsafe!` form the `unsafe` expression, which (like `try!`) acknowledges the effect but doesn't propagate that effect out to the function. For the `sum` function, it would be used as follows:
469+
470+
```swift
471+
extension Array<Int> {
472+
// this function is considered safe
473+
func sum() -> Int {
474+
unsafe! withUnsafeBufferPointerSimplified { buffer in
475+
unsafe! c_library_sum_function(buffer.baseAddress, buffer.count, 0)
476+
}
477+
}
478+
}
479+
```
480+
481+
This proposal chooses not to go down this path, because having a function signature involving no unsafe types is already a strong indication that the function is providing a safe interface, and there is little to be gained from requiring additional ceremony (whether an attribute like `@safe(unchecked)` or the `unsafe!` form described above).
482+
451483
### `@safe(unchecked)` attribute to allow unsafe code
452484

453485
Early iterations of this proposal introduced a `@safe(unchecked)` attribute as an alternative to `unsafe` expressions. The `@safe(unchecked)` attribute would be placed on a function to suppress diagnostics about use of unsafe constructs within its definition. For our `sum` example, this means one would write:

0 commit comments

Comments
 (0)