[Swift] About generics

What is generics?

Generics are by receiving ** type as a parameter. This is a function for writing general-purpose programs. ** **

In Swift, generics are provided as ** generic functions ** and ** generic types **.

General purpose program

In order to know the convenience of generics, here is an example of not using generics.

The following sample code I defined a function that confirms the equivalence of the two values ​​received as arguments and returns the result.

func isEqual(_ x: Int, _ y: Int) -> Bool {
    x == y

isEqual(1, 1)   // true

This is the function when defined without using generics.

You can do general-purpose processing to compare arbitrary integers, ** This is only for integers of type Int. ** **

If you want to compare String type and Float type, You need to overload the same function for each type.

func isEqual(_ x: Int, _ y: Int) -> Bool {
    x == y

func isEqual(_ x: String, _ y: String) -> Bool {
    x == y

func isEqual(_ x: Float, _ y: Float) -> Bool {
    x == y

isEqual(1, 1)   // true
isEqual("abc", "abc")   // true
isEqual(1.11, 1.12)   // false

But with generics, ** General-purpose processing can be implemented even among multiple types. ** **

It looks like the following sample code.

<T: Equatable> means It means ** all types that comply with the Equatable protocol **.

String type, Int type, and Float type are all compliant with the Equatable protocol, so You can use this function.

Since the arguments are specified with the same T type like (_ x: T, _ y: T), When passing arguments, you must pass the same type.

The fourth is when you pass a String type and an Int type.

func isEqual<T: Equatable>(_ x: T, _ y: T) -> Bool {
    x == y

isEqual(1, 1)   // true
isEqual("abc", "abc")   // true
isEqual(1.11, 1.12)   // false
isEqual("100", 100)   //Compile error

Error details: Cannot convert value of type'Int' to expected argument type'String' Unable to convert value of type'Int' to expected argument type'String'

Definition method

To define generic functions and generic types It can be defined by adding ** type argument ** to the normal definition.

Enclose the type arguments in <>, and if there are multiple types, separate them with , (comma).

//For normal functions
func function name(Argument name:Mold) ->Return type{
Processing executed when a function is called

//For generic functions
func function name<Type argument>(Argument name: Type argument) ->Return type{
Processing executed when a function is called

The type declared as a type argument is Inside a generic function or generic type ** it can be treated like a regular type. ** **

It can also be used as the return type of a generic function.

In the following sample code The first argument T contains the Int type, and the second argument U contains the String type. Also, the return type will be String type.

func sampleMethod<T, U>(_ x: T, _ y: U) -> U {
    let a: T = x   //Type annotation
    let b = y   //Type inference
    let c = 1 as? T   //Cast of type
    print("The value of a is\(a), The type is\(type(of: a))\The value of nb is\(b), The type is\(type(of: b))\The type of nc is\(type(of: c))")
    return y

sampleMethod(123, "abc")

Execution result
The value of a is 123 and the type is Int
The value of b is abc and the type is String
The type of c is Optional<Int>

Specialization method

Inside a generic function or generic type, you can abstractly represent the type as a type argument, If you want to actually call a generic function or instantiate a generic type, You must specify a specific value for the type argument.

In other words, what does that mean? When defining a function, type arguments can be used abstractly in various processes, Pass a specific value when calling a function or instantiating a type. about it.

** For what is generically defined using generics Establishing a type by giving a specific type argument is called specialization. **

There are two main methods of specialization.

In the following sample code ** One is to specify the type argument in <>, The other is type inference to infer type arguments. ** **

//Content is a type argument
struct Sample<T> {
    let a: T

//When specifying that the type argument is of type String
let stringSample = Sample<String>(a: "abc")   // Sample<String>

//When type inferring type arguments
let intSample = Sample(a: 123)   // Sample<Int>

Optional \ type and Array \ type Wrapped type and Element type, It is used by applying a specific type such as Int type or String type, This is also one of the specializations.

Formal and real arguments

For generics as well as functions, with the type arguments used when defining the generics If you want to explicitly distinguish the type arguments you specify when specializing generics,

** The former is called a formal argument and the latter is called a real argument. ** **

Both versatility and type safety

The good thing about generics is not just generalization ** It is generalized while maintaining type safety by static typing. ** **

What is static typing? It is a mechanism that the compiler checks the safety of the type before execution.

By being checked in advance Eliminates errors due to running type mismatch.

** Because type arguments are kept in generic functions and generic types The type given as a type argument has the same type safety as a normal type. ** **

In the following sample code It takes the type of the argument as the type argument T and returns the return value with the same type T.

In other words, if you pass an Int type as a type argument, the return type will be an Int type, If you pass a String type, the return type will be a String type.

It is guaranteed that the argument type and the return type, expressed as the same type T, are the same.

func sample<T>(_ x: T) -> T {
    return x

let a = sample(123)   //Int type
let b = sample("abc")   //String type

Comparison with Any type

Actually, it can be generalized not only for generics but also for Any type.

** Any type is a protocol that all types are implicitly compliant with. In other words, any type can be expressed. ** **

What is the difference between generics and Any type? When you say There is a big difference in type safety.

While generics are ** type generalized while maintaining type safety **, Any type is a ** type unsafe generalization **.

In the following sample code Both sampleGeneric (_ :) and sampleAny (_ :) are generic.

So both take Int and String arguments The received value is returned as it is with return.

However, while the return type of a function that uses generics changes depending on the type argument, ** The return type of a function that uses the Any type will always be the Any type. ** **

What's wrong with this is ** Any type cannot be treated as the original type without downcasting, so When actually dealing with Any type variables and constants, it is necessary to downcast. ** **

//Functions using generics
func sampleGeneric<T>(_ x: T) -> T {
    return x

let a = sampleGeneric(123)   //Int type
let b = sampleGeneric("abc")   //String type

//Function using Any
func sampleAny(_ x: Any) -> Any {
    return x

let c = sampleAny(123)   //Any type
let d = sampleAny("abc")   //Any type

//Downcast as Int type
if let int = c as? Int {
    //It can be treated as an Int type for the first time here
    print("c is \(int)")
} else {
    //It is necessary to consider the case where downcasting to Int type cannot be performed.
    print("c is not Int")

Execution result
c is 123

That's the basic explanation of generics!

Any type and generics, Personally, I think it's a generic choice.

Any is better! I would be grateful if you could let me know if you have any questions.

Thank you for watching until the end.

Recommended Posts

[Swift] About generics
About AsyncTask Generics
[Swift] About enumeration types
[Swift] About protocol extension
About [Cocoa] [Swift] AirPlay 2
[Swift] Summary about Bool type
[Swift] About asynchronous processing "Operation"
About =
[Swift] I thought about compare
About method.invoke
About Kotlin
About attr_accessor
[Swift] Hit the API using Decodable / Generics
About Hinemos
About inheritance
About params
About Docker
[Swift] A note about function and closure
About Rails 6
About form_for
About Spring ③
About enum
About polymorphism
About Optional
About hashes
About JitPack
About Dockerfile
About this ()
About devise
[Swift] Closure
About encapsulation
About Docker
About active_hash
About static
About exceptions
[Java] Generics
About scope
[Maven] About Maven
[Swift] [Beginner] I searched a lot about #selector
[Note] About the introduction to Swift practice Chapter 11
About keyboard events on Mac Catalyst [Xcode & Swift]