Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion better-code/src/chapter-2-contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -1138,7 +1138,7 @@ contract is an engineering decision you will have to make. To reduce
the risk you could add this assertion[^checks], which will stop the program if
the ordering is strict-weak:

```swift
```swift,ignore
precondition(
self.isEmpty || areInOrder(first!, first!),
"Total preorder required; did you pass a strict-weak ordering?")
Expand Down
65 changes: 46 additions & 19 deletions better-code/src/chapter-3-errors.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ or misspelled. Guessing correctly affects not only the quality of the
error message, but also whether further diagnostics will be
useful. For example, in this code, the `while` keyword is misspelled:

```swift
```swift,ignore
func f(x: inout Int) {
whilee x < 10 {
x += 1
Expand Down Expand Up @@ -208,12 +208,14 @@ Swift supplies a function for checking that a precondition is upheld,
which can be used as follows:

```swift
# let n = 0
precondition(n >= 0)
```

*or*

```swift
# let n = 0
precondition(n >= 0, "n == \(n); it must be non-negative.")
```

Expand Down Expand Up @@ -243,6 +245,8 @@ checks at function boundaries. For example, in the binary search
algorithm mentioned in the previous chapter,

```swift
# var l = 0
# var h = 10
// precondition: l <= h
let m = (h - l) / 2
h = l + m
Expand All @@ -258,6 +262,8 @@ help us uncover those flaws during testing of debug builds without
impacting performance of release builds:

```swift
# var l = 0
# var h = 10
assert(l <= h)
let m = (h - l) / 2
h = l + m
Expand Down Expand Up @@ -304,11 +310,12 @@ operations must never be turned off in release builds.
extension Array {
/// Exchanges the first and last elements.
mutating func swapFirstAndLast() {
precondition(!self.isEmpty)
if count() == 1 { return } // swapping would be a no-op.
withUnsafeBufferPointer { b in
f = b.baseAddress
l = f + b.count - 1
precondition(!isEmpty)
if count == 1 { return } // swapping would be a no-op.
withUnsafeMutableBufferPointer { b in
guard let base = b.baseAddress else { return }
var f = base
var l = base + (b.count - 1)
swap(&f.pointee, &l.pointee)
}
}
Expand All @@ -335,14 +342,14 @@ code in an unfinished state):
/// Returns the number of unused elements when a maximal
/// number of `n`-element chunks are stored in `self`.
func excessWhenFilled(withChunksOfSize n: Int) {
count() % n // n == 0 would violate the precondition of %
count % n // n == 0 would violate the precondition of %
}
}
```

2. Something your function uses can itself report a runtime error:

```swift
```swift,ignore
extension Array {
/// Writes a textual representation of `self` to a temporary file
/// whose location is returned.
Expand Down Expand Up @@ -395,11 +402,12 @@ It's appropriate to add a precondition when:
we should instead propagate the error:

```swift
# import Foundation
extension Array {
/// Writes a textual representation of `self` to a temporary file
/// whose location is returned.
func writeToTempFile(withChunksOfSize n: Int) throws -> URL {
let r = FileManager.defaultTemporaryDirectory
let r = FileManager.default.temporaryDirectory
.appendingPathComponent(UUID().uuidString)
try "\(self)".write(to: r, atomically: false, encoding: .utf8)
return r
Expand Down Expand Up @@ -435,6 +443,9 @@ features to accommodate it without causing this kind of repeated
boilerplate:

```swift
# func thing1ThatCanFail() -> Result<Int, Error> { .success(0) }
# func thing2ThatCanFail() -> Result<Int, Error> { .success(0) }
# func _resultPropagationDemo() -> Result<Int, Error> {
let someValueOrError = thing1ThatCanFail()
guard case .success(let someValue) = someValueOrError else {
return someValueOrError
Expand All @@ -444,13 +455,17 @@ let otherValueOrError = thing2ThatCanFail()
guard case .success(let otherValue) = otherValueOrError else {
return otherValueOrError
}
# return .success(otherValue)
# }
```


Swift's thrown errors fill that role by propagating errors upward with
a simple `try` label on an expression containing the call.

```swift
# func thing1ThatCanFail() throws -> Int { 0 }
# func thing2ThatCanFail() throws -> Int { 0 }
let someValue = try thing1ThatCanFail()
let otherValue = try thing2ThatCanFail()
```
Expand Down Expand Up @@ -536,6 +551,8 @@ whose primary home is the summary sentence fragment.[^result-doc]
as though they just return a `T`:

```swift
# import Foundation
# enum IOError: Error {}
extension Array {
/// Writes a textual representation of `self` to a temporary file,
/// returning its location.
Expand Down Expand Up @@ -592,18 +609,19 @@ The following code uses that guarantee to ensure that all the
allocated buffers are eventually freed.

```swift
# struct X {}
/// Processes each element of `xs` in an order determined by the
/// [total
/// preorder](https://en.wikipedia.org/wiki/Weak_ordering#Total_preorders)
/// `areInOrder` using a distinct 1Kb buffer for each one.
func f(_ xs: [X], orderedBy areInOrder: (X, X) throws -> Bool) rethrows
{
var buffers = xs.map { x in
(p, UnsafeMutablePointer<UInt8>.allocate(capacity: 1024)) }
defer { for _, b in buffers { b.deallocate() } }
(x, UnsafeMutablePointer<UInt8>.allocate(capacity: 1024)) }
defer { for (_, b) in buffers { b.deallocate() } }

buffers.sort { !areInOrder($1.0, $0.0) }
...
try buffers.sort { !(try areInOrder($1.0, $0.0)) }
// ...
}
```

Expand Down Expand Up @@ -656,6 +674,7 @@ making it a precondition that the comparison is a total preorder, we
could weaken the postcondition as follows:

```swift
# extension Array {
/// Sorts the elements so that all adjacent pairs satisfy
/// `areInOrder`, or permutes the elements in an unspecified way if
/// `areInOrder` is not a [total
Expand All @@ -665,6 +684,7 @@ could weaken the postcondition as follows:
/// - Complexity: at most N log N comparisons, where N is the number
/// of elements.
mutating func sort(areInOrder: (Element, Element)->Bool) { ... }
# }
```

As you can see, this change makes the API more complicated to no
Expand Down Expand Up @@ -783,8 +803,10 @@ manage that is with a `defer` block releasing the resources
immediately after they are allocated:

```swift
let f = try FileHandle(forReadingFrom: p)
defer { f.close() }
# import Foundation
# let p = URL(fileURLWithPath: "/tmp/example.txt")
let f: FileHandle = try .init(forReadingFrom: p)
defer { try? f.close() }
// use f
```

Expand All @@ -793,15 +815,16 @@ scope where they were allocated, you can tie them to the `deinit` of
some type:

```swift
# import Foundation
struct OpenFileHandle: ~Copyable {
/// The underlying type with unmanaged close functionality
private let raw: FileHandle

/// An instance for reading from p.
init(forReadingFrom p: URL) { raw = .init(forReadingFrom: p) }
init(forReadingFrom p: URL) { raw = try! .init(forReadingFrom: p) }

deinit {
raw.close()
try? raw.close()
}
}
```
Expand Down Expand Up @@ -864,16 +887,20 @@ invariants. For example, imagine a disk-backed version of `PairArray`
from the last chapter, where I/O operations can throw:

```swift
# struct DiskBackedArray<Element> {
# init() {}
# mutating func append(_: Element) throws {}
# }
/// A disk-backed series of `(X, Y)` pairs, where the `X`s and `Y`s
/// are stored in separate files.
struct DiskBackedPairArray<X, Y> {
// Invariant: `xs.count == ys.count`

/// The first part of each element.
private var xs = DiskBackedArray()
private var xs: DiskBackedArray<X> = DiskBackedArray()

/// The second part of each element.
private var ys = DiskBackedArray()
private var ys: DiskBackedArray<Y> = DiskBackedArray()

// ...

Expand Down
15 changes: 14 additions & 1 deletion better-code/src/chapter-4-algorithms.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ remove a selected shape from an array. The naive implementation is a loop that
scans for the shape and erases it.

```swift
# struct Shape { var isSelected: Bool }
/// Removes the selected shape.
func removeSelected(shapes: inout [Shape]) {
for i in 0..<shapes.count {
Expand All @@ -44,6 +45,7 @@ remaining ones, so the code must handle an additional case.

<!-- bad -->
```swift
# struct Shape { var isSelected: Bool }
/// Remove all selected shapes.
func removeAllSelected(shapes: inout [Shape]) {
var i = 0
Expand All @@ -63,6 +65,7 @@ subsequent indices are affected by the removal this removes the fix-up.

<!-- bad -->
```swift
# struct Shape { var isSelected: Bool }
/// Remove all selected shapes.
func removeAllSelected(shapes: inout [Shape]) {
for i in (0..<shapes.count).reversed() {
Expand Down Expand Up @@ -120,6 +123,7 @@ Both approaches will preserve the relative
order of the unselected shapes. Now we can write the code:

```swift
# struct Shape { var isSelected: Bool }
/// Remove all selected shapes.
func removeAllSelected(shapes: inout [Shape]) {
var p = 0
Expand Down Expand Up @@ -208,6 +212,15 @@ extension MutableCollection {
Given `halfStablePartition()` we can rewrite `removeAllSelected()`.

```swift
# struct Shape { var isSelected: Bool }
# extension MutableCollection {
# /// See the full `halfStablePartition` implementation earlier in this chapter.
# mutating func halfStablePartition(
# by belongsInSecondPartition: (Element) -> Bool
# ) -> Index {
# fatalError()
# }
# }
func removeAllSelected(shapes: inout [Shape]) {
shapes.removeSubrange(shapes.halfStablePartition(by: { $0.isSelected })...)
}
Expand Down Expand Up @@ -459,7 +472,7 @@ clarity, and enable efficient computation.

<!-- Test to get swift embedded in mdbook, need to build the infrastructure for testing swift code.-->
```swift
{{#include test.swift:2:5}}
{{#include test.swift}}
```


Expand Down
3 changes: 3 additions & 0 deletions better-code/src/chapter-5-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ is why `successors_` is private.
The meaning of a type implies a set of operations. For example,

```swift
# struct Point2D {
# let x, y: Float
# }
extension Point2D {
/// Returns the length of the line segment between `self` and
/// `other`.
Expand Down
Loading