87

How to explicitly specialize a generic function in Swift

 3 years ago
source link: https://sarunw.com/posts/how-to-explicitly-specialize-generic-function-in-swift/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

How to explicitly specialize a generic function in Swift

28 Jul 2021 ⋅ 4 min read ⋅ Swift

Table of Contents

What is an explicit specialization

Explicit specialization is a process where you explicitly specify a type of a generic function at a call site.

Here is an example of generic function which will create an array of type T, which required T to be a subclass of UIView.

func viewFactory<T: UIView>(numberOfView: Int) -> [T] {
return Array(repeating: T(), count: numberOfView)
}

If we want to produce a UIButton out of the viewFactory with explicit specialization, you have to specify a type directly like this viewFactory<UIButton>.

let buttons = viewFactory<UIButton>(numberOfView: 3)
// Cannot explicitly specialize a generic function

Too bad explicit specialization isn't supported in Swift[1], and you will get the following compile error.

Cannot explicitly specialize a generic function

Before going to a solution, I think it is good to learn about how the type arguments of a generic function are determined.

donny-combine.png

Type inference

The type arguments of a generic function are always determined via type inference. For a function to be inferrable, it must reference the type arguments in its method signature. So the compiler can infer the type arguments from the context.

You can reference the type arguments either in a method parameter or as a return type.

In the following example, the type argument is determined from parameter (t: T) and return type -> T.

func foo<T>(t: T)
func foo<T>() -> T

Infer from method's parameters

Here is a more concrete example of a generic function that infers the type from passing argument.

func greeeting<T: CustomStringConvertible>(_ t: T) {
print(type(of: T.self))
print("Hello, \(t.description)!")
}

greeeting("Sarunw")
// String.Type
//Hello, Sarunw!

greeeting(true)
// Bool.Type
// Hello, true!

The greeting function accepts any type (T) parameter that conforms to the CustomStringConvertible protocol. We pass a string and boolean into the function, and that's where T infer its type, Bool and String.

Infer from the return type

Another way that a generic function can infer its type is via a return type. An example of this is the one we use at the beginning of the article.

func viewFactory<T: UIView>(numberOfView: Int) -> [T] {
return Array(repeating: T(), count: numberOfView)
}

The compiler can infer the type from the context that the result is used. Here are two examples.

Variable type

A variable type that stores the function result use to determine the generic type argument.

Here is an example where the type of buttons use to determine the generic type. T will be UIButton in this case.

let buttons: [UIButton] = viewFactory(numberOfView: 3)

Method parameter type

If you pass a generic function result as an argument of any method, the method's parameter type is used to determine the generic type.

Here is an example where the parameter type of the methodThatsNeedButtons method is used to determine the generic type. T also be UIButton here.

func methodThatsNeedButtons(_ buttons: [UIButton]) {}

// 1
methodThatsNeedButtons(viewFactory(numberOfView: 3))

<1> viewFactory result is use as a argument for methodThatsNeedButtons method, so viewFactory can infer its type from method parameter type of methodThatsNeedButtons which is [UIButton].

Since Swift needs to infer generic type arguments somehow, a method that doesn't refer to a generic type in method signature (no parameter or return type) won't work.

You will get a compile error, "Generic parameter 'T' is not used in function signature" for this kind of method.

func foo<T>()

Solution

A generic function that you might need to use explicit specialization is the one that infer its type from return type—the workaround for this by adding a parameter of type T as a way to inject type explicitly. In other words, we make it infer from method's parameters instead. Let's see how this change can help us directly specify the type of arguments.

I modified our viewFactory to support this explicit specialization.

// 1
func viewFactory<T: UIView>(_ t: T.Type, numberOfView: Int) -> [T] {
return Array(repeating: T(), count: numberOfView)
}

// 2
viewFactory(UIButton.self, numberOfView: 3)

<1> We add an argument of type T.Type. This makes our generic function can infer its type from the passing argument.
<2> Then, we can explicitly specify the type T by passing a type that you want this factory to create as an argument. In this case, a button.

We use T.Type instead of T because we need the type, not an object instance.

Conclusion

Swift doesn't support explicit specialization, but we can achieve a similar effect using type inference. Apple also uses this technique in their API. One example is decode(_:from:).

func decode<T>(_ type: T.Type, from data: Data) throws -> T where T : Decodable

let user = try? JSONDecoder().decode(User.self, from: dataJson)

You may also like

Print unescaped string output in Swift

How to print object (po) in a debugger (lldb) without escape special characters.

Swift
Reduce boilerplate code with an automatic synthesis of Equatable and Hashable conformance

Equatable and Hashable are two essential protocols in the Swift world. Let's learn an old Swift feature that you might forget.

Swift
What is @escaping in Swift closures

Learn the meaning of @escaping, so you know what to do when you see it or when you need to write one.

Swift

Read more article about Swift

or see all available topic

Enjoy the read?

If you enjoy this article, you can subscribe to the weekly newsletter.
Every Friday, you'll get a quick recap of all articles and tips posted on this site. No strings attached. Unsubscribe anytime.

Feel free to follow me on Twitter and ask your questions related to this post. Thanks for reading and see you next time.

If you enjoy my writing, please check out my Patreon https://www.patreon.com/sarunw and become my supporter. Sharing the article is also greatly appreciated.

Become a patron

Buy me a coffee

Tweet

Share

Previous
How to show and hide a sidebar in a SwiftUI macOS app

Once the sidebar is collapsed, there is no way to get it back. Learn how to mitigate the situation.

Next
Spell checking in Xcode

Did you know that you have an option to enable spell checking in Xcode?

← Home


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK