You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/StandardLibraryProgrammersManual.md
+223-1Lines changed: 223 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,10 +12,232 @@ In this document, "stdlib" refers to the core standard library (`stdlib/public/c
12
12
13
13
### Formatting Conventions
14
14
15
-
The stdlib currently has a hard line length limit of 80 characters. To break long lines, please closely follow the indentation conventions you see in the existing codebase. (FIXME: Describe.)
15
+
The Standard Library codebase has some uniformly applied formatting conventions. While these aren't currently automatically enforced, we still expect these conventions to be followed in every PR, including draft PRs. (PRs are first and foremost intended to be read/reviewed by people, and it's crucial that trivial formatting issues don't get in the way of understanding proposed changes.)
16
+
17
+
Some of this code is very subtle, and its presentation matters greatly. Effort spent on getting formatting _just right_ is time very well spent: new code we add is going to be repeatedly read and re-read by many people, and it's important that code is presented in a way that helps understanding it.
18
+
19
+
#### Line Breaking
20
+
21
+
The stdlib currently has a hard line length limit of 80 characters. This allows code to be easily read in environments that don't gracefully handle long lines, including (especially!) code reviews on GitHub.
16
22
17
23
We use two spaces as the unit of indentation. We don't use tabs.
18
24
25
+
To break long lines, please closely follow the indentation conventions you see in the existing codebase. (FIXME: Describe in detail.)
26
+
27
+
Our primary rule is that if we need to insert a line break anywhere in the middle of a list (such as arguments, tuple or array/dictionary literals, generic type parameters, etc.), then we must go all the way and put each item on its own line, indented by +1 unit, even if some of the items would fit on a single line together.
28
+
29
+
The rationale for this is that line breaks tend to put strong visual emphasis on the item that follows them, risking subsequent items on the same line to be glanced over during review. For example, see how easy it is to accidentally miss `arg2` in the second example below.
As a special case, function arguments that are very tightly coupled together are sometimes kept on the same line. The typical example for this is a pair of defaulted file/line arguments that track the caller's source position:
56
+
57
+
```swift
58
+
// OK
59
+
internalfunc_preconditionFailure(
60
+
_ message:StaticString=StaticString(),
61
+
file: StaticString=#file, line: UInt=#line
62
+
) ->Never {
63
+
...
64
+
}
65
+
66
+
// Also OK
67
+
internalfunc_preconditionFailure(
68
+
_message: StaticString=StaticString(),
69
+
file: StaticString=#file,
70
+
line: UInt=#line
71
+
) ->Never {
72
+
...
73
+
}
74
+
```
75
+
76
+
(When in doubt, err on the side of adding more line breaks.)
77
+
78
+
79
+
For lists that have delimiter characters (`(`/`)`, `[`/`]`, `<`/`>`, etc.), we prefer to put a line break both *after* the opening delimiter, and *before* the closing delimiter.
80
+
However, within function bodies, it's okay to omit the line break before the closing delimiter.
81
+
82
+
```swift
83
+
// GOOD:
84
+
func foo<S:Sequence, T>(
85
+
input: S,
86
+
transform: (S.Element) ->throws T
87
+
) -> [S.Element] { // Note: there *must* be a line break before the ')'
88
+
...
89
+
someLongFunctionCall(
90
+
on: S,
91
+
startingAt: i,
92
+
stride: 32) // Note: the break before the closing paren is optional
93
+
}
94
+
```
95
+
96
+
If the entire contents of a list fit on a single line, it is okay to only break at the delimiters. That said, it is also acceptable to put breaks around each item:
The rules typically don't require breaking lines that don't exceed the length limit; but if you find it helps understanding, feel free to do so anyway.
119
+
120
+
```swift
121
+
// OK
122
+
guardlet foo = foo else { returnfalse }
123
+
124
+
// Also OK
125
+
guardlet foo = foo else {
126
+
returnfalse
127
+
}
128
+
```
129
+
130
+
Historically, we had a one (1) exception to the line limit, which is that we allowed string literals to go over the margin. Now that Swift has multi-line string literals, we could start breaking overlong ones. However, multiline literals can be a bit heavy visually, whilein most cases the string is a precondition failure message, which doesn't necessarily need to be emphasized as much -- so the old exception still applies:
131
+
132
+
```swift
133
+
// OK
134
+
_precondition( |
135
+
buffer.baseAddress== firstElementAddress, |
136
+
"Can't reassign buffer in Array(unsafeUninitializedCapacity:initializingWith:)"
137
+
) |
138
+
|
139
+
// Also OK, although spending 4 lines on the message is a bit much |
In every other case, long lines must be broken up. We expect this rule to be strictly observed.
150
+
151
+
152
+
#### Presentation of Type Definitions
153
+
154
+
To ease reading/understanding type declarations, we prefer to define members in the following order:
155
+
156
+
1. Crucial type aliases and nested types, not exceeding a handful of lines in length
157
+
2. Stored properties
158
+
3. Initializers
159
+
4. Any other instance members (methods, computed properties, etc)
160
+
161
+
Please keep all stored properties together in a single uninterrupted list, followed immediately by the type's most crucial initializer(s). Put these as close to the top of the type declaration as possible -- we don't want to force readers to scroll around to find these core definitions.
162
+
163
+
We also have some recommendations for defining other members. These aren't strict rules, as the best way to present definitions varies; but it usually makes sense to break up the implementation into easily digestable, logical chunks.
164
+
165
+
- In general, it is a good idea to keep the main `struct`/`class` definiton as short as possible: preferably it should consist of the type's stored properties and a handful of critical initializers, and nothing else.
166
+
167
+
- Everything else should go in standalone extensions, arranged by logical theme. For example, it's often nice to define protocol conformances in dedicated extensions. If it makes sense, feel free to add a comment to title these sectioning extensions.
168
+
169
+
- Think about what order you present these sections -- put related conformances together, follow some didactic progression, etc. E.g, conformance definitions for closely related protocols such as `Equatable`/`Hashable`/`Comparable` should be kept very close to each other, for easy referencing.
170
+
171
+
- In some cases, it can also work well to declare the most essential protocol conformances directly on the type definition; feel free to do so if it helps understanding. (You can still implement requirements in separate extensions in this case, or you can do it within the main declaration.)
172
+
173
+
- It's okay for the core type declaration to forward reference large nested types or static members that are defined in subsequent extensions. It's often a good idea to define these in an extension immediately following the type declaration, but this is not a strict rule.
174
+
175
+
Extensions are a nice way to break up the implementation into easily digestable chunks, but they aren't the only way. The goal is to make things easy to understand -- if a type is small enough, it may be best to list every member directly in the `struct`/`class` definition, while for huge types it often makes more sense to break them up into a handful of separate source files instead.
176
+
177
+
```swift
178
+
// BAD (a jumbled mess)
179
+
struct Foo: RandomAccessCollection, Hashable {
180
+
var count: Int { ... }
181
+
182
+
struct Iterator: IteratorProtocol { /* hundreds of lines */ }
0 commit comments