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
- Dependent parameters
- Dependent properties
- Conditional dependencies
- Immortal lifetimes
- Depending on immutable global variables
- Depending on an escapable `BitwiseCopyable` value
- Depending on an escapable `BitwiseCopyable` value
We expect these implicit inferences to cover most cases, with the explicit form only occasionally being necessary in practice.
277
277
278
+
### Dependent parameters
279
+
280
+
Normally, lifetime dependence is required when a nonescapable function result depends on an argument to that function. In some rare cases, however, a nonescapable function parameter may depend on another argument to that function. Consider a function with an `inout` parameter. The function body may reassign that parameter to a value that depends on another parameter. This is similar in principle to a result dependence.
We've discussed how a nonescapable result must be destroyed before the source of its lifetime dependence. Similarly, a dependent argument must be destroyed before an argument that it depends on. The difference is that the dependent argument may already have a lifetime dependence when it enters the function. The new function argument dependence is additive, because the call does not guarantee reassignment. Instead, passing the 'inout' argument is like a conditional reassignment. After the function call, the dependent argument carries both lifetime dependencies.
299
+
300
+
```swift
301
+
let a1: Array<Int> =...
302
+
var span = a1.span()
303
+
let a2: Array<Int> =...
304
+
mayReassign(span: &span, to: a2)
305
+
// 'span' now depends on both 'a1' and 'a2'.
306
+
```
307
+
308
+
### Dependent properties
309
+
310
+
Structural composition is an important use case for nonescapable types. Getting or setting a nonescapable property requires lifetime dependence, just like a function result or an 'inout' parameter. There's no need for explicit annotation in these cases, because only one dependence is possible. A getter returns a value that depends on `self`. A setter replaces the current dependence from `self` with a dependence on `newValue`.
funcgetElement() -> dependsOn(self) Element { element }
334
+
}
335
+
336
+
extensionContainer<E> { // OK: conforms to Escapable.
337
+
// Escapable context...
338
+
}
339
+
```
340
+
341
+
Here, `Container` becomes nonescapable only when its element type is nonescapable. When `Container` is nonescapable, it inherits the lifetime of its single element value from the initializer and propagates that lifetime to all uses of its `element` property or the `getElement()` function.
342
+
343
+
In some contexts, however, `Container` and `Element` both conform to `Escapable`. In those contexts, any `dependsOn` in `Container`'s interface is ignored, whether explicitly annotated or implied. So, when `Container`'s element conforms to `Escapable`, the `-> dependsOn(element) Self` annotation in its initializer is ignored, and the `-> dependsOn(self) Element` in `getElement()` is ignored.
344
+
345
+
### Immortal lifetimes
346
+
347
+
In some cases, a nonescapable value must be constructed without any object that can stand in as the source of a dependence. Consider extending the standard library `Optional` or `Result` types to be conditionally escapable:
348
+
349
+
```swift
350
+
enumOptional<Wrapped: ~Escapable>: ~Escapable {
351
+
casenone, some(Wrapped)
352
+
}
353
+
354
+
extensionOptional: Escapable where Wrapped:Escapable {}
extensionResult: Escapable where Success:Escapable {}
361
+
```
362
+
363
+
When constructing an `Optional<NotEscapable>.none` or `Result<NotEscapable>.failure(error)` case, there's no lifetime to assign to the constructed value in isolation, and it wouldn't necessarily need one for safety purposes, because the given instance of the value doesn't store any state with a lifetime dependency. Instead, the initializer for cases like this can be annotated with `dependsOn(immortal)`:
364
+
365
+
```swift
366
+
extensionOptional {
367
+
init(nilLiteral: ()) dependsOn(immortal) {
368
+
self= .none
369
+
}
370
+
}
371
+
```
372
+
373
+
Once the escapable instance is constructed, it is limited in scope to the caller's function body since the caller only sees the static nonescapable type. If a dynamically escapable value needs to be returned further up the stack, that can be done by chaining multiple `dependsOn(immortal)` functions.
374
+
375
+
#### Depending on immutable global variables
376
+
377
+
Another place where immortal lifetimes might come up is with dependencies on global variables. When a value has a scoped dependency on a global let constant, that constant lives for the duration of the process and is effectively perpetually borrowed, so one could say that values dependent on such a constant have an effectively infinite lifetime as well. This will allow returning a value that depends on a global by declaring the function's return type with `dependsOn(immortal)`:
### Depending on an escapable `BitwiseCopyable` value
388
+
389
+
The source of a lifetime depenence may be an escapable `BitwiseCopyable` value. This is useful in the implementation of data types that internally use `UnsafePointer`:
390
+
391
+
```swift
392
+
structSpan<T>: ~Escapable {
393
+
...
394
+
// The caller must ensure that `unsafeBaseAddress` is valid over all uses of the result.
By convention, when the source of a dependence is escapable and `BitwiseCopyable`, it should have an "unsafe" label, such as `unsafeBaseAddress` above. This communicates to anyone who calls the function, that they are reponsibile for ensuring that the value that the result depends on is valid over all uses of the result. The compiler can't guarantee safety because `BitwiseCopyable` types do not have a formal point at which the value is destroyed. Specifically, for `UnsafePointer`, the compiler does not know which object owns the pointed-to storage.
These are useful for nonescapable data types that are internally represented using escapable types such as `UnsafePointer`. For example, some methods on `Span` will need to derive a new `Span` object that copies the lifetime dependence of `self`:
463
+
464
+
```swift
465
+
extensionSpan {
466
+
consumingfuncdropFirst() -> Span<T> {
467
+
let local =Span(base: self.base+1, count: self.count-1)
Since `self.base` is an escapable value, it does not propagate the lifetime dependence of its container. Without the call to `unsafeLifetime`, `local` would be limited to the local scope of the value retrieved from `self.base`, and could not be returned from the method. In this example, `unsafeLifetime` communicates that all of the dependent state from `self` has been *copied* into `local`, and, therefore, `local` can persist after `self` is destroyed.
0 commit comments