Files
gh-kylehughes-the-unofficia…/skills/programming-swift/ReferenceManual/GenericParametersAndArguments.md
2025-11-30 08:36:15 +08:00

373 lines
13 KiB
Markdown

# Generic Parameters and Arguments
Generalize declarations to abstract away concrete types.
This chapter describes parameters and arguments for generic types, functions, and
initializers. When you declare a generic type, function, subscript, or initializer,
you specify the type parameters that the generic type, function, or initializer
can work with. These type parameters act as placeholders that
are replaced by actual concrete type arguments when an instance of a generic type is
created or a generic function or initializer is called.
For an overview of generics in Swift, see <doc:Generics>.
<!--
NOTE: Generic types are sometimes referred to as :newTerm:`parameterized types`
because they're declared with one or more type parameters.
-->
## Generic Parameter Clause
A *generic parameter clause* specifies the type parameters of a generic
type or function, along with any associated constraints and requirements on those parameters.
A generic parameter clause is enclosed in angle brackets (<>)
and has the following form:
```swift
<<#generic parameter list#>>
```
The *generic parameter list* is a comma-separated list of generic parameters,
each of which has the following form:
```swift
<#type parameter#>: <#constraint#>
```
A generic parameter consists of a *type parameter* followed by
an optional *constraint*. A *type parameter* is simply the name
of a placeholder type
(for example, `T`, `U`, `V`, `Key`, `Value`, and so on).
You have access to the type parameters (and any of their associated types) in the rest of the
type, function, or initializer declaration, including in the signature of the function
or initializer.
The *constraint* specifies that a type parameter inherits
from a specific class or conforms to a protocol or protocol composition.
For example, in the generic function below, the generic parameter `T: Comparable`
indicates that any type argument substituted
for the type parameter `T` must conform to the `Comparable` protocol.
```swift
func simpleMax<T: Comparable>(_ x: T, _ y: T) -> T {
if x < y {
return y
}
return x
}
```
<!--
- test: `generic-params`
```swifttest
-> func simpleMax<T: Comparable>(_ x: T, _ y: T) -> T {
if x < y {
return y
}
return x
}
```
-->
Because `Int` and `Double`, for example, both conform to the `Comparable` protocol,
this function accepts arguments of either type. In contrast with generic types, you don't
specify a generic argument clause when you use a generic function or initializer.
The type arguments are instead inferred from the type of the arguments passed
to the function or initializer.
```swift
simpleMax(17, 42) // T is inferred to be Int
simpleMax(3.14159, 2.71828) // T is inferred to be Double
```
<!--
- test: `generic-params`
```swifttest
>> let r0 =
-> simpleMax(17, 42) // T is inferred to be Int
>> assert(r0 == 42)
>> let r1 =
-> simpleMax(3.14159, 2.71828) // T is inferred to be Double
>> assert(r1 == 3.14159)
```
-->
<!--
Rewrite the above to avoid bare expressions.
Tracking bug is <rdar://problem/35301593>
-->
### Integer Generic Parameters
An *integer generic parameter*
acts as a placeholder for an integer value rather than a type.
It has the following form:
```swift
let <#type parameter#>: <#type>
```
The *type* must be the `Int` type from the Swift standard library,
or a type alias or generic type that resolves to `Int`.
The value you provide for an integer generic parameter
must be either an integer literal
or another integer generic parameter from the enclosing generic context.
For example:
```swift
struct SomeStruct<let x: Int> { }
let a: SomeStruct<2> // OK: integer literal
struct AnotherStruct<let x: Int, T, each U> {
let b: SomeStruct<x> // OK: another integer generic parameter
static let c = 42
let d: SomeStruct<c> // Error: Can't use a constant.
let e: SomeStruct<T> // Error: Can't use a generic type parameter.
let f: SomeStruct<U> // Error: Can't use a parameter pack.
}
```
The value of an integer generic parameter on a type
is accessible as a static constant member of that type,
with the same visibility as the type itself.
The value of an integer generic parameter on a function
is accessible as a constant from within the function.
When used in an expression,
these constants have type `Int`.
```swift
print(a.x) // Prints "4"
```
The value of an integer generic parameter can be inferred
from the types of the arguments you use
when initializing the type or calling the function.
```swift
struct AnotherStruct<let y: Int> {
var s: SomeStruct<y>
}
func someFunction<let z: Int>(s: SomeStruct<z>) {
print(z)
}
let s1 = SomeStruct<12>()
let s2 = AnotherStruct(s: s1) // AnotherStruct.y is inferred to be 12.
someFunction(s: s1) // Prints "12"
```
### Generic Where Clauses
You can specify additional requirements on type parameters and their associated types
by including a generic `where` clause right before the opening curly brace
of a type or function's body.
A generic `where` clause consists of the `where` keyword,
followed by a comma-separated list of one or more *requirements*.
```swift
where <#requirements#>
```
The *requirements* in a generic `where` clause specify that a type parameter inherits from
a class or conforms to a protocol or protocol composition.
Although the generic `where` clause provides syntactic
sugar for expressing simple constraints on type parameters
(for example, `<T: Comparable>` is equivalent to `<T> where T: Comparable` and so on),
you can use it to provide more complex constraints on type parameters
and their associated types. For example,
you can constrain the associated types of type parameters to conform to protocols.
For example, `<S: Sequence> where S.Iterator.Element: Equatable`
specifies that `S` conforms to the `Sequence` protocol
and that the associated type `S.Iterator.Element`
conforms to the `Equatable` protocol.
This constraint ensures that each element of the sequence is equatable.
Integer generic parameters can't have protocol or superclass requirements.
You can also specify the requirement that two types be identical,
using the `==` operator. For example,
`<S1: Sequence, S2: Sequence> where S1.Iterator.Element == S2.Iterator.Element`
expresses the constraints that `S1` and `S2` conform to the `Sequence` protocol
and that the elements of both sequences must be of the same type.
For integer generic parameters,
the `==` operator specifies a requirement for their values.
You can require two integer generic parameters to have the same value,
or you can require a specific integer value for the integer generic parameter.
Any type argument substituted for a type parameter must
meet all the constraints and requirements placed on the type parameter.
A generic `where` clause can appear
as part of a declaration that includes type parameters,
or as part of a declaration
that's nested inside of a declaration that includes type parameters.
The generic `where` clause for a nested declaration
can still refer to the type parameters of the enclosing declaration;
however,
the requirements from that `where` clause
apply only to the declaration where it's written.
If the enclosing declaration also has a `where` clause,
the requirements from both clauses are combined.
In the example below, `startsWithZero()` is available
only if `Element` conforms to both `SomeProtocol` and `Numeric`.
```swift
extension Collection where Element: SomeProtocol {
func startsWithZero() -> Bool where Element: Numeric {
return first == .zero
}
}
```
<!--
- test: `contextual-where-clauses-combine`
```swifttest
>> protocol SomeProtocol { }
>> extension Int: SomeProtocol { }
-> extension Collection where Element: SomeProtocol {
func startsWithZero() -> Bool where Element: Numeric {
return first == .zero
}
}
>> print( [1, 2, 3].startsWithZero() )
<< false
```
-->
<!--
- test: `contextual-where-clause-combine-err`
```swifttest
>> protocol SomeProtocol { }
>> extension Bool: SomeProtocol { }
>> extension Collection where Element: SomeProtocol {
>> func returnTrue() -> Bool where Element == Bool {
>> return true
>> }
>> func returnTrue() -> Bool where Element == Int {
>> return true
>> }
>> }
!$ error: no type for 'Self.Element' can satisfy both 'Self.Element == Int' and 'Self.Element : SomeProtocol'
!! func returnTrue() -> Bool where Element == Int {
!! ^
```
-->
You can overload a generic function or initializer by providing different
constraints, requirements, or both on the type parameters.
When you call an overloaded generic function or initializer,
the compiler uses these constraints to resolve which overloaded function
or initializer to invoke.
For more information about generic `where` clauses and to see an example
of one in a generic function declaration,
see <doc:Generics#Generic-Where-Clauses>.
> Grammar of a generic parameter clause:
>
> *generic-parameter-clause* → **`<`** *generic-parameter-list* **`>`** \
> *generic-parameter-list* → *generic-parameter* | *generic-parameter* **`,`** *generic-parameter-list* \
> *generic-parameter* → *type-name* \
> *generic-parameter* → *type-name* **`:`** *type-identifier* \
> *generic-parameter* → *type-name* **`:`** *protocol-composition-type* \
> *generic-parameter* → **`let`** *type-name* **`:`** *type* \
>
> *generic-where-clause* → **`where`** *requirement-list* \
> *requirement-list* → *requirement* | *requirement* **`,`** *requirement-list* \
> *requirement* → *conformance-requirement* | *same-type-requirement*
>
> *conformance-requirement* → *type-identifier* **`:`** *type-identifier* \
> *conformance-requirement* → *type-identifier* **`:`** *protocol-composition-type* \
> *same-type-requirement* → *type-identifier* **`==`** *type* \
> *same-type-requirement* → *type-identifier* **`==`** *signed-integer-literal*
<!--
NOTE: A conformance requirement can only have one type after the colon,
otherwise, you'd have a syntactic ambiguity
(a comma-separated list types inside of a comma-separated list of requirements).
-->
## Generic Argument Clause
A *generic argument clause* specifies the type arguments of a generic
type.
A generic argument clause is enclosed in angle brackets (<>)
and has the following form:
```swift
<<#generic argument list#>>
```
The *generic argument list* is a comma-separated list of type arguments.
A *type argument* is the name of an actual concrete type that replaces
a corresponding type parameter in the generic parameter clause of a generic type ---
or, for an integer generic parameter,
an integer value that replaces that integer generic parameter.
The result is a specialized version of that generic type.
The example below shows a simplified version of the Swift standard library's
generic dictionary type.
```swift
struct Dictionary<Key: Hashable, Value>: Collection, ExpressibleByDictionaryLiteral {
/* ... */
}
```
<!--
TODO: How are we supposed to wrap code lines like the above?
-->
The specialized version of the generic `Dictionary` type, `Dictionary<String, Int>`
is formed by replacing the generic parameters `Key: Hashable` and `Value`
with the concrete type arguments `String` and `Int`. Each type argument must satisfy
all the constraints of the generic parameter it replaces, including any additional
requirements specified in a generic `where` clause. In the example above,
the `Key` type parameter is constrained to conform to the `Hashable` protocol
and therefore `String` must also conform to the `Hashable` protocol.
You can also replace a type parameter with a type argument that's itself
a specialized version of a generic type (provided it satisfies the appropriate
constraints and requirements). For example, you can replace the type parameter
`Element` in `Array<Element>` with a specialized version of an array, `Array<Int>`,
to form an array whose elements are themselves arrays of integers.
```swift
let arrayOfArrays: Array<Array<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
```
<!--
- test: `array-of-arrays`
```swifttest
-> let arrayOfArrays: Array<Array<Int>> = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
```
-->
As mentioned in <doc:GenericParametersAndArguments#Generic-Parameter-Clause>,
you don't use a generic argument clause to specify the type arguments
of a generic function or initializer.
> Grammar of a generic argument clause:
>
> *generic-argument-clause* → **`<`** *generic-argument-list* **`>`** \
> *generic-argument-list* → *generic-argument* | *generic-argument* **`,`** *generic-argument-list* \
> *generic-argument* → *type* | *signed-integer-literal*
<!--
This source file is part of the Swift.org open source project
Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
Licensed under Apache License v2.0 with Runtime Library Exception
See https://swift.org/LICENSE.txt for license information
See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
-->