Cannot explicitly specialize a generic function

GenericsSwift

Generics Problem Overview


I have issue with following code:

func generic1<T>(name : String){
}

func generic2<T>(name : String){
     generic1<T>(name)
}

the generic1(name) result to compiler error "Cannot explicitly specialize a generic function"

Is any way to avoid this error? I can't change signature of generic1 function, therefore it should be (String) -> Void

Generics Solutions


Solution 1 - Generics

I also had this problem and I found a workaround for my case.

In this article the author has the same problem

https://www.iphonelife.com/blog/31369/swift-programming-101-generics-practical-guide

So the problem seems to be, that the compiler needs to infer the type of T somehow. But it isn't allowed to simply use generic< type >(params...).

Normally, the compiler can look for the type of T, by scanning the parameter types because this is where T is used in many cases.

In my case it was a little bit different, because the return type of my function was T. In your case it seems that you haven't used T at all in your function. I guess you just simplified the example code.

So I have the following function

func getProperty<T>( propertyID : String ) -> T

And in case of, for instance

getProperty<Int>("countProperty")

the compiler gives me the error:

> Cannot explicitly specialize a generic function

So, to give the compiler another source of information to infer the type of T from, you have to explicitly declare the type of the variable the return value is saved in.

var value : Int = getProperty("countProperty")

This way the compiler knows that T has to be an integer.

So I think overall it simply means that if you specify a generic function you have to at least use T in your parameter types or as a return type.

Solution 2 - Generics

Swift 5

Typically there are many ways to define generic functions. But they are based on condition that T must be used as a parameter, or as a return type.

extension UIViewController {
    class func doSomething<T: UIView>() -> T {
        return T()
    }
    
    class func doSomethingElse<T: UIView>(value: T) {
        // Note: value is a instance of T
    }
    
    class func doLastThing<T: UIView>(value: T.Type) {
        // Note: value is a MetaType of T
    }
}

After that, we must provide T when calling.

let result = UIViewController.doSomething() as UIImageView // Define `T` by casting, as UIImageView
let result: UILabel = UIViewController.doSomething() // Define `T` with property type, as UILabel
UIViewController.doSomethingElse(value: UIButton()) // Define `T` with parameter type, as UIButton
UIViewController.doLastThing(value: UITextView.self) // Define `T` with parameter type, as UITextView

Ref:

  1. http://austinzheng.com/2015/01/02/swift-generics-pt-1/
  2. https://dispatchswift.com/type-constraints-for-generics-in-swift-d6bf2f0dbbb2

Solution 3 - Generics

The solution is taking the class type as parameter (like in Java)

To let compiler know what type he is dealing with pass the class as argument

extension UIViewController {
    func navigate<ControllerType: UIViewController>(_ dump: ControllerType.Type, id: String, before: ((ControllerType) -> Void)?){
        let controller = self.storyboard?.instantiateViewController(withIdentifier: id) as! ControllerType
        before?(controller)
        self.navigationController?.pushViewController(controller, animated: true)
    }
}

Call as:

self.navigate(UserDetailsViewController.self, id: "UserDetailsViewController", before: {
        controller in
        controller.user = self.notification.sender
    })

Solution 4 - Generics

You don't need a generic here since you have static types (String as parameter), but if you want to have a generic function call another you could do the following.

Using Generic methods

func fetchObjectOrCreate<T: NSManagedObject>(type: T.Type) -> T {
    if let existing = fetchExisting(type) {
       return existing
    }
    else {
        return createNew(type)
    }
}

func fetchExisting<T: NSManagedObject>(type: T.Type) -> T {
    let entityName = NSStringFromClass(type)
     // Run query for entiry
} 

func createNew<T: NSManagedObject>(type: T.Type) -> T {
     let entityName = NSStringFromClass(type)
     // create entity with name
} 

Using a generic class (Less flexible as the generic can be defined for 1 type only per instance)

class Foo<T> {
   
   func doStuff(text: String) -> T {
      return doOtherStuff(text)
   }

