The Basics of Generics
suggest changeGenerics are placeholders for types, allowing you to write flexible code that can be applied across multiple types. The advantage of using generics over Any
is that they still allow the compiler to enforce strong type-safety.
A generic placeholder is defined within angle brackets <>
.
Generic Functions
For functions, this placeholder is placed after the function name:
<T>TTT
In this case, the generic placeholder is T
. When you come to call the function, Swift can infer the type of T
for you (as it simply acts as a placeholder for an actual type).
let randomOutput = pickRandom(5, 7) // returns an Int (that's either 5 or 7)
Here we’re passing two integers to the function. Therefore Swift is inferring T == Int
– thus the function signature is inferred to be (Int, Int) -> Int
.
Because of the strong type safety that generics offer – both the arguments and return of the function must be the same type. Therefore the following will not compile:
struct Foo {}
let foo = Foo()
let randomOutput = pickRandom(foo, 5) // error: cannot convert value of type 'Int' to expected argument type 'Foo'
Generic Types
In order to use generics with classes, structs or enums, you can define the generic placeholder after the type name.
<T>TT
This generic placeholder will require a type when you come to use the class Bar
. In this case, it can be inferred from the initialiser init(baz:T)
.
let bar = Bar(baz: "a string") // bar's type is Bar<String>
Here the generic placeholder T
is inferred to be of type String
, thus creating a Bar<String>
instance. You can also specify the type explicitly:
<String>
When used with a type, the given generic placeholder will keep its type for the entire lifetime of the given instance, and cannot be changed after initialisation. Therefore when you access the property baz
, it will always be of type String
for this given instance.
let str = bar.baz // of type String
Passing Around Generic Types
When you come to pass around generic types, in most cases you have to be explicit about the generic placeholder type you expect. For example, as a function input:
<Int>
This function will only accept a Bar<Int>
. Attempting to pass in a Bar
instance where the generic placeholder type is not Int
will result in a compiler error.
Generic Placeholder Naming
Generic placeholder names are not just limited to single letters. If a given placeholder represents a meaningful concept, you should give it a descriptive name. For example, Swift’s Array
has a generic placeholder called Element
, which defines the element type of a given Array
instance.
<Element>