简体   繁体   中英

Swift 5: how to specify a generic type conforming to protocol when declaring a variable

I am on Swift 5. I have a protocol:

protocol Pipe {
    associatedtype T
    func await() -> Void
    func yield( to: Any, with listener: Selector ) -> Void
}

And I would like to reference an instance of this protocol somewhere in code. That is, I want foo: , or a variable of generic type T implementing Pipe. Per this documentation: https://docs.swift.org/swift-book/ReferenceManual/GenericParametersAndArguments.html

I tried writing:

var imageSource: <Pipe T>

and any permutation of said symbols, ie imageSource: but the syntax is wrong across all cases.

In fact, T conform to two protocols, Renderable and Pipe, so I really want:

var imageSource: <Pipe, Renderable T>

Syntax wise this is gibberish, but semantically it's not an uncommon use case.

__________________ EDIT after two answers have been given __________

I tried simplifying the Pipe protocol for this post, but now I realize I simplified it too much. In my code base it's

  protocol Pipe {
        associatedtype T
        func await() -> Void
        func yield( to: Any, with listener: Selector ) -> Void
        func batch() -> [T]
    }

That's why there's a T there. But it's not crucial, I can drop the batch() -> [T] if I am able to write what I want above.

This is called a generalized existential, and is not available in Swift. A protocol with an associated type describes other types; it is not a type itself and cannot be the type of a variable, or put into a collection.

This specific protocol doesn't make a lot of sense, since you don't use T anywhere. But what you would need to do is pull this into the containing type:

struct Something<Source> where Source: Pipe & Renderable {
    var imageSource: Source
}

I suspect you really want to redesign this in a different way, however. This looks like a fairly common misuse of protocols. You probably want Pipe and Renderer types that are structs (or even just functions). Without knowing what the calling code looks like, I can't say precisely how you would design it.

If you remove T (which isn't being used here), then Max's answer will address this issue. Protocols without associated types have an implicit existential type, and so you can treat them somewhat as "normal" types (assigning them to variables or putting them in collections).

An associated type is used when you want your protocol to work with a variety of types, think a Container protocol that might have several methods all dealing with one contained type.

But your protocol is not that, it doesn't need to know any other types to specify the necessary behavior, so get rid of the associated type.

protocol Pipe {
    func await() -> Void
    func yield( to: Any, with listener: Selector ) -> Void
}

class Foo {
  var imageSource: Pipe & Renderable
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM