-
Notifications
You must be signed in to change notification settings - Fork 2.4k
[pitch] Span
-providing properties in the standard library
#2620
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
var bytes: RawSpan { get } | ||
} | ||
|
||
extension String.UTF8View { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q: What is the behavior (and what are the performance characteristics) of this operation when a String
has storage other than contiguous UTF-8? Would it be desirable to return an optional, or alternatively require the user to know or have called makeContiguousUTF8()
on the String
as a precondition?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Behind the scenes, we are implementing a new "lazy eager bridging" behaviour for bridged Array
and bridged String
. They will always succeed, but they will sometimes entail an allocation and copy (i.e. "usually O(1), sometimes O(n)".) An initial step is here. For native non-bridged instances, always O(1).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool! The implementation details would of course be out of scope for this text, but might be worth calling out here the performance implications for calling storage
and bytes
on these types, pros and cons for usability vs predictability, etc. (given that this is supposed to be a safe, performant API).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I appended a paragraph to the detailed design to spell this out.
84708d7
to
670ac2d
Compare
8a7d1ff
to
49668a2
Compare
- these properties are not expressible in the current type system. - if we don’t get the generic ones, we should remove the raw ones too.
} | ||
``` | ||
|
||
All of these unsafe conversions return a value whose lifetime is dependent on the _binding_ of the UnsafeBufferPointer. Note that this does not keep the underlying memory alive, as usual where the `UnsafePointer` family of types is involved. The programmer must ensure the following invariants for as long as the `Span` or `RawSpan` binding is valid: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This sentence is somewhat mysterious:
All of these unsafe conversions return a value whose lifetime is dependent on the binding of the UnsafeBufferPointer.
Here's an attempted concrete explanation...
Swift does not manage the lifetime of unsafe buffer values. Consequently, when an unsafe buffer type provides a storage
property, the resulting Span
value depends on the variable that the unsafe buffer is bound to. The compiler enforces that the span does not escape the lexical scope of the variable that it depends on. It is up to the programmer to ensure that the unsafe buffer is valid within that lexical scope, which is typically true.
For example, if the unsafe buffer is a function argument, then it's storage is valid within the function body and can even be returned if the function's result depends on the argument:
@lifetime(borrow ubp)
func returnUBPStorage(ubp: UnsafeRawBufferPointer) -> RawSpan {
return ubp.storage // OK: 'span' can be returned since the function's return value also has a dependence on 'ubp'.
}
It is an error to use the unsafe buffer's storage outside of it's variable's lexical scope, regardless of the buffer's origin:
@lifetime(borrow ubp)
func copyUBPStorage(ubp: UnsafeRawBufferPointer) -> RawSpan {
let localBuffer = ubp
return localBuffer.storage // ERROR: lifetime-dependent value escapes its scope
// it depends on the lifetime of variable 'localBuffer'
}
func escapeUBPStorage(array: [Int64]) {
var span = RawSpan()
array.withUnsafeBytes {
span = $0.storage // ERROR: lifetime-dependent value escapes its scope
// it depends on the lifetime of argument '$0'
}
read(span)
}
I'll manage this review |
Continuing the work of adding
Span
, this is a proposal to add the ability to useSpan
as computed properties on standard library types, includingArray
.