max1701
max1701

Reputation: 70

How to use a protocol with associative type as return value in Swift?

I'm trying to use a protocol with associated type as return value of a class func.

Another post here on stackoverflow is about the same question and based on this post's answer I tried the following:

// addArg takes a single Int argument and returns something, 
// which is capable of adding a another Int argument
class OpWithNoArg {
    func addArg<OP: OpWithOneArg where OP.ArgType == Int>(arg: Int) -> OP {
        return OpWithOneArgImpl() // (1) error here !
    }
}

// a protocol with associated type, which allows adding another
// argument of a not yet specified type
protocol OpWithOneArg {
    typealias ArgType
    func addAnotherArg(arg: ArgType)
}

// an implementation of the protocol above, 
// which fixes the associated type to an Int
class OpWithOneArgImpl : OpWithOneArg {
    typealias ArgType = Int
    func addAnotherArg(arg: Int) {
        // ...
    }
}

The error in Xcode 7.0 Beta 4 on line marked with (1) is

cannot convert return expression of type 'OpWithOneArgImpl' to expected return type 'OP'

If I change the return value to an optional and nil is returned, the sample compiles successfully:

// return null  
class OpWithNoArg {
    func addArg<OP: OpWithOneArg where OP.ArgType == Int>(arg: Int) -> OP? {
        // return OpWithOneArgImpl()
        return nil
    }
}

Is this way of using a protocol with associative type as return value possible in swift, and if that's the case, how can the compiler error mentioned above be fixed?

Thanks in advance!

EDIT:

In Java, the code would be something like the following snippet. I try to find a way in Swift to achieve the same.

class C {
    <T> P<T> f(T arg) {
        return new PImpl();
    }
}

interface P<S> {
    void g(S arg);
}

class PImpl<S> implements P<S> {
    PImpl() {}
    public void g(S arg) {}
}

Upvotes: 0

Views: 1589

Answers (1)

Matteo Piombo
Matteo Piombo

Reputation: 6726

Not clear what you are trying to achieve, but imho the error stems from the fact that in your addArg function you define a generic type OP which should be typically used in the function's arguments and body.

Instead you return a non generic type trying to force it to be treated as a generic `OP.

A quick fix could be a force cast to OP of your return object:

return OpWithOneArgImpl() as !OP

but I wouldn't recommend it.

In your example your addArg will always return a OpWithOneArgImpl thus it could just be defined as a func addArg(arg: Int) -> OpWithOneArgImpl

Hope this helps.

EDIT:

Probably this is not what you're trying to achieve, but hope it can help with a clearer explanation of what I intended above.

protocol OpWithOneArg {
    typealias ArgType
    func addAnotherArg(arg: ArgType)
    init() // Must have a init with no argument
}


class OpWithOneArgImpl : OpWithOneArg {
    typealias ArgType = Int
    func addAnotherArg(arg: Int) {
        // ...
    }
    required init() {
        // init implementation
    }
}

class OpWithNoArg {
    func addArgWithOPType<OP: OpWithOneArg where OP.ArgType == Int>(op: OP.Type, arg: Int) -> OP {
        return OP() // Here we use the init
    }
}

let foo = OpWithNoArg()
foo.addArgWithOPType(OpWithOneArgImpl.self, arg: 3)

Edit:

Maybe you might investigate the use of generic types:

protocol OpWithOneArg {
    typealias ArgType
    func addAnotherArg(arg: ArgType)
    init() // Must have a init with no argument
}


class OpWithOneArgImpl<T> : OpWithOneArg { // Generic implementation confirming to protocol
    typealias ArgType = T
    func addAnotherArg(arg: T) {
        // ...
    }
    required init() {
        // init implementation
    }
}


class OpWithNoArg {
    func addArgWithOPType<OP: OpWithOneArg>(op: OP.Type, arg: OP.ArgType) -> OP {
        return OP() // Here we use the init
    }
}

let foo = OpWithNoArg()

// OpWithOneArgImpl<Int>
foo.addArgWithOPType(OpWithOneArgImpl.self, arg: 3) 

// OpWithOneArgImpl<String>
foo.addArgWithOPType(OpWithOneArgImpl.self, arg: "Hello")

Upvotes: 2

Related Questions