How to restrict a generic type parameter to only protocol types?

Is there a way to restrict a generic type parameter to only allow protocol types to be used with it, similar to how you can restrict it to only allow reference types with AnyObject?

Like is there a way to do:

func foo<T>(bar: T) where T: ExistentialContainer { ... }

...?

If not, does this seem like something that might be worth adding to the language? If not, why would it be bad?

If you're already restricting the container, why don't you directly use it?

func foo(bar: ExistentialContainer)

Well for one thing, ExistentialContainer is not a type. For another thing, at compile time, we don't know which existential container will be passed into the generic parameter.

I might not fully understand your question. Do you want bar to be any existential container, or an existential container of a certain protocol (or protocol combination)? If it's the latter, you can just use the protocol name. If it's the former, I don't think we can do that.

Somewhat related, I have on occasion wanted to require that an associated type be a protocol, which another associated type conforms to. Unfortunately I don’t know of a way to make it work today:

protocol P {
  associatedtype Q: protocol      // not valid syntax
  associatedtype R: Q
}
2 Likes

If it's the former, I don't think we can do that.

Yeah, it's the former. Was hoping to be able to make a function that restricts you to only passing in protocol-type values/objects, but basically, looking for something like what @Nevin mentioned.

As for why this is something we'd want to do, well, it's kind of a long story... but basically we're using type metadata on generics to look up values for dependency injection. The types should always be protocols since the DI system is based on the idea of dependency inversion wehre everything depending on protocols (abstractions) and the implementations being hidden from the consumer.

Conversely it would also be nice to be able to use ! in where statements on generic types, like:

/// Does foo with bar
/// - Parameter bar: a value of some value type that:
///                            - can't be an existential
///                            - can't be an object
func foo<T>(bar: T) where T != Protocol && !(T: AnyObject) 

This would enforce that you can only use a plain value type for some function where we want to make sure that no reference type semantics can creep into the code over time and we don't get the performance hit from existential containers.

Not sure how that would be useful. You would have no idea what Q's interface is, which means you have no idea what R's interface is nor how to exploit it.

Right now, what can go into the "ExistentialContainer" part is at least one protocol and/or at most one (non-final?) class. So, are you asking that you do not want a class there, just pure protocol(s)? Are you asking for whatever T ends up being to be a non-class type? If that last question is true, then you'd need the oft requested AnyValue poison protocol.

Yes. Just looking for a way to use generics with "protocol-type" objects/values (aka "existential containers"). Because currently, you can't.

Are you asking for whatever T ends up being to be a non-class type?

No, it has nothing to do with reference-semantics vs. value-semantics. Clearly, you can have a reference-type existential container, e.g. any kind of AnyObject.

And not really looking to derail this conversation towards a discussion of AnyValue.

Terms of Service

Privacy Policy

Cookie Policy