无法显式专门化泛型函数

我对以下代码有疑问:

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


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

编译器的 Generic1(名称)结果错误“无法显式专门化泛型函数”

有什么办法可以避免这个错误吗?我不能改变 generic1函数的签名,因此它应该是(String)-> Void

58221 次浏览

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

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.

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")

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.

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
})

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

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.
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.