Reputation: 1393
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
Upvotes: 123
Views: 71208
Reputation: 1894
if you have a return value in your generic functions with a signature similar to
func anyServerLoadingEndEvent<Result>(
result buildResult: @autoclosure () -> Result)
-> ServerLoadingEndEvent<Result>
{
ServerLoadingEndEvent(
param1: ...,
result: buildResult()
)
}
func anyServerUploadingEndEvent<Result>(
result buildResult: @autoclosure () -> Result)
-> ServerUploadingEndEvent<Result>
{
ServerUploadingEndEvent(
param1: ...,
result: buildResult()
)
}
you can call it your code using as
as a generic function specializer (see // (*)
)
serverLoadingEventsNotifier.notifyServerLoadingCompleted(
anyServerLoadingEndEvent(
result: ServerLoadingCompletedResult(...)
) as ServerLoadingEndEvent<ServerLoadingCompletedResult> // (*)
)
serverUploadingEventsNotifier.notifyServerUploadingFailed(
anyServerUploadingEndEvent(
result: ServerUploadingFailedResult(...)
) as ServerUploadingEndEvent<ServerUploadingFailedResult> // (*)
)
Upvotes: 0
Reputation: 498
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("\(type) 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
generic2(of: Float.self, name: "bye")
// Float generic
// bye
Since calling as generic<T>()
is illegal in Swift,
This generic(of: T.self)
usage is a workaround I found.
Upvotes: 2
Reputation: 8105
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:
Upvotes: 88
Reputation: 2515
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.
Upvotes: 208
Reputation: 11912
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
.
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)
}
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.Upvotes: 3
Reputation: 10060
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
})
Upvotes: 23
Reputation: 2674
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.
Upvotes: 1
Reputation: 77646
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")
Upvotes: 5
Reputation: 1814
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
Upvotes: 4