   func doOtherStuff(text: String) -> T {

   }  

}

let foo = Foo<Int>()
foo.doStuff("text")

Solution 5 - Generics

I think that when you specify generic function you should specify some of the parameters of type T, like followed:

func generic1<T>(parameter: T) {
    println("OK")
}

func generic2<T>(parameter: T) {
    generic1(parameter)
}

and if you want to call handle() method, then you may do this by writing protocol, and specifying type constraint for T:

protocol Example {
    func handle() -> String
}

extension String: Example {
    func handle() -> String {
        return "OK"
    }
}

func generic1<T: Example>(parameter: T) {
    println(parameter.handle())
}

func generic2<T: Example>(parameter: T) {
    generic1(parameter)
}

so you may call this generic function with String:

generic2("Some")

and it will compile

Solution 6 - Generics

So far, my personal best practise used @orkhan-alikhanov 's answer. Today, when looking at SwiftUI and how .modifier() and the ViewModifier is implemented, I found another way (or is it more a workaround?)

Simply wrap the second function into a struct.

Example:

If this one gives you the "Cannot explicitly specialize a generic function"

func generic2<T>(name: String){
     generic1<T>(name)
}

This one might help. Wrap the declaration of generic1 into a struct:

struct Generic1Struct<T> {
    func generic1(name: String) {## do, whatever it needs with T ##}
}

and call it with:

func generic2<T>(name : String){
     Generic1Struct<T>().generic1(name: name)
}

Remarks:

  • I don't know if it helps in any possible case, when this error message occurs. I just know, that I was stuck many times when this came up. I do know, that this solution helped today, when the error-message came up.
  • The way Swift handles Generics is for me still confusing.
  • This example and the workaround with the struct is a good example. The workaround here has not a bit more information - but passes the compiler. Same information, but different results? Then something is wrong. If it is a compiler-bug it can get fixed.

Solution 7 - Generics

I had a similar problem with my generic class function class func retrieveByKey<T: GrandLite>(key: String) -> T?.

I could not call it let a = retrieveByKey<Categories>(key: "abc") where Categories is a subclass of GrandLite.

let a = Categories.retrieveByKey(key:"abc") returned GrandLite, not Categories. Generic functions do not infer type based on the class that calls them.

class func retrieveByKey<T: GrandLite>(aType: T, key: String>) -> T? gave me an error when I tried let a = Categories.retrieveByKey(aType: Categories, key: "abc") gave me an error that it could not convert Categories.Type to GrandLite, even though Categories is a subclass of GrandLite. HOWEVER...

class func retrieveByKey<T: GrandLite>(aType: [T], key: String) -> T? did work if I tried let a = Categories.retrieveByKey(aType: [Categories](), key: "abc") apparently an explicit assignment of a subclass does not work, but an implicit assigment using another generic type (array) does work in Swift 3.

Solution 8 - Generics

func generic1<T>(of type: T.Type, name: String) {
    switch type {
    case is String.Type:
        print("String generic")
    case is Int.Type:
        print("Int generic")
    default:
        print("T generic")
    }
    print(name)
}

func generic2<T>(of type: T.Type, name: String) {
     generic1(of: T.self, name: name)
}

generic2(of: String.self, name: "hello")
// String generic
// hello
generic2(of: Int.self, name: "world")
// Int generic
// world

Since generic<T>() is illegal in Swift,
This generic(of: T.self) usage is a workaround I found.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGreyisfView Question on Stackoverflow
Solution 1 - GenericsThottChiefView Answer on Stackoverflow
Solution 2 - Genericsnahung89View Answer on Stackoverflow
Solution 3 - GenericsOrkhan AlikhanovView Answer on Stackoverflow
Solution 4 - GenericsaryaxtView Answer on Stackoverflow
Solution 5 - GenericsValerii LiderView Answer on Stackoverflow
Solution 6 - GenericsjboiView Answer on Stackoverflow
Solution 7 - GenericsadamekView Answer on Stackoverflow
Solution 8 - Genericseastriver leeView Answer on Stackoverflow