Hello everyone,
I've been experimenting with some of the newer Swift proposals and wanted to check whether my mental model aligns with the reality.
I have a type called Context that is generic over some State. It is both ~Copyable and ~Escapable, with its lifetime tied to the pointer it wraps:
public struct Context<State>: ~Copyable, ~Escapable {
@usableFromInline
internal let statePointer: UnsafeMutablePointer<State>
@_lifetime(borrow statePointer)
internal init(statePointer: UnsafeMutablePointer<State>) {
self.statePointer = statePointer
}
@inlinable
@inline(__always)
public var state: State {
_read {
yield statePointer.pointee
}
nonmutating _modify {
yield &statePointer.pointee
}
}
}
Context is borred by a synchronous function:
func process(action: Action, context: borrowing Context<State>) -> FeatureWork
And the call site constructs it like this
private func work(from action: Action) -> Work<Action, Dependency> {
withUnsafeMutablePointer(to: &_state) { [feature] pointer in
feature.process(
action: action,
context: Context<F.State>(
statePointer: pointer
)
)
}
}
The intent is to give process efficient, direct access to _state through the pointer, while keeping mutation contained within Context. My assumption is that ~Escapable and ~Copyable together do most of the safety work here. The compiler should prevent the Context (and therefore the pointer) from outliving the withUnsafeMutablePointer scope, and the non-copyable constraint prevents aliased access.
Does this reasoning hold, or is there an oversight or misunderstanding of the concepts from my end?