Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift protocol generic as function return type

Tags:

generics

swift

I want to use generic protocol type as a function return type like this:

protocol P {
  associatedtype T
  func get() -> T?
  func set(v: T)
}

class C<T>: P {
  private var v: T?
  func get() -> T? {
    return v
  }
  func set(v: T) {
    self.v = v
  }
}

class Factory {
  func createC<T>() -> P<T> {
    return C<T>()
  }
}

But this code compile with errors complained:

  1. Cannot specialize non-generic type 'P'
  2. Generic parameter 'T' is not used in function signature

Is there any way to achieve similar function with Swift?

like image 964
ufosky Avatar asked Jun 01 '16 04:06

ufosky


People also ask

CAN protocol functions have return types?

Using a protocol type as the return type for a function gives you the flexibility to return any type that conforms to the protocol. However, the cost of that flexibility is that some operations aren't possible on the returned values.

What is Associatedtype Swift?

An associated type gives a placeholder name to a type that's used as part of the protocol. The actual type to use for that associated type isn't specified until the protocol is adopted. Associated types are specified with the associatedtype keyword.


2 Answers

The problem is you cannot use the syntax P<T>. P is a protocol, meaning it can't be treated as a generic type (Cannot specialize non-generic type 'P'), even though it may have a given associatedtype.

In fact, because it has an associatedtype, you now can't even use the protocol type itself directly – you can only use it as a generic constraint.

One solution to your problem is to simply change your function signature to createC<T>() -> C<T>, as that's exactly what it returns.

class Factory {
    func createC<T>() -> C<T> {
        return C<T>()
    }
}

I'm not entirely sure what you would gain from having the return type be a protocol here. Presumably your example is just a simplification of your actual code and you want to be able to return an arbitrary instance that conforms to P. In that case, you could use type erasure:

class AnyP<T> : P {

    private let _get : () -> T?
    private let _set : (T) -> ()

    init<U:P where U.T == T>(_ base:U) {
        _get = base.get
        _set = base.set
    }

    func get() -> T? {return _get()}
    func set(v: T) {_set(v)}
}

class Factory {
    func createC<T>() -> AnyP<T> {
        return AnyP(C<T>())
    }
}
like image 81
Hamish Avatar answered Sep 20 '22 17:09

Hamish


Swift 5.1 supports returning associated types using Opaque type. Using opaque type, your code builds successfully. Ref

protocol P {
    associatedtype T
    func get() -> T?
    func set(v: T)
}

class C<T>: P {
    private var v: T?

    func get() -> T? {
        return v
    }
    func set(v: T) {
        self.v = v
    }
}

class Factory {
    func createC<T>() -> some P {
        return C<T>()
}
like image 36
pranav Avatar answered Sep 20 '22 17:09

pranav