Home > Blockchain >  swift generic constraint type is also a return type
swift generic constraint type is also a return type

Time:10-30

protocol Builder {
    associatedtype Output where Output: MyProtocol
    func build() -> Output?
}

// concrete type
struct ABuilder: Builder {
       func builder() -> MyProtocol {
           if someCondition {
               return aSubClassOfMyProtocol
           } else {
               return anotherSubClassOfMyProtocol
           }
       }
}

MyProtocol is a protocol type. It is also the Output constraint. Because the concrete Builder ABuilder is going to return two different sub class types that conform MyProtocol. How can I make the generic constraint work?

I am trying to make the generic constraint be the same.

CodePudding user response:

You can make build() function generic or use typecasting. Anyway this two ways determine concrete type outside of method not inside. So first of all you need to remove associatedtype. If you specify associated type Output it means that the implementation of your Builder protocol should return some concrete type which conforms to MyProtocol. If you want your Builder to return any type which conforms to MyProtocol not the concrete one specified for this implementation of Builder then you shouldn't declare any associated types.

Let's see how type casting can work:

protocol Builder {
    func build() -> MyProtocol?
}

struct ABuilder: Builder {
    func build() -> MyProtocol? {
        // some code
    }
}

let builder = ABuilder()
guard let a = builder.build() as? SomeClass else { return }

Or we can use generic approach:

protocol Builder {
    func build<T: MyProtocol>() -> T?
}

struct ABuilder: Builder {
    func build<T: MyProtocol>() -> T? {
        // some code
    }
}

let builder = ABuilder()
let a: SomeClass = builder.build()

You can read more about that here, here and here. Moreover I strongly recommend watching this video which describes all the limitations of generics in swift.

CodePudding user response:

I think you are correct that having the typecasting outside the Builder is a better way. I also found another possibility. The difference is that we have the concrete class types from the build function.

    protocol MyProtocol { }

    protocol Builder {
        associatedtype Output
        func build() -> Output?
    }

    struct ASubClassOfMyProtocol: MyProtocol {}

    struct AnotherSubClassOfMyProtocol: MyProtocol {}

    struct ABuilder: Builder {
        func build() -> (any MyProtocol)? {
            if Int.random(in: 0...10) < 5 {
                return ASubClassOfMyProtocol()
            } else {
                return AnotherSubClassOfMyProtocol()
            }
        }
    }
    
  • Related