Is there any way to return a *type* (not an *instance*) as an opaque type?

Suppose we have a module with some private types that are not meant to be (directly) made use of by a client of the module. We can define a public protocol and implement that protocol with a private type:

public protocol PublicProtocol {
  func print()
}

private struct PrivateStruct : PublicProtocol {
  func print() {
    Swift.print("this is a private instance")
  }
}

Our module could then define some public method (or variable) to request the type conforming to this protocol:

public var PublicInstance: some PublicProtocol {
  return PrivateStruct()
}

PublicInstance.print()
//  This is a private instance.

Here, we use Swift Opaque Types to “hide” the actual return type from the client of this module; all the client sees is the PublicProtocol interface. This is good, as long as we are interested in just an instance of the PrivateStruct. If what we are interested in is the type of the PrivateStruct, things don't look that easy:

public protocol PublicProtocol {
  static func print()
}

private struct PrivateStruct : PublicProtocol {
  static func print() {
    Swift.print("This is a private type.")
  }
}

public var PublicInstance: some PublicProtocol.Type {
  return PrivateStruct.self
}
//  error: an 'opaque' type must specify only 'Any', 'AnyObject', protocols, and/or a base class

We can't use an opaque return type here (AFAIK). Could we return the PublicProtocol type itself?

public var PublicType: PublicProtocol.Type {
  return PrivateStruct.self
}

PublicType.print()
//  This is a private type.

That works, unless our PublicProtocol defines an associatedtype:

public protocol PublicProtocol {
  associatedtype ChildType
  
  static func print()
}

private struct PrivateStruct : PublicProtocol {
  typealias ChildType = Void
  
  static func print() {
    Swift.print("This is a private type.")
  }
}

//  error: protocol 'PublicProtocol' can only be used as a generic constraint because it has Self or associated type requirements

In theory, we might (someday) have the ability to fix this with a public typealias:

public typealias PublicType : some PublicProtocol = PrivateStruct

error: 'some' types are only implemented for the declared type of properties and subscripts and the return type of functions

We don't have functionality like this today (AFAIK).

We could just declare PrivateStruct as a public type:

public protocol PublicProtocol {
  associatedtype ChildType
  
  static func print()
}

public struct PrivateStruct : PublicProtocol {
  public typealias ChildType = Void
  
  public static func print() {
    Swift.print("This is a private type.")
  }
}

public typealias PublicType = PrivateStruct

PublicType.print()
//  This is a private type.

This “works”, except our private type is now public to the client of this module.

Is there any way to return a type (not an instance) as an opaque type? Is there any other support for doing something like this somewhere else in Swift?

Thanks.

public protocol PublicProtocol {
  associatedtype PrivateConstraint
  
  static func print()
}

private struct PrivateStruct : PublicProtocol {
  typealias PrivateConstraint = Void
  
  static func print() {
    Swift.print("This is a private type.")
  }
}

public var PublicType: any PublicProtocol.Type {
  return PrivateStruct.self
}

PublicType.print()
//  This is a private type.

This code works using the any keyword introduced in Swift 5.6.

This works too:

public var PublicInstance: (some PublicProtocol).Type {
  return PrivateStruct.self
}
1 Like
(some PublicProtocol).Type

@Slava_Pestov Ohh… wow! That's what I was looking for this whole time!

public var PublicType: some PublicProtocol.Type {
  return PrivateStruct.self
}

This code never worked… and this whole time it's because I was missing the round braces! Ahh… good to know! Thanks!

2 Likes