Skip to content

Commit f6f7537

Browse files
author
Ben Cohen
committed
Add a draft chapter to the Language Guide for Ownership
1 parent f69e312 commit f6f7537

File tree

2 files changed

+399
-0
lines changed

2 files changed

+399
-0
lines changed
Lines changed: 398 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,398 @@
1+
# Ownership
2+
3+
Control the ability to copy and store types.
4+
5+
By default, types you write in Swift gain certain capabilities without you
6+
having to write any code.
7+
Variables of these types can be copied into other local or global variables,
8+
and into stored properties of another type.
9+
They can also be passed to any function that accepts that type.
10+
This is made possible because Swift generates the code to handle the copying
11+
of a type, and inserts calls to this copying code automatically if needed.
12+
For classes, this involves incrementing the reference count of the object.
13+
For structures, it involves copying the individual stored properties into a
14+
new instance.
15+
For enumerations, the associated values of the case are copied.
16+
17+
> **Note**
18+
> Swift does not allow you to customize the way a value is copied,
19+
> or detect when a value is being copied.
20+
> The only way to determine if a reference to a class instance has been copied
21+
> is afterwards, using the `isKnownUniquelyReferenced` function, which
22+
> returns `true` if there is more than one variable referencing the class.
23+
> Value types that hold a class reference in a stored property can use this
24+
> to detect that they have been copied. This is how types like `Array`
25+
> and `String` implement their copy-on-write behavior.
26+
27+
This default assumption of copyability is the right choice for most use cases.
28+
However, in some cases it is important to constrain the ability to make
29+
copies of an instance.
30+
Often this is because the type controls a resource,
31+
such as a file handle or memory allocation,
32+
and allowing copies would interfere with goals such as correctness
33+
or runtime performance.
34+
35+
## Supressing Copyability
36+
37+
You can supress Swift's default assumption that a type is copyable
38+
using the `~Copyable` constraint:
39+
40+
```swift
41+
struct File: ~Copyable {
42+
private let handle: Int32
43+
44+
init(named name: String) {
45+
// Open file using sytem API,
46+
// storing the file handle.
47+
}
48+
var size: Int {
49+
// Retrieve size of file from
50+
// file system using handle.
51+
}
52+
}
53+
```
54+
55+
The inclusion of the `~Copyable` constraint on the structure definition stops
56+
the compiler from automatically adding the ability to copy the `File` type.
57+
This constraint must appear on the declaration of the type, it cannot be
58+
added in an extension.
59+
60+
Since the type does not support copying, Swift prevents you from
61+
performing operations that would require copying:
62+
63+
```swift
64+
let originalFileVariable = File(named: "swift.txt")
65+
print(originalFile.handle)
66+
67+
let secondFileVariable = originalFile
68+
print(originalFile.size)
69+
// This is a compile-time error: originalFileVariable cannot
70+
// be used after its value is assigned to secondFileVariable.
71+
```
72+
73+
Assigning the file instance to a new variable does not involve making a copy
74+
of the value, because `File` does not support copying.
75+
This assignment is referred to as "consuming" the original value.
76+
You cannot use a variable after its value is consumed.
77+
In reporting the error,
78+
the Swift compiler will show the location where the value was consumed,
79+
and where it is subsequently used.
80+
81+
In some cases, it is not permitted to consume a value. Global variables
82+
cannot be consumed, as it would not be possible to prevent them
83+
from subsequent use in other functions:
84+
85+
```swift
86+
var globalFileVariable = File(named: "swift.txt")
87+
88+
func process() {
89+
let localFileVariable = globalFile
90+
// This is a compile-time error: global variables cannot be consumed.
91+
}
92+
```
93+
94+
The `Optional` type has the ability to take a noncopyable value,
95+
leaving a `nil` behind,
96+
which you can use if a global variable needs to be consumable:
97+
98+
```swift
99+
var globalFileVariable: File? = File(named: "swift.txt")
100+
101+
func process() {
102+
if let localFile = globalFile.take() {
103+
// Use localFile, which now owns the File
104+
// instance previosuly held in the global
105+
// variable, which now holds nil.
106+
} else {
107+
// handle nil value for the file
108+
}
109+
}
110+
```
111+
112+
When working with enumerations (including `Optional`), associated values
113+
can be extracted temporarily into a variable using Swift's pattern matching:
114+
115+
```swift
116+
func process() {
117+
switch globalFileVariable {
118+
case let localFile?:
119+
// Use localFile, the wrapped value temporarily
120+
// borrowed from the global optional variable.
121+
case nil:
122+
// handle nil value for the global file variable
123+
}
124+
}
125+
```
126+
127+
## Deinitialzers on structures
128+
129+
As described in <doc:Deinitialization>, class types can be given code that
130+
runs when the class is destroyed.
131+
Copyable structures and enumerations cannot have a deinitializer.
132+
When these types are noncopyable, they can,
133+
because they have unique ownership of their data:
134+
135+
```swift
136+
struct File: ~Copyable {
137+
let handle: Int32
138+
139+
init(from name: String) {
140+
// open file and store file handle
141+
}
142+
143+
deinit {
144+
// Since file handle cannot be shared
145+
// with any other instance, it can
146+
// be closed here.
147+
}
148+
}
149+
```
150+
151+
This allows a noncopyable structure to be used to manage resources,
152+
in a similar manner to classes.
153+
There are pros and cons to using a noncopyable type in this way.
154+
Unlike classes, noncopyable types are uniquely owned and do not need
155+
reference counting and heap allocation, reducing runtime overhead.
156+
They also cannot be shared, making it easier to ensure they are safely
157+
`Sendable` and reducing the overhead needed for Swift to ensure
158+
<doc:MemorySafety>. This comes at the cost that working with noncopyable
159+
values is less convenient, as seen with the global variable example above.
160+
161+
## Types Holding Noncopyable Values
162+
163+
If a type holds another noncopyable type as a stored property, that type
164+
also cannot be copyable, since there would be no way to perform a full
165+
copy of each of the type's stored properties:
166+
167+
```swift
168+
struct Package {
169+
var manifest: File
170+
// This is a compile-time error: Package is Copyable
171+
}
172+
```
173+
174+
The containing type must also be marked as noncopyable:
175+
176+
```swift
177+
struct Package: ~Copyable {
178+
var manifest: File
179+
}
180+
```
181+
182+
Since class instances are not copied – only references to them – a class type
183+
can hold noncopyable types.
184+
185+
As `Array` and `Dictionary` are copyable types, they cannot hold noncopyable
186+
elements. `InlineArray` however is only conditionally copyable, as described
187+
below, so can hold noncopyable elements.
188+
189+
## Specifying Ownership in Functions
190+
191+
Functions that operate on noncopyable types require additional information
192+
on their parameters, specifying whether the function is _borrowing_ or
193+
_consuming_ the value.
194+
195+
This is done with an additional modifier. If the function is only borrowing
196+
the value, and will not be keeping it, then the `borrowing` keyword is used:
197+
198+
```swift
199+
func printSize(of file: borrowing File) {
200+
print("The file is \(file.size) bytes")
201+
}
202+
```
203+
204+
If the function needs to keep the value, it must use the `consuming` keyword.
205+
The most common case is with initializers:
206+
207+
```swift
208+
init(manifest: consuming File) {
209+
self.manifest = manifest
210+
}
211+
```
212+
213+
If a function borrows a parameter, it cannot then consume it in its implementation:
214+
215+
```swift
216+
init(manifest: borrowing File) {
217+
self.manifest = manifest
218+
// This is a compile-time error: manifest is borrowed and cannot be consumed.
219+
}
220+
```
221+
222+
If a function consumes an argument, that means it can no longer be used after
223+
being passed in:
224+
225+
```swift
226+
let package = Package(manifest: file)
227+
print(file.size)
228+
// This is a compile-time error: file cannot be used after it is consumed.
229+
```
230+
231+
> **Note**
232+
> With copyable types, Swift follows the convention of borrowing arguments
233+
> passed to functions, except for initializers that consume their arguments.
234+
> This is optimal for minimizing reference counting. The `borrowing` and
235+
> `consuming` keywords can also be used with copyable types to change these
236+
> defaults. For example, the `append` method on `Array` is annotated as
237+
> consuming the element to be appended.
238+
239+
Methods on noncopyable types can also be marked as `consuming` if they
240+
must represent the final use of a type:
241+
242+
```swift
243+
extension File {
244+
// Explicitly close the file, rather than allowing the deinit to do it.
245+
// The file should not be used after it is closed.
246+
consuming func close() {
247+
discard self
248+
// call the system API to close a file handle
249+
}
250+
}
251+
```
252+
253+
Here, the `discard` keyword has been used to indicate that the `deinit` for
254+
this type should not run. In the case of `File`, this is necessary to avoid
255+
the file handle being closed twice.
256+
257+
## Generics and Copyability
258+
259+
In order to conform a noncopyable type to a protocol, that protocol cannot
260+
assume the conforming type is copyable. Like with types, this is expressed
261+
with the `~Copyable` constraint:
262+
263+
```swift
264+
protocol SizeProviding: ~Copyable {
265+
var size: Int { get }
266+
}
267+
```
268+
269+
This annotation states that a conformance to `SizeProvididing` cannot
270+
mean the conforming value will be copyable. Without this, a copyable type could
271+
not conform, because extensions on the protocol might make copies of `Self`
272+
in their implementations – something they are free to do in extensions
273+
to copyable protocols.
274+
275+
> **Note**
276+
> Until now, we have seen `~Copyable` applied to type declarations to
277+
> make them noncopyable. Adding a `~Copyable` constraint to a protocol
278+
> is not as strict. It means conforming types do not _have_ to be
279+
> copyable. But this does not prevent copyable types from conforming to
280+
> the protocol. For this reason, whenever you see it, `~Copyable` should be
281+
> viewed as meaning "don't assume a type is copyable" rather than
282+
> "a type is not copyable".
283+
284+
Since `SizeProviding` does not require its conforming types to be copyable,
285+
the `File` type can conform to it:
286+
287+
```swift
288+
extension File: SizeProviding { }
289+
// requirements are fulfilled by File's existing API
290+
```
291+
292+
If `SizeProviding` had not included the `~Copyable` constraint, this
293+
conformance would have generated a compile-time error that `File' does not
294+
conform to protocol 'Copyable'.
295+
296+
297+
Just like with protocols, generic placeholders can be annotated with the
298+
`~Copyable` constraint, allowing both noncopyable types to be used for the
299+
generic type. An example of this is the declaration of the `Optional` type:
300+
301+
```
302+
enum Optional<Wrapped: ~Copyable>: ~Copyable {
303+
case some(Wrapped)
304+
case none
305+
}
306+
```
307+
308+
The `Optional` type can therefore wrap both noncopyable types (such as the
309+
`File` type above) and copyable types.
310+
311+
Just as a type that wraps a noncopyable stored property must itself be
312+
noncopyable, the `Optional` enum must be noncopyable in order to be able
313+
to hold a _potentially_ noncopyable value. "Potentially" noncopyable because,
314+
just like with the protocol constraint, `~Copyable` here only means "don't
315+
assume this value is copyable".
316+
317+
## Extensions and Copyability
318+
319+
Marking a protocol as `~Copyable` makes it more flexible: it can be conformed
320+
to by both copyable and noncopyable types. A protocol might be marked
321+
`~Copyable` even though nearly every type conforming to it is actually
322+
copyable.
323+
324+
The same goes for generic types. Allowing a type to work with both copyable
325+
and noncopyable types makes it more useful. This is why Swift's `Optional'
326+
type is written to work this way.
327+
328+
In Swift, most code is written assuming copyability. This default assumption
329+
extends to protocol extensions and extensions on generic types. By default,
330+
Swift assumes copyability of all the types involved in your extension.
331+
332+
Here is an extension on `Optional` that either unwraps the value,
333+
or throws an error if the optional is nil:
334+
335+
```swift
336+
extension Optional {
337+
struct UnwrapFailure: Error { }
338+
339+
func unwrapOrThrow() throws -> Wrapped {
340+
guard let value = self else { throw UnwrapFailure() }
341+
return value
342+
}
343+
}
344+
```
345+
346+
This code assumes that the value wrapped by the optional is copyable. If it
347+
was not, then it cannot just be unwrapped and returned because it would
348+
now be in two places: still inside the optional, and returned to the caller.
349+
This would require making a copy.
350+
351+
This code does compile without errors, because Swift assumes that extensions
352+
by default only apply to copyable elements.
353+
354+
To change this code to apply to noncopyable elements too, you would need
355+
to explicitly state this in the extension, and also mark the function
356+
as consuming.
357+
358+
```swift
359+
extension Optional where Wrapped: ~Copyable {
360+
consuming func unwrapOrThrow() throws -> Wrapped {
361+
guard let value = self else { throw UnwrapFailure() }
362+
return value
363+
}
364+
}
365+
```
366+
367+
Since the function is marked consuming, the optional value
368+
would no longer be available, allowing the unwrapped value to be returned
369+
without needing to copy it.
370+
371+
> Many Swift programmers use types like `Optional` that work with noncopyable
372+
> values, without even being aware of features like ownership. The default
373+
> assumption of copyability helps keep the learning curve shallow, and ensures
374+
> that you can mark your types and protocols as supporting noncopyable types
375+
> without the concern that you might be making using them harder for users who
376+
> don't want to deal with more advanced language features.
377+
378+
## Conditional Copyability
379+
380+
Sometimes whether a generic type is copyable depends on one of its
381+
placeholders. We have already seen an example of this, with the `Optional`
382+
type. An `Optional` type that wraps a copyable type can be copied, whereas
383+
one that wraps a noncopyable type cannot.
384+
385+
To conditionalize copyability on a placeholder, you can write a conditional
386+
conformance to the `Copyable` protocol, with a where clause that depends on
387+
another type being copyable:
388+
389+
```swift
390+
extension Optional: Copyable where Wrapped: Copyable { }
391+
```
392+
393+
The `Copyable` conformance does not need to fulfill any protocol requirements –
394+
these are handled by the Swift compiler.
395+
396+
This conformance can only be added in the module in which the type is defined.
397+
398+
Types that have a deinitializer cannot be made conditionally copyable.

0 commit comments

Comments
 (0)