gnuoyd
(David Young)
1
My attempts to make a generic "type registry" struct The error "type
'T' constrained to non-protocol, non-class type 'Base'" has so far
foiled my attempts to make a generic "type registry" struct and I do not
understand why.
In my project I use an ExampleTypeRegistry to map subtypes of class
Example to UUID and back. In a serialized data structure that
contains Examples, the UUIDs stand for the subtypes.
The code looks like this,
import Foundation
public class Example {
}
public struct ExampleTypeRegistry {
public typealias Base = Example
var uuidToType: [UUID : Base.Type] = [:]
var oidToUUID: [ObjectIdentifier : UUID] = [:]
public init() {
}
public mutating func insert<T : Base>(_ type: T.Type) -> UUID {
let oid = ObjectIdentifier(type)
if let found = oidToUUID[oid] {
return found
}
let uuid = UUID()
oidToUUID[oid] = uuid
uuidToType[uuid] = type
return uuid
}
public func contains<T : Base>(_ type: T.Type) -> Bool {
return oidToUUID[ObjectIdentifier(type)] != nil
}
public func contains(_ uuid: UUID) -> Bool {
return uuidToType[uuid] != nil
}
public func lookup<T : Base>(_ type: T.Type) -> UUID? {
return oidToUUID[ObjectIdentifier(type)]
}
public func lookup(_ uuid: UUID) -> Base.Type? {
return uuidToType[uuid]
}
public mutating func remove<T : Base>(_ type: T.Type) -> UUID? {
let oid = ObjectIdentifier(type)
guard let uuid = oidToUUID[oid] else {
return nil
}
defer {
oidToUUID[oid] = nil
uuidToType[uuid] = nil
}
return uuid
}
public mutating func remove(_ uuid: UUID) -> Base.Type? {
guard let type = uuidToType[uuid] else {
return nil
}
let oid = ObjectIdentifier(type)
defer {
oidToUUID[oid] = nil
uuidToType[uuid] = nil
}
return type
}
}
I would like to reuse the same type registry code with other base
classes. I wrote TypeRegistry to work with any base class:
public struct TypeRegistry<Base : AnyObject> {
var uuidToType: [UUID : Base.Type] = [:]
var oidToUUID: [ObjectIdentifier : UUID] = [:]
public init() {
}
public mutating func insert<T : Base>(_ type: T.Type) -> UUID {
let oid = ObjectIdentifier(type)
if let found = oidToUUID[oid] {
return found
}
let uuid = UUID()
oidToUUID[oid] = uuid
uuidToType[uuid] = type
return uuid
}
public func contains<T : Base>(_ type: T.Type) -> Bool {
return oidToUUID[ObjectIdentifier(type)] != nil
}
public func contains(_ uuid: UUID) -> Bool {
return uuidToType[uuid] != nil
}
public func lookup<T : Base>(_ type: T.Type) -> UUID? {
return oidToUUID[ObjectIdentifier(type)]
}
public func lookup(_ uuid: UUID) -> Base.Type? {
return uuidToType[uuid]
}
public mutating func remove<T : Base>(_ type: T.Type) -> UUID? {
let oid = ObjectIdentifier(type)
guard let uuid = oidToUUID[oid] else {
return nil
}
defer {
oidToUUID[oid] = nil
uuidToType[uuid] = nil
}
return uuid
}
public mutating func remove(_ uuid: UUID) -> Base.Type? {
guard let type = uuidToType[uuid] else {
return nil
}
let oid = ObjectIdentifier(type)
defer {
oidToUUID[oid] = nil
uuidToType[uuid] = nil
}
return type
}
}
The compiler prints an error for several functions in TypeRegistry,
error: type 'T' constrained to non-protocol, non-class type 'Base'
public mutating func insert<T : Base>(_ type: T.Type) -> UUID {
The error is a surprise to me. Doesn't the declaration struct TypeRegistry<Base : AnyObject> constrain Base to a class type?
Is there some other way to make TypeRegistry work with arbitrary base
classes?
David
1 Like
bbrk24
2
AnyObject includes final classes and actors, which can't be inherited from. There is, to my knowledge, no way to constrain a type to be only an inheritable class or protocol.
jrose
(Jordan Rose)
3
That isn’t really the reason. Even a final class X can be a bound in a generic, though it’s silly to do so, because X itself satisfies the constraint.
Today’s Swift just doesn’t support using generic parameters as bounds for other generic parameters. I don’t remember if there’s a specific problematic pattern that prevents this from being added to the language, or if it’s just added complexity that hasn’t been implemented. Certainly it seems reasonable for classes, but it’s a bit trickier if you want it to support protocols as well, because protocols aren’t types (any MyProtocol is the type).
1 Like