Non-copyable associatedtype for non-escapable variables in public APIs

I am looking the way to form an API that would prevent escaping the variable outside of the scope.

One of the ideas I had is to define associated type as non-copyable thus non-escapable borrowing type.

Unfortunately got an error: - error: cannot suppress 'Copyable' requirement of an associated type

// Public API
public protocol P1: ~Copyable {}

public protocol P2: ~Copyable {
    associatedtype T: P1, ~Copyable // - error: cannot suppress 'Copyable' requirement of an associated type

    func withT<T>(body: (borrowing Self.T) throws -> T) rethrows -> T
}


// Hidden implementation
struct S1: P1, ~Copyable {

}

struct S2: P2, ~Copyable {
    typealias T = S1

    var s1: S1 = .init()

    func withT<T>(body: (borrowing Self.T) throws -> T) rethrows -> T {
        try body(s1)
    }
}

If I understand correctly, P1 allows T to be non-copyable and I add additional requirement ~Copyable.

I wonder if someone could suggest if I am missing something or that might be a bug or feature?

the associated type issue is, IIUC, a yet-to-be-implemented feature – the pitch to add support for it is here: Pitch: Suppressed Default Conformances on Associated Types.

ideally i think you'd use an ~Escapable type since that's the tool being designed to support these use cases (~Copyable does enforce nonescaping use in many instances, but in general the concepts are distinct). but per my understanding, such types still require use of the -enable-experimental-feature Lifetimes flag so that lifetime dependencies can be recorded and enforced. without that feature, it seems you can't even initialize such types in many (all?) cases. if you have a tolerance for that, you could try turning it on and using a non-escapable wrapper to vend to your closure. i was playing with that here, but i'm not sure it does quite what you want.

if you can't get the static checking to work, you could potentially try runtime enforcement. you'd pass a reference type wrapper to your clients, and then when the closure has run, perform the isKnownUniquelyReferenced check on it to infer whether it escaped.

1 Like

Thank you for the example - I played a bit and seems that actually might work for my cases.

I guess ~Escapable attribute is something that I am looking for and seems works in 6.2 to some extent (maybe with less diagnostics and some things to improve like in godbolt example)...

Though, I am a bit confused that this is an attribute for an object rather than for argument in a function signature. I would expect that to be similar to sending for arguments or @escaping for closures, e.g. (nonescapable S) -> () and that it would work for classes as well.

But I guess I need to read the thread about ~Escapable to understand the reasoning for that...