How do you assign to a protocol requirement with an opaque instance?

This is related to "How do you forward opaque inputs to default implementation outputs without a derived protocol?" but this is a simpler question. (Use case for this, here.)

You have a protocol requirement that uses an associated type.

protocol Protocol {
  associatedtype Property
  var property: Property { get }
}

You want to use an opaque value to define Property.

var opaqueValue: some Any {
  "This is from a 3rd party API and must be opaque."
}

It cannot be done directly.

struct Conformer: Protocol { // Type 'Conformer' does not conform to protocol 'Protocol'
  init() { property = opaqueValue }
  var property: Property // Reference to invalid associated type 'Property' of type 'Conformer'
}

But it can be done via another protocol. Is that really the simplest solution?

struct Conformer {
  init() { property = Self._property() }
  var property: Property
}

private protocol _Conformer: Protocol where Property == OpaqueProperty {
  associatedtype OpaqueProperty
  static func _property() -> OpaqueProperty
}

extension Conformer: _Conformer {
  static func _property() -> some Any { opaqueValue }
}

Clarification: It must be possible to refer to the resulting type, such as for defining a variable of its type.

struct Requirement {
  let requirement: Conformer
}

Have you tried typealias?

Ahh… nevermind… you need to start with a some.

struct Conformer<T>: P {
  var property: T
  
  init(property: T) {
    self.property = property
  }
}

let _ = Conformer(property: opaqueValue)

Are you blocked from making conformer generic over Property?

Why not var property: some Any = opaqueValue?

4 Likes

Yes. I added a Clarification about this at the end.


  1. I didn't even know that would work, given that
var property = opaqueValue

…which I had tried, did not work. That feels pretty weird. Is it intended?


  1. Property initializers do not support arguments. Got some magic to fix that too?
typealias Input = Void // Input type doesn't matter.

func opaqueValue(_ input: Input) -> some Any {
  "This is from a 3rd party API and must be opaque."
}

struct Conformer {
  init(_ input: Input) { property = Self._property(input) }
  var property: Property
}

private protocol _Conformer: Protocol where Property == OpaqueProperty {
  associatedtype OpaqueProperty
  static func _property(_ input: Input) -> OpaqueProperty
}

extension Conformer: _Conformer {
  static func _property(_ input: Input) -> some Any { opaqueValue(input) }
}