Hi
I had the need recently to mark a value-type as @noescape, but we only allow
that for closure-types. I would like to propose that we allow any parameter to
any function to be marked @noescape (by default, unlike closures, they would be
potentially-escaping).
The standard library has several locations where this could be useful, for
example Array’s withUnsafeBufferPointer() method:
let array = [1, 2, 3]
var ptr : UnsafeBufferPointer<Int>
array.withUnsafeBufferPointer { ptr = $0 }
ptr.count // has escaped the withUnsafeBufferPointer context
If we could mark the pointer as @noescape, the assignment to ptr would be a
compile-time error.
My own (non-stdlib) case was rather complex: we had a class with a big
data-table, and clients could asynchronously request data. They would get a
callback inside a synchronised window during which they could access the data,
but by that time they may have moved on and not care any more. So we decided
the callback should tell the clients which data was available, and rather than
pass the data itself, pass an accessor which they can use if they still care.
Over time, we added more operations that they could perform within their little
slice of exclusive access, and wrapped those accessors up in a struct (with a
private initialiser). Unfortunately, it’s now no-longer a closure type, and we
can’t annotate that the view object clients receive is only valid for the
duration of the callback and should not be captured.
Something like this:
class Database<Value> {
public func editRecord<T>(at index: RecordIndex, with block:
(Record<Value>)->T) -> T {
// Must be synchronised to the DB’s internal queue
return dbQueue.sync {
let view = Record(database: self, index: index)
return block(view)
}
}
fileprivate func _editValue(at index: RecordIndex, newValue: Value) {
// precondition: on queue ‘dbQueue'
}
fileprivate func _deleteRecord(at index: RecordIndex) {
// precondition: on queue ‘dbQueue'
}
}
struct Record<Value> {
private let database: DB
private let index: RecordIdx
public var value : Value {
get { return database._getValue(at: index) }
nonmutating set { database._deleteValue(at: index) }
}
public func delete() { database._deleteValue(at: index) }
}
// Usage example.
let recordIdx = database.queryRecord(...)
database.editRecord(at: recordIdx) {
$0.value = “Hello!”
// $0 (‘Record’) could escape this closure and be used to make
non-synchronised edits
}
Maybe the new memory-model will solve these problems; I can’t find a single,
clear proposal (that I can understand…)
Since it has standard-library consequences, it would be ABI-breaking.
- Karl
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution