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
The examples all refer to a `span()` function. This is unimplementable for
`Array` because it might not have contiguous storage. An `Array` span requires a
`_read` accessor because we need to create a temporary copy of the Array when it
is discontiguous. But we can't talk about `_read` here because it hasn't been
proposed yet.
Copy file name to clipboardExpand all lines: proposals/NNNN-lifetime-dependency.md
+21-21Lines changed: 21 additions & 21 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -47,11 +47,11 @@ This is a key requirement for the `Span` type (previously called `BufferView`) b
47
47
An efficient way to provide one piece of code with temporary access to data stored in some other piece of code is with a pointer to the data in memory.
48
48
Swift's `Unsafe*Pointer` family of types can be used here, but as the name implies, using these types can be error-prone.
49
49
50
-
For example, suppose `Array` had a property `unsafeBufferPointer` that returned an `UnsafeBufferPointer` to the contents of the array.
50
+
For example, suppose `ContiguousArray` had a property `unsafeBufferPointer` that returned an `UnsafeBufferPointer` to the contents of the array.
51
51
Here's an attempt to use such a property:
52
52
53
53
```swift
54
-
let array =getArrayWithData()
54
+
let array =getContiguousArrayWithData()
55
55
let buff = array.unsafeBufferPointer
56
56
parse(buff) // <== 🛑 NOT SAFE!
57
57
```
@@ -111,11 +111,11 @@ In the most common cases, these constraints can be inferred automatically.
111
111
112
112
To make the semantics clearer, we’ll begin by describing how one can explicitly specify a lifetime constraint in cases where the default inference rules do not apply.
113
113
114
-
Let’s consider adding support for our hypothetical `Span` type to `Array`.
114
+
Let’s consider adding support for our hypothetical `Span` type to `ContiguousArray`.
115
115
Our proposal would allow you to declare an `array.span()` method as follows:
@@ -153,7 +153,7 @@ Similar to the previous example:
153
153
154
154
In both this and the previous case, the lifetime of the return value is "scoped" to the lifetime of the original value.
155
155
Because lifetime dependencies can only be attached to nonescapable values, types that contain pointers will generally need to be nonescapable in order to provide safe semantics.
156
-
As a result, **scoped lifetime dependencies** are the only possibility whenever an `Escapable` value (such as an Array or similar container) is providing a nonescapable value (such as the `Span` or `MutatingSpan` in these examples).
156
+
As a result, **scoped lifetime dependencies** are the only possibility whenever an `Escapable` value (such as a ContiguousArray or similar container) is providing a nonescapable value (such as the `Span` or `MutatingSpan` in these examples).
157
157
158
158
#### Copied Lifetime Dependency
159
159
@@ -176,7 +176,7 @@ If the original `Span` was borrowing some array, the new `Span` will continue to
176
176
177
177
This supports coding patterns such as this:
178
178
```swift
179
-
let a: Array<Int>
179
+
let a: ContiguousArray<Int>
180
180
let ref1 = a.span() // ref1 cannot outlive a
181
181
let ref2 = ref1.drop(4) // ref2 also cannot outlive a
182
182
```
@@ -317,9 +317,9 @@ extension Span {
317
317
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.
318
318
319
319
```swift
320
-
let a1: Array<Int> =...
320
+
let a1: ContiguousArray<Int> =...
321
321
var span = a1.span()
322
-
let a2: Array<Int> =...
322
+
let a2: ContiguousArray<Int> =...
323
323
mayReassign(span: &span, to: a2)
324
324
// 'span' now depends on both 'a1' and 'a2'.
325
325
```
@@ -680,10 +680,10 @@ If a function, method, or initializer has a nonescapable return value, does not
680
680
681
681
### Dependency semantics by example
682
682
683
-
This section illustrates the semantics of lifetime dependence one example at a time for each interesting variation. The following helper functions will be useful: `Array.span()` creates a scoped dependence to a nonescapable `Span` result, `copySpan()` creates a copied dependence to a `Span` result, and `parse` uses a `Span`.
683
+
This section illustrates the semantics of lifetime dependence one example at a time for each interesting variation. The following helper functions will be useful: `ContiguousArray.span()` creates a scoped dependence to a nonescapable `Span` result, `copySpan()` creates a copied dependence to a `Span` result, and `parse` uses a `Span`.
684
684
685
685
```swift
686
-
extensionArray {
686
+
extensionContiguousArray {
687
687
// The returned span depends on the scope of Self.
@@ -715,7 +715,7 @@ Let's contrast scoped dependence shown above with copied dependence on a variabl
715
715
An assignment that copies or moves a nonescapable value from one variable into another **copies** any lifetime dependence from the source value to the destination value. Thus, variable assignment has the same lifetime copy semantics as passing an argument using a `dependsOn()` annotation *without* a `scoped` keyword. So, the statement `let temp = span` has identical semantics to `let temp = copySpan(span)`.
716
716
717
717
```swift
718
-
let a: Array<Int> = arg
718
+
let a: ContiguousArray<Int> = arg
719
719
let final: Span<Int>
720
720
do {
721
721
let span = a.span()
@@ -746,7 +746,7 @@ extension Span {
746
746
A dependence may be copied from a mutable ('inout') variable. In that case, the dependence is inherited from whatever value the mutable variable holds when it is accessed.
747
747
748
748
```swift
749
-
let a: Array<Int> =...
749
+
let a: ContiguousArray<Int> =...
750
750
var prefix: Span<Int>
751
751
do {
752
752
var temp = a.span()
@@ -761,7 +761,7 @@ parse(prefix) // ✅ Safe: still within lifetime of 'a'
761
761
Now, let's return to scoped dependence, this time on a mutable variable. This is where exclusivity guarantees come into play. A scoped depenendence extends an access of the mutable variable across all uses of the dependent value. If the variable mutates again before the last use of the dependent, then it is an exclusivity violation.
762
762
763
763
```swift
764
-
let a: Array<Int> =...
764
+
let a: ContiguousArray<Int> =...
765
765
a[i] =...
766
766
let span = a1.span()
767
767
parse(span) // ✅ Safe: still within 'span's access on 'a'
@@ -777,7 +777,7 @@ We've described how a mutable variable can be the source of a lifetime dependenc
777
777
778
778
```swift
779
779
funcreassign(_span: inout Span<Int>) {
780
-
let a: Array<Int> =...
780
+
let a: ContiguousArray<Int> =...
781
781
span = a.span() // 🛑 Error: 'span' escapes the scope of 'a'
'inout' argument dependence behaves like a conditional reassignment. After the call, the variable passed to the 'inout' argument has both its original dependence along with a new dependence on the argument that is the source of the argument dependence.
798
798
799
799
```swift
800
-
let a1: Array<Int> = arg
800
+
let a1: ContiguousArray<Int> = arg
801
801
do {
802
-
let a2: Array<Int> = arg
802
+
let a2: ContiguousArray<Int> = arg
803
803
var span = a1.span()
804
804
testReassignArgDependence(&span, a2) // creates a conjoined dependence
805
805
parse(span) // ✅ OK: within the lifetime of 'a1' & 'a2'
@@ -850,21 +850,21 @@ We propose above putting the annotation on the return value, which we believe ma
850
850
It would also be possible to put an annotation on the parameters instead:
An earlier version of this proposal advocated using the existing `borrow`/`mutate`/`consume`/`copy` keywords to specify a particular lifetime dependency semantic:
This was changed after we realized that there was in practice almost always a single viable semantic for any given situation, so the additional refinement seemed unnecessary.
0 commit comments