Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:36:15 +08:00
commit 759b610929
47 changed files with 56475 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
{
"name": "programming-swift-skill",
"description": "The complete content of The Swift Programming Language book.",
"version": "0.0.0-2025.11.28",
"author": {
"name": "Kyle Hughes",
"email": "kyle@kylehugh.es"
},
"skills": [
"./skills/programming-swift"
]
}

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# programming-swift-skill
The complete content of The Swift Programming Language book.

216
plugin.lock.json Normal file
View File

@@ -0,0 +1,216 @@
{
"$schema": "internal://schemas/plugin.lock.v1.json",
"pluginId": "gh:kylehughes/the-unofficial-swift-programming-language-skill:programming-swift-skill",
"normalized": {
"repo": null,
"ref": "refs/tags/v20251128.0",
"commit": "0902e12881eda8adf77cedc0e2791bc5a6610439",
"treeHash": "d8192a0c14e13b4ceb758127debcb664ee44d7271f986eb16be85270966150ab",
"generatedAt": "2025-11-28T10:20:01.031503Z",
"toolVersion": "publish_plugins.py@0.2.0"
},
"origin": {
"remote": "git@github.com:zhongweili/42plugin-data.git",
"branch": "master",
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
},
"manifest": {
"name": "programming-swift-skill",
"description": "The complete content of The Swift Programming Language book."
},
"content": {
"files": [
{
"path": "README.md",
"sha256": "ead2aa2d311a0b384440cea0b0ec527b1e6b5ae78731063c894b38832e5cecc4"
},
{
"path": ".claude-plugin/plugin.json",
"sha256": "add22218dfcc90fa96e827f67f54348e225aceef9e906cbd779ac3590471e2b0"
},
{
"path": "skills/programming-swift/SKILL.md",
"sha256": "d9f3ed89cf6d2cb01163354abb62403daeac038c8b7b3dfdf34365d540bbe027"
},
{
"path": "skills/programming-swift/LICENSE.txt",
"sha256": "770af8291f708538d8ff885a0bbc4e045cd700531741c4f99528d435c14d7f55"
},
{
"path": "skills/programming-swift/GuidedTour/GuidedTour.md",
"sha256": "7c483e32906333eaa289948338207662529ffc3937016bab29d7bf90f8a06d75"
},
{
"path": "skills/programming-swift/GuidedTour/AboutSwift.md",
"sha256": "e87bf7bc9bebfbd2c73b0d8b3af32cd808111a00c2520c9ca1543a1a36be401d"
},
{
"path": "skills/programming-swift/GuidedTour/Compatibility.md",
"sha256": "54ad056bcf43c022ebcd2e3a31cfab851229c98a460eb52754478bf7ffdce08e"
},
{
"path": "skills/programming-swift/LanguageGuide/Macros.md",
"sha256": "6cebe6f09f9ff0add0d5c34b05bb0a0a85c7a8bf597153b8a94fd9b82c476f30"
},
{
"path": "skills/programming-swift/LanguageGuide/BasicOperators.md",
"sha256": "1a515132932d4ad8fffbf6ec3266e6c7a538a623bd515e4aae2ad033dd84a986"
},
{
"path": "skills/programming-swift/LanguageGuide/NestedTypes.md",
"sha256": "92112126d4cbced6c895d28a0c59724702d4eda3d295a4dcddf3d40369179a94"
},
{
"path": "skills/programming-swift/LanguageGuide/AdvancedOperators.md",
"sha256": "47b57b78a429c6955868f473a725cd7390a06c2261019f6c0acd68362f06c572"
},
{
"path": "skills/programming-swift/LanguageGuide/Extensions.md",
"sha256": "f85e4338b286493ed80f392357804f951a79166e79d7c2f18e5fbc77bd2e5eb9"
},
{
"path": "skills/programming-swift/LanguageGuide/Initialization.md",
"sha256": "c74b674e398e60a2fe624c64ff44622362d5653131a6f76485df1a77487a8fba"
},
{
"path": "skills/programming-swift/LanguageGuide/Deinitialization.md",
"sha256": "f7f6b0df9518653fd3443988cf98cc2d7e06809aa6cfde52d66c98f236a366dc"
},
{
"path": "skills/programming-swift/LanguageGuide/Generics.md",
"sha256": "6b6c2cd0e3301cab793746f959c4e4f48f91b6f1e8127d142468547839aa6fa4"
},
{
"path": "skills/programming-swift/LanguageGuide/ControlFlow.md",
"sha256": "d64bf2bfff753c6000fb77639e2221db75bc5740b41c461238e8d6fffb396465"
},
{
"path": "skills/programming-swift/LanguageGuide/StringsAndCharacters.md",
"sha256": "2bd491271377eedabedfddebb2cece8f292009759ba9836bc43d43ee44634385"
},
{
"path": "skills/programming-swift/LanguageGuide/Inheritance.md",
"sha256": "5664bb867fea8774adbf27a9488dec4274201b94fd3ec9a3cae7a21d41711f12"
},
{
"path": "skills/programming-swift/LanguageGuide/MemorySafety.md",
"sha256": "46d5e2e6f1ed118e8b0ff0301131d5799d6262dc08867d3ca72e6d790c5905df"
},
{
"path": "skills/programming-swift/LanguageGuide/TypeCasting.md",
"sha256": "2271c6b1584720d2d44161201c8bee190ba7224b709d3059e50fb00c4a312e55"
},
{
"path": "skills/programming-swift/LanguageGuide/ErrorHandling.md",
"sha256": "b4f13efff36d450a578a3702922100327fb18ca119c8d48c5b87992d13dd6d1a"
},
{
"path": "skills/programming-swift/LanguageGuide/Subscripts.md",
"sha256": "54844cf530c3325708028e1cf88d94f885c3658ec6316f869421d5cbba9b939a"
},
{
"path": "skills/programming-swift/LanguageGuide/Properties.md",
"sha256": "875c5268895f3abe65b67d73ac9950b603ca68edb25e6e890ad613df08f6c7e0"
},
{
"path": "skills/programming-swift/LanguageGuide/Methods.md",
"sha256": "59fc2060e01f69999848b5df6e99a79c334de7d3f4607210769ff55dfd5db66f"
},
{
"path": "skills/programming-swift/LanguageGuide/OptionalChaining.md",
"sha256": "d67c0a49b54100c2bd2fd9356ed9f8d6af6ff5997da6e7cd7df7916a40006711"
},
{
"path": "skills/programming-swift/LanguageGuide/TheBasics.md",
"sha256": "97ae97fdd3f4dd627fa716fb715d04c87bb814099f97052fd4972e0e52e3e732"
},
{
"path": "skills/programming-swift/LanguageGuide/Functions.md",
"sha256": "af10de74bf9fc39cba39ac96a90b9be294e465f7158c5ece842131b6c6928086"
},
{
"path": "skills/programming-swift/LanguageGuide/CollectionTypes.md",
"sha256": "ea2c08636e0e8f3c6429acb64d0d21a69351b7c022d5c2465e93a99dcd63b1a5"
},
{
"path": "skills/programming-swift/LanguageGuide/AutomaticReferenceCounting.md",
"sha256": "71e9dc5980cd3d80daa4f33de50ba000711835b12d817326613f322ee3bd34d3"
},
{
"path": "skills/programming-swift/LanguageGuide/AccessControl.md",
"sha256": "af4c7975b7fa0850c6a0603f7a65e97b0ed883d53e5451363678301ec9be47bd"
},
{
"path": "skills/programming-swift/LanguageGuide/OpaqueTypes.md",
"sha256": "c139890c22385d59d8ba1e663ccd707c8e285e9cbe4d2bdfe25fcd59420a1f3b"
},
{
"path": "skills/programming-swift/LanguageGuide/Closures.md",
"sha256": "301b636069570c5b8ec56b0a80a4d3ee3596f049f668b646512f8c41c34a63cf"
},
{
"path": "skills/programming-swift/LanguageGuide/Protocols.md",
"sha256": "06e6b9cce869cd9986e0780741eb01c8143dc79763c31190f7dcf130027c694a"
},
{
"path": "skills/programming-swift/LanguageGuide/Concurrency.md",
"sha256": "d1c645e1efe899eb90cb7de6590c7c97c539c5b8de8984429945294815912ee4"
},
{
"path": "skills/programming-swift/LanguageGuide/ClassesAndStructures.md",
"sha256": "3dccadb0090fbe820570193c366a7d1b9ecff725af267b1fa61b0323c0b60de5"
},
{
"path": "skills/programming-swift/LanguageGuide/Enumerations.md",
"sha256": "02613db1e56d42fe4cf0dd36343f7b9b93e67aa5a9247c10ae30ec87ae8cda05"
},
{
"path": "skills/programming-swift/ReferenceManual/SummaryOfTheGrammar.md",
"sha256": "f46c73fef14baa0b22d6f1f6864a2a4b7a454f1e9a252bafe6505d832510f551"
},
{
"path": "skills/programming-swift/ReferenceManual/Declarations.md",
"sha256": "622f2e28c20e128d0a27d9278fc98962645a61cf98a2e7a6e52337d1ad88eac7"
},
{
"path": "skills/programming-swift/ReferenceManual/Statements.md",
"sha256": "1ae516a56be36fc16c661dd7332878b45962db05d58e17aab417750375cdad64"
},
{
"path": "skills/programming-swift/ReferenceManual/GenericParametersAndArguments.md",
"sha256": "097d6884a229a0fcaafbfc8f25f8eb765291df84217e59f4cf3790dff055d584"
},
{
"path": "skills/programming-swift/ReferenceManual/Patterns.md",
"sha256": "89002b2c5e02f6aa8e177f48e41aa1f6b7205648c35053b15cd3ce4a65328067"
},
{
"path": "skills/programming-swift/ReferenceManual/Attributes.md",
"sha256": "edcb3e9ba335a6050c631b2ffe9757625c986a126c9ae76cdbb65052ffcb0655"
},
{
"path": "skills/programming-swift/ReferenceManual/AboutTheLanguageReference.md",
"sha256": "a8f697ca399b79642cb724de1512fe439ff3878054515abe36e5f77a8d0f7a8c"
},
{
"path": "skills/programming-swift/ReferenceManual/Types.md",
"sha256": "e7acbe9508298ba201f3f9dfb20d8ab4e1a4f3e8f427c954f075510331bdcefe"
},
{
"path": "skills/programming-swift/ReferenceManual/LexicalStructure.md",
"sha256": "6f136199e0e632fb698960a05ac2f6ba93a72744e70af7097f22d713101d04eb"
},
{
"path": "skills/programming-swift/ReferenceManual/Expressions.md",
"sha256": "24ed3b8e21b57525396c5fb6c0d96ddfcde087a0f191939e46e7362867ff0ec0"
}
],
"dirSha256": "d8192a0c14e13b4ceb758127debcb664ee44d7271f986eb16be85270966150ab"
},
"security": {
"scannedAt": null,
"scannerVersion": null,
"flags": []
}
}

View File

@@ -0,0 +1,56 @@
# About Swift
Understand the high-level goals of the language.
Swift is a fantastic way to write software
for phones, tablets, desktops, servers,
or anything else that runs code.
It's a safe and fast programming language
that combines the best in modern language thinking
with wisdom from a diverse open source community.
Swift is friendly to new programmers,
without sacrificing the power and flexibility
that experienced programmers need.
It's an industrial-quality programming language
that's as expressive and enjoyable as a scripting language.
The compiler is optimized for performance
and the language is optimized for development,
without compromising on either.
Swift defines away large classes of common programming errors
by adopting modern programming patterns:
- Variables are always initialized before use.
- Array indices are checked for out-of-bounds errors.
- Integers are checked for overflow.
- Optionals ensure that `nil` values are handled explicitly.
- Memory is managed automatically.
- Error handling allows controlled recovery from unexpected failures.
Swift code is compiled and optimized to get the most out of modern hardware.
The syntax and standard library have been designed
based on the guiding principle that
the obvious way to write your code should also perform the best.
Its combination of safety and speed make Swift an excellent choice for
everything from "Hello, world!" to an entire operating system.
Swift combines a modern, lightweight syntax
that's familiar for developers coming from other popular languages
with powerful features like type inference and pattern matching,
allowing complex ideas to be expressed in a clear and concise manner.
As a result, code is easier to read, write, and maintain.
Swift continues to evolve with thoughtful new features and powerful capabilities.
The goals for Swift are ambitious.
We cant wait to see what you create with it.
<!--
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
-->

View File

@@ -0,0 +1,54 @@
# Version Compatibility
Learn what functionality is available in older language modes.
This book describes Swift 6.2.1,
the default version of Swift that's included in Xcode 26.1.
You can use the Swift 6.2.1 compiler to build code
that's written in Swift 6.2.1, Swift 5, Swift 4.2, or Swift 4.
When you use the Swift 6.2.1 compiler
to build code that uses the Swift 5 language mode,
you can use the new features from Swift 6.2.1 ---
they're enabled either by default or by an upcoming feature flag.
However, to enable strict concurrency checking,
you need to upgrade to the Swift 6.2.1 language mode.
In addition,
when you use Xcode 15.3 to build Swift 4 and Swift 4.2 code,
most Swift 5 functionality is still available.
That said,
the following changes are available only to code
that uses the Swift 5 language mode:
- Functions that return an opaque type require the Swift 5.1 runtime.
- The `try?` expression doesn't introduce an extra level of optionality
to expressions that already return optionals.
- Large integer literal initialization expressions are inferred
to be of the correct integer type.
For example, `UInt64(0xffff_ffff_ffff_ffff)` evaluates to the correct value
rather than overflowing.
Concurrency requires the Swift 5 language mode
and a version of the Swift standard library
that provides the corresponding concurrency types.
On Apple platforms, set a deployment target
of at least iOS 13, macOS 10.15, tvOS 13, watchOS 6, or visionOS 1.
A target written in Swift 6.2.1 can depend on
a target that's written in Swift 5, Swift 4.2 or Swift 4,
and vice versa.
This means, if you have a large project
that's divided into multiple frameworks,
you can migrate your code to a newer language version
one framework at a time.
<!--
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
-->

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,211 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
## Runtime Library Exception to the Apache 2.0 License: ##
As an exception, if you use this Software to compile your source code and
portions of this Software are embedded into the binary product as a result,
you may redistribute such product without providing attribution as would
otherwise be required by Sections 4(a), 4(b) and 4(d) of the License.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,721 @@
# Structures and Classes
Model custom types that encapsulate data.
*Structures* and *classes* are general-purpose,
flexible constructs that become the building blocks of your program's code.
You define properties and methods to add functionality to your structures and classes
using the same syntax you use to define constants, variables, and functions.
Unlike other programming languages,
Swift doesn't require you to create separate interface and implementation files
for custom structures and classes.
In Swift, you define a structure or class in a single file,
and the external interface to that class or structure is
automatically made available for other code to use.
> Note: An instance of a class is traditionally known as an *object*.
> However, Swift structures and classes
> are much closer in functionality than in other languages,
> and much of this chapter describes functionality that applies to
> instances of *either* a class or a structure type.
> Because of this, the more general term *instance* is used.
## Comparing Structures and Classes
Structures and classes in Swift have many things in common.
Both can:
- Define properties to store values
- Define methods to provide functionality
- Define subscripts to provide access to their values using subscript syntax
- Define initializers to set up their initial state
- Be extended to expand their functionality beyond a default implementation
- Conform to protocols to provide standard functionality of a certain kind
For more information, see
<doc:Properties>, <doc:Methods>, <doc:Subscripts>, <doc:Initialization>,
<doc:Extensions>, and <doc:Protocols>.
Classes have additional capabilities that structures don't have:
- Inheritance enables one class to inherit the characteristics of another.
- Type casting enables you to check and interpret the type of a class instance at runtime.
- Deinitializers enable an instance of a class to free up any resources it has assigned.
- Reference counting allows more than one reference to a class instance.
For more information, see
<doc:Inheritance>, <doc:TypeCasting>, <doc:Deinitialization>,
and <doc:AutomaticReferenceCounting>.
The additional capabilities that classes support
come at the cost of increased complexity.
As a general guideline,
prefer structures because they're easier to reason about,
and use classes when they're appropriate or necessary.
In practice, this means most of the custom types you define
will be structures and enumerations.
For a more detailed comparison,
see [Choosing Between Structures and Classes](https://developer.apple.com/documentation/swift/choosing_between_structures_and_classes).
> Note: Classes and actors share many of the same characteristics and behaviors.
> For information about actors, see <doc:Concurrency>.
### Definition Syntax
Structures and classes have a similar definition syntax.
You introduce structures with the `struct` keyword
and classes with the `class` keyword.
Both place their entire definition within a pair of braces:
```swift
struct SomeStructure {
// structure definition goes here
}
class SomeClass {
// class definition goes here
}
```
<!--
- test: `ClassesAndStructures`
```swifttest
-> struct SomeStructure {
// structure definition goes here
}
-> class SomeClass {
// class definition goes here
}
```
-->
> Note: Whenever you define a new structure or class,
> you define a new Swift type.
> Give types `UpperCamelCase` names
> (such as `SomeStructure` and `SomeClass` here)
> to match the capitalization of standard Swift types
> (such as `String`, `Int`, and `Bool`).
> Give properties and methods `lowerCamelCase` names
> (such as `frameRate` and `incrementCount`)
> to differentiate them from type names.
Here's an example of a structure definition and a class definition:
```swift
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}
```
<!--
- test: `ClassesAndStructures`
```swifttest
-> struct Resolution {
var width = 0
var height = 0
}
-> class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}
```
-->
The example above defines a new structure called `Resolution`,
to describe a pixel-based display resolution.
This structure has two stored properties called `width` and `height`.
Stored properties are constants or variables that are bundled up and stored
as part of the structure or class.
These two properties are inferred to be of type `Int`
by setting them to an initial integer value of `0`.
The example above also defines a new class called `VideoMode`,
to describe a specific video mode for video display.
This class has four variable stored properties.
The first, `resolution`, is initialized with a new `Resolution` structure instance,
which infers a property type of `Resolution`.
For the other three properties,
new `VideoMode` instances will be initialized with
an `interlaced` setting of `false` (meaning “noninterlaced video”),
a playback frame rate of `0.0`,
and an optional `String` value called `name`.
The `name` property is automatically given a default value of `nil`,
or “no `name` value”, because it's of an optional type.
### Structure and Class Instances
The `Resolution` structure definition and the `VideoMode` class definition
only describe what a `Resolution` or `VideoMode` will look like.
They themselves don't describe a specific resolution or video mode.
To do that, you need to create an instance of the structure or class.
The syntax for creating instances is very similar for both structures and classes:
```swift
let someResolution = Resolution()
let someVideoMode = VideoMode()
```
<!--
- test: `ClassesAndStructures`
```swifttest
-> let someResolution = Resolution()
-> let someVideoMode = VideoMode()
```
-->
Structures and classes both use initializer syntax for new instances.
The simplest form of initializer syntax uses the type name of the class or structure
followed by empty parentheses, such as `Resolution()` or `VideoMode()`.
This creates a new instance of the class or structure,
with any properties initialized to their default values.
Class and structure initialization is described in more detail
in <doc:Initialization>.
<!--
TODO: note that you can only use the default constructor if you provide default values
for all properties on a structure or class.
-->
### Accessing Properties
You can access the properties of an instance using *dot syntax*.
In dot syntax, you write the property name immediately after the instance name,
separated by a period (`.`), without any spaces:
```swift
print("The width of someResolution is \(someResolution.width)")
// Prints "The width of someResolution is 0".
```
<!--
- test: `ClassesAndStructures`
```swifttest
-> print("The width of someResolution is \(someResolution.width)")
<- The width of someResolution is 0
```
-->
In this example,
`someResolution.width` refers to the `width` property of `someResolution`,
and returns its default initial value of `0`.
You can drill down into subproperties,
such as the `width` property in the `resolution` property of a `VideoMode`:
```swift
print("The width of someVideoMode is \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is 0".
```
<!--
- test: `ClassesAndStructures`
```swifttest
-> print("The width of someVideoMode is \(someVideoMode.resolution.width)")
<- The width of someVideoMode is 0
```
-->
You can also use dot syntax to assign a new value to a variable property:
```swift
someVideoMode.resolution.width = 1280
print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
// Prints "The width of someVideoMode is now 1280".
```
<!--
- test: `ClassesAndStructures`
```swifttest
-> someVideoMode.resolution.width = 1280
-> print("The width of someVideoMode is now \(someVideoMode.resolution.width)")
<- The width of someVideoMode is now 1280
```
-->
### Memberwise Initializers for Structure Types
All structures have an automatically generated *memberwise initializer*,
which you can use to initialize the member properties of new structure instances.
Initial values for the properties of the new instance
can be passed to the memberwise initializer by name:
```swift
let vga = Resolution(width: 640, height: 480)
```
<!--
- test: `ClassesAndStructures`
```swifttest
-> let vga = Resolution(width: 640, height: 480)
```
-->
Unlike structures, class instances don't receive a default memberwise initializer.
Initializers are described in more detail in <doc:Initialization>.
<!--
- test: `classesDontHaveADefaultMemberwiseInitializer`
```swifttest
-> class C { var x = 0, y = 0 }
-> let c = C(x: 1, y: 1)
!$ error: argument passed to call that takes no arguments
!! let c = C(x: 1, y: 1)
!! ^~~~~~~~~~~~
!!-
```
-->
## Structures and Enumerations Are Value Types
A *value type* is a type whose value is *copied*
when it's assigned to a variable or constant,
or when it's passed to a function.
<!--
Alternate definition:
A type has value semantics when
mutation of one variable of that type
can never be observed through a different variable of the same type.
-->
You've actually been using value types extensively throughout the previous chapters.
In fact, all of the basic types in Swift ---
integers, floating-point numbers, Booleans, strings, arrays and dictionaries ---
are value types, and are implemented as structures behind the scenes.
All structures and enumerations are value types in Swift.
This means that any structure and enumeration instances you create ---
and any value types they have as properties ---
are always copied when they're passed around in your code.
> Note: Collections defined by the Swift standard library
> like arrays, dictionaries, and strings
> use an optimization to reduce the performance cost of copying.
> Instead of making a copy immediately,
> these collections share the memory where the elements are stored
> between the original instance and any copies.
> If one of the copies of the collection is modified,
> the elements are copied just before the modification.
> The behavior you see in your code
> is always as if a copy took place immediately.
Consider this example, which uses the `Resolution` structure from the previous example:
```swift
let hd = Resolution(width: 1920, height: 1080)
var cinema = hd
```
<!--
- test: `ClassesAndStructures`
```swifttest
-> let hd = Resolution(width: 1920, height: 1080)
-> var cinema = hd
```
-->
This example declares a constant called `hd`
and sets it to a `Resolution` instance initialized with
the width and height of full HD video
(1920 pixels wide by 1080 pixels high).
It then declares a variable called `cinema`
and sets it to the current value of `hd`.
Because `Resolution` is a structure,
a *copy* of the existing instance is made,
and this new copy is assigned to `cinema`.
Even though `hd` and `cinema` now have the same width and height,
they're two completely different instances behind the scenes.
Next, the `width` property of `cinema` is amended to be
the width of the slightly wider 2K standard used for digital cinema projection
(2048 pixels wide and 1080 pixels high):
```swift
cinema.width = 2048
```
<!--
- test: `ClassesAndStructures`
```swifttest
-> cinema.width = 2048
```
-->
Checking the `width` property of `cinema`
shows that it has indeed changed to be `2048`:
```swift
print("cinema is now \(cinema.width) pixels wide")
// Prints "cinema is now 2048 pixels wide".
```
<!--
- test: `ClassesAndStructures`
```swifttest
-> print("cinema is now \(cinema.width) pixels wide")
<- cinema is now 2048 pixels wide
```
-->
However, the `width` property of the original `hd` instance
still has the old value of `1920`:
```swift
print("hd is still \(hd.width) pixels wide")
// Prints "hd is still 1920 pixels wide".
```
<!--
- test: `ClassesAndStructures`
```swifttest
-> print("hd is still \(hd.width) pixels wide")
<- hd is still 1920 pixels wide
```
-->
When `cinema` was given the current value of `hd`,
the *values* stored in `hd` were copied into the new `cinema` instance.
The end result was two completely separate instances
that contained the same numeric values.
However, because they're separate instances,
setting the width of `cinema` to `2048`
doesn't affect the width stored in `hd`,
as shown in the figure below:
![](sharedStateStruct)
The same behavior applies to enumerations:
```swift
enum CompassPoint {
case north, south, east, west
mutating func turnNorth() {
self = .north
}
}
var currentDirection = CompassPoint.west
let rememberedDirection = currentDirection
currentDirection.turnNorth()
print("The current direction is \(currentDirection)")
print("The remembered direction is \(rememberedDirection)")
// Prints "The current direction is north".
// Prints "The remembered direction is west".
```
<!--
- test: `ClassesAndStructures`
```swifttest
-> enum CompassPoint {
case north, south, east, west
mutating func turnNorth() {
self = .north
}
}
-> var currentDirection = CompassPoint.west
-> let rememberedDirection = currentDirection
-> currentDirection.turnNorth()
-> print("The current direction is \(currentDirection)")
-> print("The remembered direction is \(rememberedDirection)")
<- The current direction is north
<- The remembered direction is west
```
-->
When `rememberedDirection` is assigned the value of `currentDirection`,
it's actually set to a copy of that value.
Changing the value of `currentDirection` thereafter doesn't affect
the copy of the original value that was stored in `rememberedDirection`.
<!--
TODO: Should I give an example of passing a value type to a function here?
-->
## Classes Are Reference Types
Unlike value types, *reference types* are *not* copied
when they're assigned to a variable or constant,
or when they're passed to a function.
Rather than a copy, a reference to the same existing instance is used.
Here's an example, using the `VideoMode` class defined above:
```swift
let tenEighty = VideoMode()
tenEighty.resolution = hd
tenEighty.interlaced = true
tenEighty.name = "1080i"
tenEighty.frameRate = 25.0
```
<!--
- test: `ClassesAndStructures`
```swifttest
-> let tenEighty = VideoMode()
-> tenEighty.resolution = hd
-> tenEighty.interlaced = true
-> tenEighty.name = "1080i"
-> tenEighty.frameRate = 25.0
```
-->
This example declares a new constant called `tenEighty`
and sets it to refer to a new instance of the `VideoMode` class.
The video mode is assigned a copy of the HD resolution of `1920` by `1080` from before.
It's set to be interlaced,
its name is set to `"1080i"`,
and its frame rate is set to `25.0` frames per second.
Next, `tenEighty` is assigned to a new constant, called `alsoTenEighty`,
and the frame rate of `alsoTenEighty` is modified:
```swift
let alsoTenEighty = tenEighty
alsoTenEighty.frameRate = 30.0
```
<!--
- test: `ClassesAndStructures`
```swifttest
-> let alsoTenEighty = tenEighty
-> alsoTenEighty.frameRate = 30.0
```
-->
Because classes are reference types,
`tenEighty` and `alsoTenEighty` actually both refer to the *same* `VideoMode` instance.
Effectively, they're just two different names for the same single instance,
as shown in the figure below:
![](sharedStateClass)
Checking the `frameRate` property of `tenEighty`
shows that it correctly reports the new frame rate of `30.0`
from the underlying `VideoMode` instance:
```swift
print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
// Prints "The frameRate property of tenEighty is now 30.0".
```
<!--
- test: `ClassesAndStructures`
```swifttest
-> print("The frameRate property of tenEighty is now \(tenEighty.frameRate)")
<- The frameRate property of tenEighty is now 30.0
```
-->
This example also shows how reference types can be harder to reason about.
If `tenEighty` and `alsoTenEighty` were far apart in your program's code,
it could be difficult to find all the ways that the video mode is changed.
Wherever you use `tenEighty`,
you also have to think about the code that uses `alsoTenEighty`,
and vice versa.
In contrast, value types are easier to reason about
because all of the code that interacts with the same value
is close together in your source files.
Note that `tenEighty` and `alsoTenEighty` are declared as *constants*,
rather than variables.
However, you can still change `tenEighty.frameRate` and `alsoTenEighty.frameRate` because
the values of the `tenEighty` and `alsoTenEighty` constants themselves don't actually change.
`tenEighty` and `alsoTenEighty` themselves don't “store” the `VideoMode` instance ---
instead, they both *refer* to a `VideoMode` instance behind the scenes.
It's the `frameRate` property of the underlying `VideoMode` that's changed,
not the values of the constant references to that `VideoMode`.
<!--
TODO: reiterate here that arrays and dictionaries are value types rather than reference types,
and demonstrate what that means for the values they store
when they themselves are value types or reference types.
Also make a note about what this means for key copying,
as per the swift-discuss email thread "Dictionaries and key copying"
started by Alex Migicovsky on Mar 1 2014.
-->
<!--
TODO: Add discussion about how
a struct that has a member of some reference type
is itself actually a reference type,
and about how you can make a class that's a value type.
-->
### Identity Operators
Because classes are reference types,
it's possible for multiple constants and variables to refer to
the same single instance of a class behind the scenes.
(The same isn't true for structures and enumerations,
because they're always copied when they're assigned to a constant or variable,
or passed to a function.)
<!--
- test: `structuresDontSupportTheIdentityOperators`
```swifttest
-> struct S { var x = 0, y = 0 }
-> let s1 = S()
-> let s2 = S()
-> if s1 === s2 { print("s1 === s2") } else { print("s1 !== s2") }
!$ error: argument type 'S' expected to be an instance of a class or class-constrained type
!! if s1 === s2 { print("s1 === s2") } else { print("s1 !== s2") }
!! ^
!$ error: argument type 'S' expected to be an instance of a class or class-constrained type
!! if s1 === s2 { print("s1 === s2") } else { print("s1 !== s2") }
!! ^
```
-->
<!--
- test: `enumerationsDontSupportTheIdentityOperators`
```swifttest
-> enum E { case a, b }
-> let e1 = E.a
-> let e2 = E.b
-> if e1 === e2 { print("e1 === e2") } else { print("e1 !== e2") }
!$ error: argument type 'E' expected to be an instance of a class or class-constrained type
!! if e1 === e2 { print("e1 === e2") } else { print("e1 !== e2") }
!! ^
!$ error: argument type 'E' expected to be an instance of a class or class-constrained type
!! if e1 === e2 { print("e1 === e2") } else { print("e1 !== e2") }
!! ^
```
-->
It can sometimes be useful to find out whether two constants or variables refer to
exactly the same instance of a class.
To enable this, Swift provides two identity operators:
- Identical to (`===`)
- Not identical to (`!==`)
Use these operators to check whether two constants or variables refer to the same single instance:
```swift
if tenEighty === alsoTenEighty {
print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
// Prints "tenEighty and alsoTenEighty refer to the same VideoMode instance."
```
<!--
- test: `ClassesAndStructures`
```swifttest
-> if tenEighty === alsoTenEighty {
print("tenEighty and alsoTenEighty refer to the same VideoMode instance.")
}
<- tenEighty and alsoTenEighty refer to the same VideoMode instance.
```
-->
Note that *identical to* (represented by three equal signs, or `===`)
doesn't mean the same thing as *equal to* (represented by two equal signs, or `==`).
*Identical to* means that
two constants or variables of class type refer to exactly the same class instance.
*Equal to* means that
two instances are considered equal or equivalent in value,
for some appropriate meaning of *equal*, as defined by the type's designer.
When you define your own custom structures and classes,
it's your responsibility to decide what qualifies as two instances being equal.
The process of defining your own implementations of the `==` and `!=` operators
is described in <doc:AdvancedOperators#Equivalence-Operators>.
<!--
- test: `classesDontGetEqualityByDefault`
```swifttest
-> class C { var x = 0, y = 0 }
-> let c1 = C()
-> let c2 = C()
-> if c1 == c2 { print("c1 == c2") } else { print("c1 != c2") }
!$ error: binary operator '==' cannot be applied to two 'C' operands
!! if c1 == c2 { print("c1 == c2") } else { print("c1 != c2") }
!! ~~ ^ ~~
```
-->
<!--
- test: `structuresDontGetEqualityByDefault`
```swifttest
-> struct S { var x = 0, y = 0 }
-> let s1 = S()
-> let s2 = S()
-> if s1 == s2 { print("s1 == s2") } else { print("s1 != s2") }
!$ error: binary operator '==' cannot be applied to two 'S' operands
!! if s1 == s2 { print("s1 == s2") } else { print("s1 != s2") }
!! ~~ ^ ~~
```
-->
<!--
TODO: This needs clarifying with regards to function references.
-->
### Pointers
If you have experience with C, C++, or Objective-C,
you may know that these languages use *pointers* to refer to addresses in memory.
A Swift constant or variable that refers to an instance of some reference type
is similar to a pointer in C,
but isn't a direct pointer to an address in memory,
and doesn't require you to write an asterisk (`*`)
to indicate that you are creating a reference.
Instead, these references are defined like any other constant or variable in Swift.
The Swift standard library provides pointer and buffer types
that you can use if you need to interact with pointers directly ---
see [Manual Memory Management](https://developer.apple.com/documentation/swift/swift_standard_library/manual_memory_management).
<!--
TODO: functions aren't "instances". This needs clarifying.
-->
<!--
TODO: Add a justification here to say why this is a good thing.
-->
<!--
QUESTION: what's the deal with tuples and reference types / value types?
-->
<!--
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
-->

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,253 @@
# Deinitialization
Release resources that require custom cleanup.
A *deinitializer* is called immediately before a class instance is deallocated.
You write deinitializers with the `deinit` keyword,
similar to how initializers are written with the `init` keyword.
Deinitializers are only available on class types.
## How Deinitialization Works
Swift automatically deallocates your instances when they're no longer needed,
to free up resources.
Swift handles the memory management of instances through
*automatic reference counting* (*ARC*),
as described in <doc:AutomaticReferenceCounting>.
Typically you don't need to perform manual cleanup when your instances are deallocated.
However, when you are working with your own resources,
you might need to perform some additional cleanup yourself.
For example, if you create a custom class to open a file and write some data to it,
you might need to close the file before the class instance is deallocated.
Class definitions can have at most one deinitializer per class.
The deinitializer doesn't take any parameters
and is written without parentheses:
```swift
deinit {
// perform the deinitialization
}
```
<!--
- test: `deinitializer`
```swifttest
>> class Test {
-> deinit {
// perform the deinitialization
}
>> }
```
-->
Deinitializers are called automatically, just before instance deallocation takes place.
You aren't allowed to call a deinitializer yourself.
Superclass deinitializers are inherited by their subclasses,
and the superclass deinitializer is called automatically at the end of
a subclass deinitializer implementation.
Superclass deinitializers are always called,
even if a subclass doesn't provide its own deinitializer.
Because an instance isn't deallocated until after its deinitializer is called,
a deinitializer can access all properties of the instance it's called on
and can modify its behavior based on those properties
(such as looking up the name of a file that needs to be closed).
## Deinitializers in Action
Here's an example of a deinitializer in action.
This example defines two new types, `Bank` and `Player`, for a simple game.
The `Bank` class manages a made-up currency,
which can never have more than 10,000 coins in circulation.
There can only ever be one `Bank` in the game,
and so the `Bank` is implemented as a class with type properties and methods
to store and manage its current state:
```swift
class Bank {
static var coinsInBank = 10_000
static func distribute(coins numberOfCoinsRequested: Int) -> Int {
let numberOfCoinsToVend = min(numberOfCoinsRequested, coinsInBank)
coinsInBank -= numberOfCoinsToVend
return numberOfCoinsToVend
}
static func receive(coins: Int) {
coinsInBank += coins
}
}
```
<!--
- test: `deinitializer`
```swifttest
-> class Bank {
static var coinsInBank = 10_000
static func distribute(coins numberOfCoinsRequested: Int) -> Int {
let numberOfCoinsToVend = min(numberOfCoinsRequested, coinsInBank)
coinsInBank -= numberOfCoinsToVend
return numberOfCoinsToVend
}
static func receive(coins: Int) {
coinsInBank += coins
}
}
```
-->
`Bank` keeps track of the current number of coins it holds with its `coinsInBank` property.
It also offers two methods --- `distribute(coins:)` and `receive(coins:)` ---
to handle the distribution and collection of coins.
The `distribute(coins:)` method checks that there are enough coins in the bank before distributing them.
If there aren't enough coins,
`Bank` returns a smaller number than the number that was requested
(and returns zero if no coins are left in the bank).
It returns an integer value to indicate the actual number of coins that were provided.
The `receive(coins:)` method simply adds the received number of coins back into the bank's coin store.
The `Player` class describes a player in the game.
Each player has a certain number of coins stored in their purse at any time.
This is represented by the player's `coinsInPurse` property:
```swift
class Player {
var coinsInPurse: Int
init(coins: Int) {
coinsInPurse = Bank.distribute(coins: coins)
}
func win(coins: Int) {
coinsInPurse += Bank.distribute(coins: coins)
}
deinit {
Bank.receive(coins: coinsInPurse)
}
}
```
<!--
- test: `deinitializer`
```swifttest
-> class Player {
var coinsInPurse: Int
init(coins: Int) {
coinsInPurse = Bank.distribute(coins: coins)
}
func win(coins: Int) {
coinsInPurse += Bank.distribute(coins: coins)
}
deinit {
Bank.receive(coins: coinsInPurse)
}
}
```
-->
Each `Player` instance is initialized with a starting allowance of
a specified number of coins from the bank during initialization,
although a `Player` instance may receive fewer than that number
if not enough coins are available.
The `Player` class defines a `win(coins:)` method,
which retrieves a certain number of coins from the bank
and adds them to the player's purse.
The `Player` class also implements a deinitializer,
which is called just before a `Player` instance is deallocated.
Here, the deinitializer simply returns all of the player's coins to the bank:
```swift
var playerOne: Player? = Player(coins: 100)
print("A new player has joined the game with \(playerOne!.coinsInPurse) coins")
// Prints "A new player has joined the game with 100 coins".
print("There are now \(Bank.coinsInBank) coins left in the bank")
// Prints "There are now 9900 coins left in the bank".
```
<!--
- test: `deinitializer`
```swifttest
-> var playerOne: Player? = Player(coins: 100)
-> print("A new player has joined the game with \(playerOne!.coinsInPurse) coins")
<- A new player has joined the game with 100 coins
-> print("There are now \(Bank.coinsInBank) coins left in the bank")
<- There are now 9900 coins left in the bank
```
-->
A new `Player` instance is created, with a request for 100 coins if they're available.
This `Player` instance is stored in an optional `Player` variable called `playerOne`.
An optional variable is used here, because players can leave the game at any point.
The optional lets you track whether there's currently a player in the game.
Because `playerOne` is an optional, it's qualified with an exclamation point (`!`)
when its `coinsInPurse` property is accessed to print its default number of coins,
and whenever its `win(coins:)` method is called:
```swift
playerOne!.win(coins: 2_000)
print("PlayerOne won 2000 coins & now has \(playerOne!.coinsInPurse) coins")
// Prints "PlayerOne won 2000 coins & now has 2100 coins".
print("The bank now only has \(Bank.coinsInBank) coins left")
// Prints "The bank now only has 7900 coins left".
```
<!--
- test: `deinitializer`
```swifttest
-> playerOne!.win(coins: 2_000)
-> print("PlayerOne won 2000 coins & now has \(playerOne!.coinsInPurse) coins")
<- PlayerOne won 2000 coins & now has 2100 coins
-> print("The bank now only has \(Bank.coinsInBank) coins left")
<- The bank now only has 7900 coins left
```
-->
Here, the player has won 2,000 coins.
The player's purse now contains 2,100 coins,
and the bank has only 7,900 coins left.
```swift
playerOne = nil
print("PlayerOne has left the game")
// Prints "PlayerOne has left the game".
print("The bank now has \(Bank.coinsInBank) coins")
// Prints "The bank now has 10000 coins".
```
<!--
- test: `deinitializer`
```swifttest
-> playerOne = nil
-> print("PlayerOne has left the game")
<- PlayerOne has left the game
-> print("The bank now has \(Bank.coinsInBank) coins")
<- The bank now has 10000 coins
```
-->
The player has now left the game.
This is indicated by setting the optional `playerOne` variable to `nil`,
meaning “no `Player` instance.”
At the point that this happens,
the `playerOne` variable's reference to the `Player` instance is broken.
No other properties or variables are still referring to the `Player` instance,
and so it's deallocated in order to free up its memory.
Just before this happens, its deinitializer is called automatically,
and its coins are returned to the bank.
<!--
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
-->

View File

@@ -0,0 +1,865 @@
# Enumerations
Model custom types that define a list of possible values.
An *enumeration* defines a common type for a group of related values
and enables you to work with those values in a type-safe way within your code.
If you are familiar with C,
you will know that C enumerations assign related names to a set of integer values.
Enumerations in Swift are much more flexible,
and don't have to provide a value for each case of the enumeration.
If a value (known as a *raw* value) is provided for each enumeration case,
the value can be a string, a character,
or a value of any integer or floating-point type.
Alternatively, enumeration cases can specify
associated values of *any* type to be stored along with each different case value,
much as unions or variants do in other languages.
You can define a common set of related cases as part of one enumeration,
each of which has a different set of values of appropriate types associated with it.
Enumerations in Swift are first-class types in their own right.
They adopt many features traditionally supported only by classes,
such as computed properties to provide additional information about
the enumeration's current value,
and instance methods to provide functionality related to
the values the enumeration represents.
Enumerations can also define initializers to provide an initial case value;
can be extended to expand their functionality beyond their original implementation;
and can conform to protocols to provide standard functionality.
For more about these capabilities, see
<doc:Properties>, <doc:Methods>, <doc:Initialization>,
<doc:Extensions>, and <doc:Protocols>.
<!--
TODO: this chapter should probably mention that enums without associated values
are hashable and equatable by default (and what that means in practice)
-->
## Enumeration Syntax
You introduce enumerations with the `enum` keyword
and place their entire definition within a pair of braces:
```swift
enum SomeEnumeration {
// enumeration definition goes here
}
```
<!--
- test: `enums`
```swifttest
-> enum SomeEnumeration {
// enumeration definition goes here
}
```
-->
Here's an example for the four main points of a compass:
```swift
enum CompassPoint {
case north
case south
case east
case west
}
```
<!--
- test: `enums`
```swifttest
-> enum CompassPoint {
case north
case south
case east
case west
}
```
-->
The values defined in an enumeration
(such as `north`, `south`, `east`, and `west`)
are its *enumeration cases*.
You use the `case` keyword to introduce new enumeration cases.
> Note: Swift enumeration cases don't have an integer value set by default,
> unlike languages like C and Objective-C.
> In the `CompassPoint` example above,
> `north`, `south`, `east` and `west`
> don't implicitly equal
> `0`, `1`, `2` and `3`.
> Instead, the different enumeration cases are values in their own right,
> with an explicitly defined type of `CompassPoint`.
Multiple cases can appear on a single line, separated by commas:
```swift
enum Planet {
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
```
<!--
- test: `enums`
```swifttest
-> enum Planet {
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
```
-->
Each enumeration definition defines a new type.
Like other types in Swift, their names
(such as `CompassPoint` and `Planet`)
start with a capital letter.
Give enumeration types singular rather than plural names,
so that they read as self-evident:
```swift
var directionToHead = CompassPoint.west
```
<!--
- test: `enums`
```swifttest
-> var directionToHead = CompassPoint.west
```
-->
The type of `directionToHead` is inferred
when it's initialized with one of the possible values of `CompassPoint`.
Once `directionToHead` is declared as a `CompassPoint`,
you can set it to a different `CompassPoint` value using a shorter dot syntax:
```swift
directionToHead = .east
```
<!--
- test: `enums`
```swifttest
-> directionToHead = .east
```
-->
The type of `directionToHead` is already known,
and so you can drop the type when setting its value.
This makes for highly readable code when working with explicitly typed enumeration values.
## Matching Enumeration Values with a Switch Statement
You can match individual enumeration values with a `switch` statement:
```swift
directionToHead = .south
switch directionToHead {
case .north:
print("Lots of planets have a north")
case .south:
print("Watch out for penguins")
case .east:
print("Where the sun rises")
case .west:
print("Where the skies are blue")
}
// Prints "Watch out for penguins".
```
<!--
- test: `enums`
```swifttest
-> directionToHead = .south
-> switch directionToHead {
case .north:
print("Lots of planets have a north")
case .south:
print("Watch out for penguins")
case .east:
print("Where the sun rises")
case .west:
print("Where the skies are blue")
}
<- Watch out for penguins
```
-->
You can read this code as:
“Consider the value of `directionToHead`.
In the case where it equals `.north`,
print `"Lots of planets have a north"`.
In the case where it equals `.south`,
print `"Watch out for penguins"`.”
…and so on.
As described in <doc:ControlFlow>,
a `switch` statement must be exhaustive when considering an enumeration's cases.
If the `case` for `.west` is omitted,
this code doesn't compile,
because it doesn't consider the complete list of `CompassPoint` cases.
Requiring exhaustiveness ensures that enumeration cases aren't accidentally omitted.
When it isn't appropriate to provide a `case` for every enumeration case,
you can provide a `default` case to cover any cases that aren't addressed explicitly:
```swift
let somePlanet = Planet.earth
switch somePlanet {
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
}
// Prints "Mostly harmless".
```
<!--
- test: `enums`
```swifttest
-> let somePlanet = Planet.earth
-> switch somePlanet {
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
}
<- Mostly harmless
```
-->
## Iterating over Enumeration Cases
For some enumerations,
it's useful to have a collection of all of that enumeration's cases.
You enable this by
writing `: CaseIterable` after the enumeration's name.
Swift exposes a collection of all the cases
as an `allCases` property of the enumeration type.
Here's an example:
```swift
enum Beverage: CaseIterable {
case coffee, tea, juice
}
let numberOfChoices = Beverage.allCases.count
print("\(numberOfChoices) beverages available")
// Prints "3 beverages available".
```
<!--
- test: `enums`
```swifttest
-> enum Beverage: CaseIterable {
case coffee, tea, juice
}
-> let numberOfChoices = Beverage.allCases.count
-> print("\(numberOfChoices) beverages available")
<- 3 beverages available
```
-->
In the example above,
you write `Beverage.allCases` to access a collection
that contains all of the cases of the `Beverage` enumeration.
You can use `allCases` like any other collection ---
the collection's elements are instances of the enumeration type,
so in this case they're `Beverage` values.
The example above counts how many cases there are,
and the example below uses a `for`-`in` loop to iterate over all the cases.
```swift
for beverage in Beverage.allCases {
print(beverage)
}
// coffee
// tea
// juice
```
<!--
- test: `enums`
```swifttest
-> for beverage in Beverage.allCases {
print(beverage)
}
<< coffee
<< tea
<< juice
// coffee
// tea
// juice
```
-->
The syntax used in the examples above
marks the enumeration as conforming to the
[`CaseIterable`](https://developer.apple.com/documentation/swift/caseiterable) protocol.
For information about protocols, see <doc:Protocols>.
## Associated Values
The examples in the previous section show how the cases of an enumeration are
a defined (and typed) value in their own right.
You can set a constant or variable to `Planet.earth`,
and check for this value later.
However, it's sometimes useful to be able to store
values of other types alongside these case values.
This additional information is called an *associated value*,
and it varies each time you use that case as a value in your code.
You can define Swift enumerations to store associated values of any given type,
and the value types can be different for each case of the enumeration if needed.
Enumerations similar to these are known as
*discriminated unions*, *tagged unions*, or *variants*
in other programming languages.
For example, suppose an inventory tracking system needs to
track products by two different types of barcode.
Some products are labeled with 1D barcodes in UPC format,
which uses the numbers `0` to `9`.
Each barcode has a number system digit,
followed by five manufacturer code digits and five product code digits.
These are followed by a check digit to verify that the code has been scanned correctly:
![](barcode_UPC)
Other products are labeled with 2D barcodes in QR code format,
which can use any ISO 8859-1 character
and can encode a string up to 2,953 characters long:
![](barcode_QR)
It's convenient for an inventory tracking system to store UPC barcodes
as a tuple of four integers,
and QR code barcodes as a string of any length.
In Swift, an enumeration to define product barcodes of either type might look like this:
```swift
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
```
<!--
- test: `enums`
```swifttest
-> enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
```
-->
This can be read as:
“Define an enumeration type called `Barcode`,
which can take either a value of `upc`
with an associated value of type (`Int`, `Int`, `Int`, `Int`),
or a value of `qrCode` with an associated value of type `String`.”
This definition doesn't provide any actual `Int` or `String` values ---
it just defines the *type* of associated values
that `Barcode` constants and variables can store
when they're equal to `Barcode.upc` or `Barcode.qrCode`.
You can then create new barcodes using either type:
```swift
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
```
<!--
- test: `enums`
```swifttest
-> var productBarcode = Barcode.upc(8, 85909, 51226, 3)
```
-->
This example creates a new variable called `productBarcode`
and assigns it a value of `Barcode.upc`
with an associated tuple value of `(8, 85909, 51226, 3)`.
You can assign the same product a different type of barcode:
```swift
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
```
<!--
- test: `enums`
```swifttest
-> productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
```
-->
At this point,
the original `Barcode.upc` and its integer values are replaced by
the new `Barcode.qrCode` and its string value.
Constants and variables of type `Barcode` can store either a `.upc` or a `.qrCode`
(together with their associated values),
but they can store only one of them at any given time.
You can check the different barcode types using a switch statement,
similar to the example in
<doc:Enumerations#Matching-Enumeration-Values-with-a-Switch-Statement>.
This time, however,
the associated values are extracted as part of the switch statement.
You extract each associated value as a constant (with the `let` prefix)
or a variable (with the `var` prefix)
for use within the `switch` case's body:
```swift
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
print("QR code: \(productCode).")
}
// Prints "QR code: ABCDEFGHIJKLMNOP."
```
<!--
- test: `enums`
```swifttest
-> switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
print("QR code: \(productCode).")
}
<- QR code: ABCDEFGHIJKLMNOP.
```
-->
If all of the associated values for an enumeration case
are extracted as constants, or if all are extracted as variables,
you can place a single `let` or `var` annotation before the case name, for brevity:
```swift
switch productBarcode {
case let .upc(numberSystem, manufacturer, product, check):
print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).")
case let .qrCode(productCode):
print("QR code: \(productCode).")
}
// Prints "QR code: ABCDEFGHIJKLMNOP."
```
<!--
- test: `enums`
```swifttest
-> switch productBarcode {
case let .upc(numberSystem, manufacturer, product, check):
print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).")
case let .qrCode(productCode):
print("QR code: \(productCode).")
}
<- QR code: ABCDEFGHIJKLMNOP.
```
-->
When you're matching just one case of an enumeration ---
for example,
to extract its associated value ---
you can use an `if`-`case` statement
instead of writing a full switch statement.
Here's what it looks like:
```swift
if case .qrCode(let productCode) = productBarcode {
print("QR code: \(productCode).")
}
```
Just like in the switch statement earlier,
the `productBarcode` variable is matched against
the pattern `.qrCode(let productCode)` here.
And as in the switch case,
writing `let` extracts the associated value as a constant.
For more information about `if`-`case` statements,
see <doc:ControlFlow#Patterns>.
## Raw Values
The barcode example in <doc:Enumerations#Associated-Values>
shows how cases of an enumeration can declare that they store
associated values of different types.
As an alternative to associated values,
enumeration cases can come prepopulated with default values
(called *raw values*),
which are all of the same type.
Here's an example that stores raw ASCII values alongside named enumeration cases:
```swift
enum ASCIIControlCharacter: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
```
<!--
- test: `rawValues`
```swifttest
-> enum ASCIIControlCharacter: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
```
-->
Here, the raw values for an enumeration called `ASCIIControlCharacter`
are defined to be of type `Character`,
and are set to some of the more common ASCII control characters.
`Character` values are described in <doc:StringsAndCharacters>.
Raw values can be
strings, characters, or any of the integer or floating-point number types.
Each raw value must be unique within its enumeration declaration.
Although you can use both raw values and associated values
to give an enumeration an additional value,
it's important to understand the difference between them.
You pick the raw value for an enumeration case
when you define that enumeration case in your code,
such as the three ASCII codes above.
The raw value for a particular enumeration case is always the same.
In contrast,
you pick associated values when you create a new constant or variable
using one of the enumeration's cases,
and you can pick a different value each time you do so.
### Implicitly Assigned Raw Values
When you're working with enumerations that store integer or string raw values,
you don't have to explicitly assign a raw value for each case.
When you don't, Swift automatically assigns the values for you.
For example, when integers are used for raw values,
the implicit value for each case is one more than the previous case.
If the first case doesn't have a value set, its value is `0`.
The enumeration below is a refinement of the earlier `Planet` enumeration,
with integer raw values to represent each planet's order from the sun:
```swift
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
```
<!--
- test: `rawValues`
```swifttest
-> enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
```
-->
In the example above,
`Planet.mercury` has an explicit raw value of `1`,
`Planet.venus` has an implicit raw value of `2`, and so on.
When strings are used for raw values,
the implicit value for each case is the text of that case's name.
The enumeration below is a refinement of the earlier `CompassPoint` enumeration,
with string raw values to represent each direction's name:
```swift
enum CompassPoint: String {
case north, south, east, west
}
```
<!--
- test: `rawValues`
```swifttest
-> enum CompassPoint: String {
case north, south, east, west
}
```
-->
In the example above,
`CompassPoint.south` has an implicit raw value of `"south"`, and so on.
You access the raw value of an enumeration case with its `rawValue` property:
```swift
let earthsOrder = Planet.earth.rawValue
// earthsOrder is 3
let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection is "west"
```
<!--
- test: `rawValues`
```swifttest
-> let earthsOrder = Planet.earth.rawValue
/> earthsOrder is \(earthsOrder)
</ earthsOrder is 3
-> let sunsetDirection = CompassPoint.west.rawValue
/> sunsetDirection is \"\(sunsetDirection)\"
</ sunsetDirection is "west"
```
-->
### Initializing from a Raw Value
If you define an enumeration with a raw-value type,
the enumeration automatically receives an initializer
that takes a value of the raw value's type (as a parameter called `rawValue`)
and returns either an enumeration case or `nil`.
You can use this initializer to try to create a new instance of the enumeration.
This example identifies Uranus from its raw value of `7`:
```swift
let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet is of type Planet? and equals Planet.uranus
```
<!--
- test: `rawValues`
```swifttest
-> let possiblePlanet = Planet(rawValue: 7)
>> print(type(of: possiblePlanet))
<< Optional<Planet>
>> assert(possiblePlanet == .uranus)
// possiblePlanet is of type Planet? and equals Planet.uranus
```
-->
Not all possible `Int` values will find a matching planet, however.
Because of this, the raw value initializer always returns an *optional* enumeration case.
In the example above, `possiblePlanet` is of type `Planet?`,
or “optional `Planet`.”
> Note: The raw value initializer is a failable initializer,
> because not every raw value will return an enumeration case.
> For more information, see <doc:Declarations#Failable-Initializers>.
If you try to find a planet with a position of `11`,
the optional `Planet` value returned by the raw value initializer will be `nil`:
```swift
let positionToFind = 11
if let somePlanet = Planet(rawValue: positionToFind) {
switch somePlanet {
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
}
} else {
print("There isn't a planet at position \(positionToFind)")
}
// Prints "There isn't a planet at position 11".
```
<!--
- test: `rawValues`
```swifttest
-> let positionToFind = 11
-> if let somePlanet = Planet(rawValue: positionToFind) {
switch somePlanet {
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
}
} else {
print("There isn't a planet at position \(positionToFind)")
}
<- There isn't a planet at position 11
```
-->
This example uses optional binding to try to access a planet with a raw value of `11`.
The statement `if let somePlanet = Planet(rawValue: 11)` creates an optional `Planet`,
and sets `somePlanet` to the value of that optional `Planet` if it can be retrieved.
In this case, it isn't possible to retrieve a planet with a position of `11`,
and so the `else` branch is executed instead.
<!--
TODO: Switch around the order of this chapter so that all of the non-union stuff
is together, and the union bits (aka Associated Values) come last.
-->
## Recursive Enumerations
A *recursive enumeration* is an enumeration
that has another instance of the enumeration
as the associated value for one or more of the enumeration cases.
You indicate that an enumeration case is recursive
by writing `indirect` before it,
which tells the compiler to insert the necessary layer of indirection.
For example, here is an enumeration that stores simple arithmetic expressions:
```swift
enum ArithmeticExpression {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression)
indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}
```
<!--
- test: `recursive-enum-intro`
```swifttest
-> enum ArithmeticExpression {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression)
indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}
```
-->
You can also write `indirect` before the beginning of the enumeration
to enable indirection for all of the enumeration's cases that have an associated value:
```swift
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}
```
<!--
- test: `recursive-enum`
```swifttest
-> indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}
```
-->
This enumeration can store three kinds of arithmetic expressions:
a plain number,
the addition of two expressions,
and the multiplication of two expressions.
The `addition` and `multiplication` cases have associated values
that are also arithmetic expressions ---
these associated values make it possible to nest expressions.
For example, the expression `(5 + 4) * 2`
has a number on the right-hand side of the multiplication
and another expression on the left-hand side of the multiplication.
Because the data is nested,
the enumeration used to store the data also needs to support nesting ---
this means the enumeration needs to be recursive.
The code below shows the `ArithmeticExpression` recursive enumeration
being created for `(5 + 4) * 2`:
```swift
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
```
<!--
- test: `recursive-enum`
```swifttest
-> let five = ArithmeticExpression.number(5)
-> let four = ArithmeticExpression.number(4)
-> let sum = ArithmeticExpression.addition(five, four)
-> let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
```
-->
A recursive function is a straightforward way
to work with data that has a recursive structure.
For example, here's a function that evaluates an arithmetic expression:
```swift
func evaluate(_ expression: ArithmeticExpression) -> Int {
switch expression {
case let .number(value):
return value
case let .addition(left, right):
return evaluate(left) + evaluate(right)
case let .multiplication(left, right):
return evaluate(left) * evaluate(right)
}
}
print(evaluate(product))
// Prints "18".
```
<!--
- test: `recursive-enum`
```swifttest
-> func evaluate(_ expression: ArithmeticExpression) -> Int {
switch expression {
case let .number(value):
return value
case let .addition(left, right):
return evaluate(left) + evaluate(right)
case let .multiplication(left, right):
return evaluate(left) * evaluate(right)
}
}
-> print(evaluate(product))
<- 18
```
-->
This function evaluates a plain number
by simply returning the associated value.
It evaluates an addition or multiplication
by evaluating the expression on the left-hand side,
evaluating the expression on the right-hand side,
and then adding them or multiplying them.
<!--
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
-->

View File

@@ -0,0 +1,993 @@
# Error Handling
Respond to and recover from errors.
*Error handling* is the process of responding to
and recovering from error conditions in your program.
Swift provides first-class support for
throwing, catching, propagating, and manipulating
recoverable errors at runtime.
Some operations
aren't guaranteed to always complete execution or produce a useful output.
Optionals are used to represent the absence of a value,
but when an operation fails,
it's often useful to understand what caused the failure,
so that your code can respond accordingly.
As an example, consider the task of reading and processing data from a file on disk.
There are a number of ways this task can fail, including
the file not existing at the specified path,
the file not having read permissions, or
the file not being encoded in a compatible format.
Distinguishing among these different situations
allows a program to resolve some errors
and to communicate to the user any errors it can't resolve.
> Note: Error handling in Swift interoperates with error handling patterns
> that use the `NSError` class in Cocoa and Objective-C.
> For more information about this class,
> see [Handling Cocoa Errors in Swift](https://developer.apple.com/documentation/swift/cocoa_design_patterns/handling_cocoa_errors_in_swift).
## Representing and Throwing Errors
In Swift, errors are represented by
values of types that conform to the `Error` protocol.
This empty protocol indicates that a type
can be used for error handling.
Swift enumerations are particularly well suited to modeling
a group of related error conditions,
with associated values allowing for additional information
about the nature of an error to be communicated.
For example, here's how you might represent the error conditions
of operating a vending machine inside a game:
```swift
enum VendingMachineError: Error {
case invalidSelection
case insufficientFunds(coinsNeeded: Int)
case outOfStock
}
```
<!--
- test: `throw-enum-error`
```swifttest
-> enum VendingMachineError: Error {
case invalidSelection
case insufficientFunds(coinsNeeded: Int)
case outOfStock
}
```
-->
Throwing an error lets you indicate that something unexpected happened
and the normal flow of execution can't continue.
You use a `throw` statement to throw an error.
For example,
the following code throws an error to indicate
that five additional coins are needed by the vending machine:
```swift
throw VendingMachineError.insufficientFunds(coinsNeeded: 5)
```
<!--
- test: `throw-enum-error`
```swifttest
-> throw VendingMachineError.insufficientFunds(coinsNeeded: 5)
xx fatal error
```
-->
## Handling Errors
When an error is thrown,
some surrounding piece of code must be responsible
for handling the error ---
for example, by correcting the problem,
trying an alternative approach,
or informing the user of the failure.
There are four ways to handle errors in Swift.
You can propagate the error from a function to the code that calls that function,
handle the error using a `do`-`catch` statement,
handle the error as an optional value,
or assert that the error will not occur.
Each approach is described in a section below.
When a function throws an error,
it changes the flow of your program,
so it's important that you can quickly identify places in your code that can throw errors.
To identify these places in your code, write the `try` keyword ---
or the `try?` or `try!` variation ---
before a piece of code that calls a function, method, or initializer that can throw an error.
These keywords are described in the sections below.
> Note: Error handling in Swift resembles exception handling in other languages,
> with the use of the `try`, `catch` and `throw` keywords.
> Unlike exception handling in many languages ---
> including Objective-C ---
> error handling in Swift doesn't involve unwinding the call stack,
> a process that can be computationally expensive.
> As such, the performance characteristics
> of a `throw` statement
> are comparable to those of a `return` statement.
### Propagating Errors Using Throwing Functions
To indicate that a function, method, or initializer can throw an error,
you write the `throws` keyword in the function's declaration
after its parameters.
A function marked with `throws` is called a *throwing function*.
If the function specifies a return type,
you write the `throws` keyword before the return arrow (`->`).
<!--
TODO Add discussion of throwing initializers
-->
```swift
func canThrowErrors() throws -> String
func cannotThrowErrors() -> String
```
<!--
- test: `throwingFunctionDeclaration`
```swifttest
-> func canThrowErrors() throws -> String
>> { return "foo" }
-> func cannotThrowErrors() -> String
>> { return "foo" }
```
-->
<!--
- test: `throwing-function-cant-overload-nonthrowing`
```swifttest
-> func f() -> Int { return 10 }
-> func f() throws -> Int { return 10 } // Error
!$ error: invalid redeclaration of 'f()'
!! func f() throws -> Int { return 10 } // Error
!! ^
!$ note: 'f()' previously declared here
!! func f() -> Int { return 10 }
!! ^
```
-->
<!--
- test: `throwing-parameter-can-overload-nonthrowing`
```swifttest
-> func f(callback: () -> Int) {}
-> func f(callback: () throws -> Int) {} // Allowed
```
-->
<!--
TODO: Add more assertions to test these behaviors
-->
<!--
TODO: Write about the fact the above rules that govern overloading
for throwing and nonthrowing functions.
-->
A throwing function propagates errors that are thrown inside of it
to the scope from which it's called.
> Note: Only throwing functions can propagate errors.
> Any errors thrown inside a nonthrowing function
> must be handled inside the function.
In the example below,
the `VendingMachine` class has a `vend(itemNamed:)` method
that throws an appropriate `VendingMachineError`
if the requested item isn't available,
is out of stock,
or has a cost that exceeds the current deposited amount:
```swift
struct Item {
var price: Int
var count: Int
}
class VendingMachine {
var inventory = [
"Candy Bar": Item(price: 12, count: 7),
"Chips": Item(price: 10, count: 4),
"Pretzels": Item(price: 7, count: 11)
]
var coinsDeposited = 0
func vend(itemNamed name: String) throws {
guard let item = inventory[name] else {
throw VendingMachineError.invalidSelection
}
guard item.count > 0 else {
throw VendingMachineError.outOfStock
}
guard item.price <= coinsDeposited else {
throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited)
}
coinsDeposited -= item.price
var newItem = item
newItem.count -= 1
inventory[name] = newItem
print("Dispensing \(name)")
}
}
```
<!--
- test: `errorHandling`
```swifttest
>> enum VendingMachineError: Error {
>> case invalidSelection
>> case insufficientFunds(coinsNeeded: Int)
>> case outOfStock
>> }
-> struct Item {
var price: Int
var count: Int
}
-> class VendingMachine {
-> var inventory = [
"Candy Bar": Item(price: 12, count: 7),
"Chips": Item(price: 10, count: 4),
"Pretzels": Item(price: 7, count: 11)
]
-> var coinsDeposited = 0
-> func vend(itemNamed name: String) throws {
guard let item = inventory[name] else {
throw VendingMachineError.invalidSelection
}
guard item.count > 0 else {
throw VendingMachineError.outOfStock
}
guard item.price <= coinsDeposited else {
throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited)
}
coinsDeposited -= item.price
var newItem = item
newItem.count -= 1
inventory[name] = newItem
print("Dispensing \(name)")
}
}
```
-->
The implementation of the `vend(itemNamed:)` method
uses `guard` statements to exit the method early and throw appropriate errors
if any of the requirements for purchasing a snack aren't met.
Because a `throw` statement immediately transfers program control,
an item will be vended only if all of these requirements are met.
Because the `vend(itemNamed:)` method propagates any errors it throws,
any code that calls this method must either handle the errors ---
using a `do`-`catch` statement, `try?`, or `try!` ---
or continue to propagate them.
For example,
the `buyFavoriteSnack(person:vendingMachine:)` in the example below
is also a throwing function,
and any errors that the `vend(itemNamed:)` method throws will
propagate up to the point where the `buyFavoriteSnack(person:vendingMachine:)` function is called.
```swift
let favoriteSnacks = [
"Alice": "Chips",
"Bob": "Licorice",
"Eve": "Pretzels",
]
func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
let snackName = favoriteSnacks[person] ?? "Candy Bar"
try vendingMachine.vend(itemNamed: snackName)
}
```
<!--
- test: `errorHandling`
```swifttest
-> let favoriteSnacks = [
"Alice": "Chips",
"Bob": "Licorice",
"Eve": "Pretzels",
]
-> func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
let snackName = favoriteSnacks[person] ?? "Candy Bar"
try vendingMachine.vend(itemNamed: snackName)
}
>> var v = VendingMachine()
>> v.coinsDeposited = 100
>> try buyFavoriteSnack(person: "Alice", vendingMachine: v)
<< Dispensing Chips
```
-->
In this example,
the `buyFavoriteSnack(person: vendingMachine:)` function looks up a given person's favorite snack
and tries to buy it for them by calling the `vend(itemNamed:)` method.
Because the `vend(itemNamed:)` method can throw an error,
it's called with the `try` keyword in front of it.
Throwing initializers can propagate errors in the same way as throwing functions.
For example,
the initializer for the `PurchasedSnack` structure in the listing below
calls a throwing function as part of the initialization process,
and it handles any errors that it encounters by propagating them to its caller.
```swift
struct PurchasedSnack {
let name: String
init(name: String, vendingMachine: VendingMachine) throws {
try vendingMachine.vend(itemNamed: name)
self.name = name
}
}
```
<!--
- test: `errorHandling`
```swifttest
-> struct PurchasedSnack {
let name: String
init(name: String, vendingMachine: VendingMachine) throws {
try vendingMachine.vend(itemNamed: name)
self.name = name
}
}
>> do {
>> let succeeds = try PurchasedSnack(name: "Candy Bar", vendingMachine: v)
>> print(succeeds)
>> } catch {
>> print("Threw unexpected error.")
>> }
<< Dispensing Candy Bar
<< PurchasedSnack(name: "Candy Bar")
>> do {
>> let throwsError = try PurchasedSnack(name: "Jelly Baby", vendingMachine: v)
>> print(throwsError)
>> } catch {
>> print("Threw EXPECTED error.")
>> }
<< Threw EXPECTED error.
```
-->
### Handling Errors Using Do-Catch
You use a `do`-`catch` statement to handle errors
by running a block of code.
If an error is thrown by the code in the `do` clause,
it's matched against the `catch` clauses
to determine which one of them can handle the error.
Here is the general form of a `do`-`catch` statement:
```swift
do {
try <#expression#>
<#statements#>
} catch <#pattern 1#> {
<#statements#>
} catch <#pattern 2#> where <#condition#> {
<#statements#>
} catch <#pattern 3#>, <#pattern 4#> where <#condition#> {
<#statements#>
} catch {
<#statements#>
}
```
You write a pattern after `catch` to indicate what errors
that clause can handle.
If a `catch` clause doesn't have a pattern,
the clause matches any error
and binds the error to a local constant named `error`.
For more information about pattern matching,
see <doc:Patterns>.
<!--
TODO: Call out the reasoning why we don't let you
consider a catch clause exhaustive by just matching
the errors in an given enum without a general catch/default.
-->
For example, the following code matches against all three cases
of the `VendingMachineError` enumeration.
```swift
var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)
print("Success! Yum.")
} catch VendingMachineError.invalidSelection {
print("Invalid Selection.")
} catch VendingMachineError.outOfStock {
print("Out of Stock.")
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
} catch {
print("Unexpected error: \(error).")
}
// Prints "Insufficient funds. Please insert an additional 2 coins."
```
<!--
- test: `errorHandling`
```swifttest
-> var vendingMachine = VendingMachine()
-> vendingMachine.coinsDeposited = 8
-> do {
try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)
print("Success! Yum.")
} catch VendingMachineError.invalidSelection {
print("Invalid Selection.")
} catch VendingMachineError.outOfStock {
print("Out of Stock.")
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
} catch {
print("Unexpected error: \(error).")
}
<- Insufficient funds. Please insert an additional 2 coins.
```
-->
In the above example,
the `buyFavoriteSnack(person:vendingMachine:)` function is called in a `try` expression,
because it can throw an error.
If an error is thrown,
execution immediately transfers to the `catch` clauses,
which decide whether to allow propagation to continue.
If no pattern is matched, the error gets caught by the final `catch`
clause and is bound to a local `error` constant.
If no error is thrown,
the remaining statements in the `do` statement are executed.
The `catch` clauses don't have to handle every possible error
that the code in the `do` clause can throw.
If none of the `catch` clauses handle the error,
the error propagates to the surrounding scope.
However, the propagated error
must be handled by *some* surrounding scope.
In a nonthrowing function,
an enclosing `do`-`catch` statement
must handle the error.
In a throwing function,
either an enclosing `do`-`catch` statement
or the caller
must handle the error.
If the error propagates to the top-level scope
without being handled,
you'll get a runtime error.
For example, the above example can be written so any
error that isn't a `VendingMachineError` is instead
caught by the calling function:
```swift
func nourish(with item: String) throws {
do {
try vendingMachine.vend(itemNamed: item)
} catch is VendingMachineError {
print("Couldn't buy that from the vending machine.")
}
}
do {
try nourish(with: "Beet-Flavored Chips")
} catch {
print("Unexpected non-vending-machine-related error: \(error)")
}
// Prints "Couldn't buy that from the vending machine."
```
<!--
- test: `errorHandling`
```swifttest
-> func nourish(with item: String) throws {
do {
try vendingMachine.vend(itemNamed: item)
} catch is VendingMachineError {
print("Couldn't buy that from the vending machine.")
}
}
-> do {
try nourish(with: "Beet-Flavored Chips")
} catch {
print("Unexpected non-vending-machine-related error: \(error)")
}
<- Couldn't buy that from the vending machine.
```
-->
In the `nourish(with:)` function,
if `vend(itemNamed:)` throws an error that's
one of the cases of the `VendingMachineError` enumeration,
`nourish(with:)` handles the error by printing a message.
Otherwise,
`nourish(with:)` propagates the error to its call site.
The error is then caught by the general `catch` clause.
Another way to catch several related errors
is to list them after `catch`, separated by commas.
For example:
```swift
func eat(item: String) throws {
do {
try vendingMachine.vend(itemNamed: item)
} catch VendingMachineError.invalidSelection, VendingMachineError.insufficientFunds, VendingMachineError.outOfStock {
print("Invalid selection, out of stock, or not enough money.")
}
}
```
<!--
- test: `errorHandling`
```swifttest
-> func eat(item: String) throws {
do {
try vendingMachine.vend(itemNamed: item)
} catch VendingMachineError.invalidSelection, VendingMachineError.insufficientFunds, VendingMachineError.outOfStock {
print("Invalid selection, out of stock, or not enough money.")
}
}
>> do {
>> try eat(item: "Beet-Flavored Chips")
>> } catch {
>> print("Unexpected error: \(error)")
>> }
<< Invalid selection, out of stock, or not enough money.
```
-->
<!--
FIXME the catch clause is getting indented oddly in HTML output if I hard wrap it
-->
The `eat(item:)` function lists the vending machine errors to catch,
and its error text corresponds to the items in that list.
If any of the three listed errors are thrown,
this `catch` clause handles them by printing a message.
Any other errors are propagated to the surrounding scope,
including any vending-machine errors that might be added later.
### Converting Errors to Optional Values
You use `try?` to handle an error by converting it to an optional value.
If an error is thrown while evaluating the `try?` expression,
the value of the expression is `nil`.
For example,
in the following code `x` and `y` have the same value and behavior:
```swift
func someThrowingFunction() throws -> Int {
// ...
}
let x = try? someThrowingFunction()
let y: Int?
do {
y = try someThrowingFunction()
} catch {
y = nil
}
```
<!--
- test: `optional-try`
```swifttest
-> func someThrowingFunction() throws -> Int {
// ...
>> return 40
-> }
-> let x = try? someThrowingFunction()
>> print(x as Any)
<< Optional(40)
-> let y: Int?
do {
y = try someThrowingFunction()
} catch {
y = nil
}
>> print(y as Any)
<< Optional(40)
```
-->
If `someThrowingFunction()` throws an error,
the value of `x` and `y` is `nil`.
Otherwise, the value of `x` and `y` is the value that the function returned.
Note that `x` and `y` are an optional of whatever type `someThrowingFunction()` returns.
Here the function returns an integer, so `x` and `y` are optional integers.
Using `try?` lets you write concise error handling code
when you want to handle all errors in the same way.
For example,
the following code
uses several approaches to fetch data,
or returns `nil` if all of the approaches fail.
```swift
func fetchData() -> Data? {
if let data = try? fetchDataFromDisk() { return data }
if let data = try? fetchDataFromServer() { return data }
return nil
}
```
<!--
- test: `optional-try-cached-data`
```swifttest
>> struct Data {}
>> func fetchDataFromDisk() throws -> Data { return Data() }
>> func fetchDataFromServer() throws -> Data { return Data() }
-> func fetchData() -> Data? {
if let data = try? fetchDataFromDisk() { return data }
if let data = try? fetchDataFromServer() { return data }
return nil
}
```
-->
### Disabling Error Propagation
Sometimes you know a throwing function or method
won't, in fact, throw an error at runtime.
On those occasions,
you can write `try!` before the expression to disable error propagation
and wrap the call in a runtime assertion that no error will be thrown.
If an error actually is thrown, you'll get a runtime error.
For example, the following code uses a `loadImage(atPath:)` function,
which loads the image resource at a given path
or throws an error if the image can't be loaded.
In this case, because the image is shipped with the application,
no error will be thrown at runtime,
so it's appropriate to disable error propagation.
```swift
let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
```
<!--
- test: `forceTryStatement`
```swifttest
>> struct Image {}
>> func loadImage(atPath path: String) throws -> Image {
>> return Image()
>> }
-> let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
```
-->
## Specifying the Error Type
All of the examples above use the most common kind of error handling,
where the errors that your code throws
can be values of any type that conforms to the `Error` protocol.
This approach matches the reality that
you don't know ahead of time every error that could happen
while the code is running,
especially when propagating errors thrown somewhere else.
It also reflects the fact that errors can change over time.
New versions of a library ---
including libraries that your dependencies use ---
can throw new errors,
and the rich complexity of real-world user configurations
can expose failure modes that weren't visible during development or testing.
The error handling code in the examples above
always includes a default case to handle errors
that don't have a specific `catch` clause.
Most Swift code doesn't specify the type for the errors it throws.
However,
you might limit code to throwing errors of only one specific type
in the following special cases:
- When running code on an embedded system
that doesn't support dynamic allocation of memory.
Throwing an instance of `any Error` or another boxed protocol type
requires allocating memory at runtime to store the error.
In contrast,
throwing an error of a specific type
lets Swift avoid heap allocation for errors.
- When the errors are an implementation detail of some unit of code,
like a library,
and aren't part of the interface to that code.
Because the errors come from only the library,
and not from other dependencies or the library's clients,
you can make an exhaustive list of all possible failures.
And because these errors are an implementation detail of the library,
they're always handled within that library.
- In code that only propagates errors described by generic parameters,
like a function that takes a closure argument
and propagates any errors from that closure.
For a comparison between propagating a specific error type
and using `rethrows`,
see <doc:Declarations#Rethrowing-Functions-and-Methods>.
For example,
consider code that summarizes ratings
and uses the following error type:
```swift
enum StatisticsError: Error {
case noRatings
case invalidRating(Int)
}
```
To specify that a function throws only `StatisticsError` values as its errors,
you write `throws(StatisticsError)` instead of only `throws`
when declaring the function.
This syntax is also called *typed throws*
because you write the error type after `throws` in the declaration.
For example,
the function below throws `StatisticsError` values as its errors.
```swift
func summarize(_ ratings: [Int]) throws(StatisticsError) {
guard !ratings.isEmpty else { throw .noRatings }
var counts = [1: 0, 2: 0, 3: 0]
for rating in ratings {
guard rating > 0 && rating <= 3 else { throw .invalidRating(rating) }
counts[rating]! += 1
}
print("*", counts[1]!, "-- **", counts[2]!, "-- ***", counts[3]!)
}
```
In the code above,
the `summarize(_:)` function summarizes a list of ratings
expressed on a scale of 1 to 3.
This function throws an instance of `StatisticsError` if the input isn't valid.
Both places in the code above that throw an error
omit the type of the error
because the function's error type is already defined.
You can use the short form, `throw .noRatings`,
instead of writing `throw StatisticsError.noRatings`
when throwing an error in a function like this.
When you write a specific error type at the start of the function,
Swift checks that you don't throw any other errors.
For example,
if you tried to use `VendingMachineError` from examples earlier in this chapter
in the `summarize(_:)` function above,
that code would produce an error at compile time.
You can call a function that uses typed throws
from within a regular throwing function:
```swift
func someThrowingFunction() throws {
let ratings = [1, 2, 3, 2, 2, 1]
try summarize(ratings)
}
```
The code above doesn't specify an error type for `someThrowingFunction()`,
so it throws `any Error`.
You could also write the error type explicitly as `throws(any Error)`;
the code below is equivalent to the code above:
```swift
func someThrowingFunction() throws(any Error) {
let ratings = [1, 2, 3, 2, 2, 1]
try summarize(ratings)
}
```
In this code,
`someThrowingFunction()` propagates any errors that `summarize(_:)` throws.
The errors from `summarize(_:)` are always `StatisticsError` values,
which is also a valid error for `someThrowingFunction()` to throw.
Just like you can write a function that never returns
with a return type of `Never`,
you can write a function that never throws with `throws(Never)`:
```swift
func nonThrowingFunction() throws(Never) {
// ...
}
```
This function can't throw because
it's impossible to create a value of type `Never` to throw.
In addition to specifying a function's error type,
you can also write a specific error type for a `do`-`catch` statement.
For example:
```swift
let ratings = []
do throws(StatisticsError) {
try summarize(ratings)
} catch {
switch error {
case .noRatings:
print("No ratings available")
case .invalidRating(let rating):
print("Invalid rating: \(rating)")
}
}
// Prints "No ratings available".
```
In this code,
writing `do throws(StatisticsError)` indicates that
the `do`-`catch` statement throws `StatisticsError` values as its errors.
Like other `do`-`catch` statements,
the `catch` clause can either handle every possible error
or propagate unhandled errors for some surrounding scope to handle.
This code handles all of the errors,
using a `switch` statement with one case for each enumeration value.
Like other `catch` clauses that don't have a pattern,
the clause matches any error
and binds the error to a local constant named `error`.
Because the `do`-`catch` statement throws `StatisticsError` values,
`error` is a value of type `StatisticsError`.
The `catch` clause above uses a `switch` statement
to match and handle each possible error.
If you tried to add a new case to `StatisticsError`
without updating the error-handling code,
Swift would give you an error
because the `switch` statement wouldn't be exhaustive anymore.
For a library that catches all of its own errors,
you could use this approach to ensure any new errors
get corresponding new code to handle them.
If a function or `do` block throws errors of only a single type,
Swift infers that this code is using typed throws.
Using this shorter syntax,
you could write the `do`-`catch` example above as follows:
```swift
let ratings = []
do {
try summarize(ratings)
} catch {
switch error {
case .noRatings:
print("No ratings available")
case .invalidRating(let rating):
print("Invalid rating: \(rating)")
}
}
// Prints "No ratings available".
```
Even though the `do`-`catch` block above
doesn't specify what type of error it throws,
Swift infers that it throws `StatisticsError`.
You can explicitly write `throws(any Error)`
to avoid letting Swift infer typed throws.
## Specifying Cleanup Actions
You use a `defer` statement to execute a set of statements
just before code execution leaves the current block of code.
This statement lets you do any necessary cleanup
that should be performed regardless
of *how* execution leaves the current block of code ---
whether it leaves because an error was thrown
or because of a statement such as `return` or `break`.
For example, you can use a `defer` statement
to ensure that file descriptors are closed
and manually allocated memory is freed.
A `defer` statement defers execution until the current scope is exited.
This statement consists of the `defer` keyword and the statements to be executed later.
The deferred statements may not contain any code
that would transfer control out of the statements,
such as a `break` or a `return` statement,
or by throwing an error.
Deferred actions are executed in the reverse of
the order that they're written in your source code.
That is, the code in the first `defer` statement executes last,
the code in the second `defer` statement executes second to last,
and so on.
The last `defer` statement in source code order executes first.
```swift
func processFile(filename: String) throws {
if exists(filename) {
let file = open(filename)
defer {
close(file)
}
while let line = try file.readline() {
// Work with the file.
}
// close(file) is called here, at the end of the scope.
}
}
```
<!--
- test: `defer`
```swifttest
>> func exists(_ file: String) -> Bool { return true }
>> struct File {
>> func readline() throws -> String? { return nil }
>> }
>> func open(_ file: String) -> File { return File() }
>> func close(_ fileHandle: File) {}
-> func processFile(filename: String) throws {
if exists(filename) {
let file = open(filename)
defer {
close(file)
}
while let line = try file.readline() {
// Work with the file.
>> print(line)
}
// close(file) is called here, at the end of the scope.
}
}
```
-->
The above example uses a `defer` statement
to ensure that the `open(_:)` function
has a corresponding call to `close(_:)`.
You can use a `defer` statement
even when no error handling code is involved.
For more information,
see <doc:ControlFlow#Deferred-Actions>.
<!--
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
-->

View File

@@ -0,0 +1,693 @@
# Extensions
Add functionality to an existing type.
*Extensions* add new functionality to an existing
class, structure, enumeration, or protocol type.
This includes the ability to extend types
for which you don't have access to the original source code
(known as *retroactive modeling*).
Extensions are similar to categories in Objective-C.
(Unlike Objective-C categories, Swift extensions don't have names.)
Extensions in Swift can:
- Add computed instance properties and computed type properties
- Define instance methods and type methods
- Provide new initializers
- Define subscripts
- Define and use new nested types
- Make an existing type conform to a protocol
In Swift,
you can even extend a protocol to provide implementations of its requirements
or add additional functionality that conforming types can take advantage of.
For more details, see <doc:Protocols#Protocol-Extensions>.
> Note: Extensions can add new functionality to a type,
> but they can't override existing functionality.
<!--
- test: `extensionsCannotOverrideExistingBehavior`
```swifttest
-> class C {
var x = 0
func foo() {}
}
-> extension C {
override var x: Int {
didSet {
print("new x is \(x)")
}
}
override func foo() {
print("called overridden foo")
}
}
!$ error: property does not override any property from its superclass
!! override var x: Int {
!! ~~~~~~~~ ^
!$ error: ambiguous use of 'x'
!! print("new x is \(x)")
!! ^
!$ note: found this candidate
!! var x = 0
!! ^
!$ note: found this candidate
!! override var x: Int {
!! ^
!$ error: invalid redeclaration of 'x'
!! override var x: Int {
!! ^
!$ note: 'x' previously declared here
!! var x = 0
!! ^
!$ error: method does not override any method from its superclass
!! override func foo() {
!! ~~~~~~~~ ^
!$ error: invalid redeclaration of 'foo()'
!! override func foo() {
!! ^
!$ note: 'foo()' previously declared here
!! func foo() {}
!! ^
```
-->
## Extension Syntax
Declare extensions with the `extension` keyword:
```swift
extension SomeType {
// new functionality to add to SomeType goes here
}
```
<!--
- test: `extensionSyntax`
```swifttest
>> struct SomeType {}
-> extension SomeType {
// new functionality to add to SomeType goes here
}
```
-->
An extension can extend an existing type to make it adopt one or more protocols.
To add protocol conformance,
you write the protocol names
the same way as you write them for a class or structure:
```swift
extension SomeType: SomeProtocol, AnotherProtocol {
// implementation of protocol requirements goes here
}
```
<!--
- test: `extensionSyntax`
```swifttest
>> protocol SomeProtocol {}
>> protocol AnotherProtocol {}
-> extension SomeType: SomeProtocol, AnotherProtocol {
// implementation of protocol requirements goes here
}
```
-->
Adding protocol conformance in this way is described in
<doc:Protocols#Adding-Protocol-Conformance-with-an-Extension>.
An extension can be used to extend an existing generic type,
as described in <doc:Generics#Extending-a-Generic-Type>.
You can also extend a generic type to conditionally add functionality,
as described in <doc:Generics#Extensions-with-a-Generic-Where-Clause>.
> Note: If you define an extension to add new functionality to an existing type,
> the new functionality will be available on all existing instances of that type,
> even if they were created before the extension was defined.
## Computed Properties
Extensions can add computed instance properties and computed type properties to existing types.
This example adds five computed instance properties to Swift's built-in `Double` type,
to provide basic support for working with distance units:
```swift
extension Double {
var km: Double { return self * 1_000.0 }
var m: Double { return self }
var cm: Double { return self / 100.0 }
var mm: Double { return self / 1_000.0 }
var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm
print("One inch is \(oneInch) meters")
// Prints "One inch is 0.0254 meters".
let threeFeet = 3.ft
print("Three feet is \(threeFeet) meters")
// Prints "Three feet is 0.914399970739201 meters".
```
<!--
- test: `extensionsComputedProperties`
```swifttest
-> extension Double {
var km: Double { return self * 1_000.0 }
var m: Double { return self }
var cm: Double { return self / 100.0 }
var mm: Double { return self / 1_000.0 }
var ft: Double { return self / 3.28084 }
}
-> let oneInch = 25.4.mm
-> print("One inch is \(oneInch) meters")
<- One inch is 0.0254 meters
-> let threeFeet = 3.ft
-> print("Three feet is \(threeFeet) meters")
<- Three feet is 0.914399970739201 meters
```
-->
These computed properties express that a `Double` value
should be considered as a certain unit of length.
Although they're implemented as computed properties,
the names of these properties can be appended to
a floating-point literal value with dot syntax,
as a way to use that literal value to perform distance conversions.
In this example, a `Double` value of `1.0` is considered to represent “one meter”.
This is why the `m` computed property returns `self` ---
the expression `1.m` is considered to calculate a `Double` value of `1.0`.
Other units require some conversion to be expressed as a value measured in meters.
One kilometer is the same as 1,000 meters,
so the `km` computed property multiplies the value by `1_000.00`
to convert into a number expressed in meters.
Similarly, there are 3.28084 feet in a meter,
and so the `ft` computed property divides the underlying `Double` value
by `3.28084`, to convert it from feet to meters.
These properties are read-only computed properties,
and so they're expressed without the `get` keyword, for brevity.
Their return value is of type `Double`,
and can be used within mathematical calculations wherever a `Double` is accepted:
```swift
let aMarathon = 42.km + 195.m
print("A marathon is \(aMarathon) meters long")
// Prints "A marathon is 42195.0 meters long".
```
<!--
- test: `extensionsComputedProperties`
```swifttest
-> let aMarathon = 42.km + 195.m
-> print("A marathon is \(aMarathon) meters long")
<- A marathon is 42195.0 meters long
```
-->
> Note: Extensions can add new computed properties, but they can't add stored properties,
> or add property observers to existing properties.
<!--
- test: `extensionsCannotAddStoredProperties`
```swifttest
-> class C {}
-> extension C { var x = 0 }
!$ error: extensions must not contain stored properties
!! extension C { var x = 0 }
!! ^
```
-->
<!--
TODO: change this example to something more advisable / less contentious.
-->
## Initializers
Extensions can add new initializers to existing types.
This enables you to extend other types to accept
your own custom types as initializer parameters,
or to provide additional initialization options
that were not included as part of the type's original implementation.
Extensions can add new convenience initializers to a class,
but they can't add new designated initializers or deinitializers to a class.
Designated initializers and deinitializers
must always be provided by the original class implementation.
If you use an extension to add an initializer to a value type that provides
default values for all of its stored properties
and doesn't define any custom initializers,
you can call the default initializer and memberwise initializer for that value type
from within your extension's initializer.
This wouldn't be the case if you had written the initializer
as part of the value type's original implementation,
as described in <doc:Initialization#Initializer-Delegation-for-Value-Types>.
If you use an extension to add an initializer to a structure
that was declared in another module,
the new initializer can't access `self` until it calls
an initializer from the defining module.
The example below defines a custom `Rect` structure to represent a geometric rectangle.
The example also defines two supporting structures called `Size` and `Point`,
both of which provide default values of `0.0` for all of their properties:
```swift
struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
}
```
<!--
- test: `extensionsInitializers`
```swifttest
-> struct Size {
var width = 0.0, height = 0.0
}
-> struct Point {
var x = 0.0, y = 0.0
}
-> struct Rect {
var origin = Point()
var size = Size()
}
```
-->
Because the `Rect` structure provides default values for all of its properties,
it receives a default initializer and a memberwise initializer automatically,
as described in <doc:Initialization#Default-Initializers>.
These initializers can be used to create new `Rect` instances:
```swift
let defaultRect = Rect()
let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
```
<!--
- test: `extensionsInitializers`
```swifttest
-> let defaultRect = Rect()
-> let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0),
size: Size(width: 5.0, height: 5.0))
```
-->
You can extend the `Rect` structure to provide an additional initializer
that takes a specific center point and size:
```swift
extension Rect {
init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init(origin: Point(x: originX, y: originY), size: size)
}
}
```
<!--
- test: `extensionsInitializers`
```swifttest
-> extension Rect {
init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init(origin: Point(x: originX, y: originY), size: size)
}
}
```
-->
This new initializer starts by calculating an appropriate origin point based on
the provided `center` point and `size` value.
The initializer then calls the structure's automatic memberwise initializer
`init(origin:size:)`, which stores the new origin and size values
in the appropriate properties:
```swift
let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
size: Size(width: 3.0, height: 3.0))
// centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)
```
<!--
- test: `extensionsInitializers`
```swifttest
-> let centerRect = Rect(center: Point(x: 4.0, y: 4.0),
size: Size(width: 3.0, height: 3.0))
/> centerRect's origin is (\(centerRect.origin.x), \(centerRect.origin.y)) and its size is (\(centerRect.size.width), \(centerRect.size.height))
</ centerRect's origin is (2.5, 2.5) and its size is (3.0, 3.0)
```
-->
> Note: If you provide a new initializer with an extension,
> you are still responsible for making sure that each instance is fully initialized
> once the initializer completes.
## Methods
Extensions can add new instance methods and type methods to existing types.
The following example adds a new instance method called `repetitions` to the `Int` type:
```swift
extension Int {
func repetitions(task: () -> Void) {
for _ in 0..<self {
task()
}
}
}
```
<!--
- test: `extensionsInstanceMethods`
```swifttest
-> extension Int {
func repetitions(task: () -> Void) {
for _ in 0..<self {
task()
}
}
}
```
-->
The `repetitions(task:)` method takes a single argument of type `() -> Void`,
which indicates a function that has no parameters and doesn't return a value.
After defining this extension,
you can call the `repetitions(task:)` method on any integer
to perform a task that many number of times:
```swift
3.repetitions {
print("Hello!")
}
// Hello!
// Hello!
// Hello!
```
<!--
- test: `extensionsInstanceMethods`
```swifttest
-> 3.repetitions {
print("Hello!")
}
</ Hello!
</ Hello!
</ Hello!
```
-->
### Mutating Instance Methods
Instance methods added with an extension can also modify (or *mutate*) the instance itself.
Structure and enumeration methods that modify `self` or its properties
must mark the instance method as `mutating`,
just like mutating methods from an original implementation.
The example below adds a new mutating method called `square` to Swift's `Int` type,
which squares the original value:
```swift
extension Int {
mutating func square() {
self = self * self
}
}
var someInt = 3
someInt.square()
// someInt is now 9
```
<!--
- test: `extensionsInstanceMethods`
```swifttest
-> extension Int {
mutating func square() {
self = self * self
}
}
-> var someInt = 3
-> someInt.square()
/> someInt is now \(someInt)
</ someInt is now 9
```
-->
## Subscripts
Extensions can add new subscripts to an existing type.
This example adds an integer subscript to Swift's built-in `Int` type.
This subscript `[n]` returns the decimal digit `n` places in
from the right of the number:
- `123456789[0]` returns `9`
- `123456789[1]` returns `8`
…and so on:
```swift
extension Int {
subscript(digitIndex: Int) -> Int {
var decimalBase = 1
for _ in 0..<digitIndex {
decimalBase *= 10
}
return (self / decimalBase) % 10
}
}
746381295[0]
// returns 5
746381295[1]
// returns 9
746381295[2]
// returns 2
746381295[8]
// returns 7
```
<!--
- test: `extensionsSubscripts`
```swifttest
-> extension Int {
subscript(digitIndex: Int) -> Int {
var decimalBase = 1
for _ in 0..<digitIndex {
decimalBase *= 10
}
return (self / decimalBase) % 10
}
}
>> let r0 =
-> 746381295[0]
/> returns \(r0)
</ returns 5
>> let r1 =
-> 746381295[1]
/> returns \(r1)
</ returns 9
>> let r2 =
-> 746381295[2]
/> returns \(r2)
</ returns 2
>> let r3 =
-> 746381295[8]
/> returns \(r3)
</ returns 7
```
-->
<!--
Rewrite the above to avoid bare expressions.
Tracking bug is <rdar://problem/35301593>
-->
<!--
TODO: Replace the for loop above with an exponent,
if/when integer exponents land in the stdlib.
Darwin's pow() function is only for floating point.
-->
If the `Int` value doesn't have enough digits for the requested index,
the subscript implementation returns `0`,
as if the number had been padded with zeros to the left:
```swift
746381295[9]
// returns 0, as if you had requested:
0746381295[9]
```
<!--
- test: `extensionsSubscripts`
```swifttest
>> let r4 =
-> 746381295[9]
/> returns \(r4), as if you had requested:
</ returns 0, as if you had requested:
>> let r5 =
-> 0746381295[9]
```
-->
<!--
TODO: provide an explanation of this example
-->
<!--
Rewrite the above to avoid bare expressions.
Tracking bug is <rdar://problem/35301593>
-->
## Nested Types
Extensions can add new nested types to existing classes, structures, and enumerations:
```swift
extension Int {
enum Kind {
case negative, zero, positive
}
var kind: Kind {
switch self {
case 0:
return .zero
case let x where x > 0:
return .positive
default:
return .negative
}
}
}
```
<!--
- test: `extensionsNestedTypes`
```swifttest
-> extension Int {
enum Kind {
case negative, zero, positive
}
var kind: Kind {
switch self {
case 0:
return .zero
case let x where x > 0:
return .positive
default:
return .negative
}
}
}
```
-->
This example adds a new nested enumeration to `Int`.
This enumeration, called `Kind`,
expresses the kind of number that a particular integer represents.
Specifically, it expresses whether the number is
negative, zero, or positive.
This example also adds a new computed instance property to `Int`,
called `kind`,
which returns the appropriate `Kind` enumeration case for that integer.
The nested enumeration can now be used with any `Int` value:
```swift
func printIntegerKinds(_ numbers: [Int]) {
for number in numbers {
switch number.kind {
case .negative:
print("- ", terminator: "")
case .zero:
print("0 ", terminator: "")
case .positive:
print("+ ", terminator: "")
}
}
print("")
}
printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
// Prints "+ + - 0 - 0 + ".
```
<!--
- test: `extensionsNestedTypes`
```swifttest
-> func printIntegerKinds(_ numbers: [Int]) {
for number in numbers {
switch number.kind {
case .negative:
print("- ", terminator: "")
case .zero:
print("0 ", terminator: "")
case .positive:
print("+ ", terminator: "")
}
}
print("")
}
-> printIntegerKinds([3, 19, -27, 0, -6, 0, 7])
<< + + - 0 - 0 +
// Prints "+ + - 0 - 0 + ".
```
-->
<!--
Workaround for rdar://26016325
-->
This function, `printIntegerKinds(_:)`,
takes an input array of `Int` values and iterates over those values in turn.
For each integer in the array,
the function considers the `kind` computed property for that integer,
and prints an appropriate description.
> Note: `number.kind` is already known to be of type `Int.Kind`.
> Because of this, all of the `Int.Kind` case values
> can be written in shorthand form inside the `switch` statement,
> such as `.negative` rather than `Int.Kind.negative`.
<!--
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
-->

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,617 @@
# Inheritance
Subclass to add or override functionality.
A class can *inherit* methods, properties, and other characteristics
from another class.
When one class inherits from another,
the inheriting class is known as a *subclass*,
and the class it inherits from is known as its *superclass*.
Inheritance is a fundamental behavior that differentiates classes
from other types in Swift.
Classes in Swift can call and access
methods, properties, and subscripts belonging to their superclass
and can provide their own overriding versions of those methods, properties, and subscripts
to refine or modify their behavior.
Swift helps to ensure your overrides are correct
by checking that the override definition has a matching superclass definition.
Classes can also add property observers to inherited properties
in order to be notified when the value of a property changes.
Property observers can be added to any property,
regardless of whether it was originally defined as a stored or computed property.
## Defining a Base Class
Any class that doesn't inherit from another class is known as a *base class*.
> Note: Swift classes don't inherit from a universal base class.
> Classes you define without specifying a superclass
> automatically become base classes for you to build upon.
The example below defines a base class called `Vehicle`.
This base class defines a stored property called `currentSpeed`,
with a default value of `0.0` (inferring a property type of `Double`).
The `currentSpeed` property's value is used by
a read-only computed `String` property called `description`
to create a description of the vehicle.
The `Vehicle` base class also defines a method called `makeNoise`.
This method doesn't actually do anything for a base `Vehicle` instance,
but will be customized by subclasses of `Vehicle` later on:
```swift
class Vehicle {
var currentSpeed = 0.0
var description: String {
return "traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
// do nothing - an arbitrary vehicle doesn't necessarily make a noise
}
}
```
<!--
- test: `inheritance`
```swifttest
-> class Vehicle {
var currentSpeed = 0.0
var description: String {
return "traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
// do nothing - an arbitrary vehicle doesn't necessarily make a noise
}
}
```
-->
You create a new instance of `Vehicle` with *initializer syntax*,
which is written as a type name followed by empty parentheses:
```swift
let someVehicle = Vehicle()
```
<!--
- test: `inheritance`
```swifttest
-> let someVehicle = Vehicle()
```
-->
Having created a new `Vehicle` instance,
you can access its `description` property to print
a human-readable description of the vehicle's current speed:
```swift
print("Vehicle: \(someVehicle.description)")
// Vehicle: traveling at 0.0 miles per hour
```
<!--
- test: `inheritance`
```swifttest
-> print("Vehicle: \(someVehicle.description)")
</ Vehicle: traveling at 0.0 miles per hour
```
-->
The `Vehicle` class defines common characteristics for an arbitrary vehicle,
but isn't much use in itself.
To make it more useful,
you need to refine it to describe more specific kinds of vehicles.
## Subclassing
*Subclassing* is the act of basing a new class on an existing class.
The subclass inherits characteristics from the existing class, which you can then refine.
You can also add new characteristics to the subclass.
To indicate that a subclass has a superclass,
write the subclass name before the superclass name,
separated by a colon:
```swift
class SomeSubclass: SomeSuperclass {
// subclass definition goes here
}
```
<!--
- test: `protocolSyntax`
```swifttest
>> class SomeSuperclass {}
-> class SomeSubclass: SomeSuperclass {
// subclass definition goes here
}
```
-->
The following example defines a subclass called `Bicycle`,
with a superclass of `Vehicle`:
```swift
class Bicycle: Vehicle {
var hasBasket = false
}
```
<!--
- test: `inheritance`
```swifttest
-> class Bicycle: Vehicle {
var hasBasket = false
}
```
-->
The new `Bicycle` class automatically gains all of the characteristics of `Vehicle`,
such as its `currentSpeed` and `description` properties and its `makeNoise()` method.
In addition to the characteristics it inherits,
the `Bicycle` class defines a new stored property,
`hasBasket`, with a default value of `false`
(inferring a type of `Bool` for the property).
By default, any new `Bicycle` instance you create will not have a basket.
You can set the `hasBasket` property to `true` for a particular `Bicycle` instance
after that instance is created:
```swift
let bicycle = Bicycle()
bicycle.hasBasket = true
```
<!--
- test: `inheritance`
```swifttest
-> let bicycle = Bicycle()
-> bicycle.hasBasket = true
```
-->
You can also modify the inherited `currentSpeed` property of a `Bicycle` instance,
and query the instance's inherited `description` property:
```swift
bicycle.currentSpeed = 15.0
print("Bicycle: \(bicycle.description)")
// Bicycle: traveling at 15.0 miles per hour
```
<!--
- test: `inheritance`
```swifttest
-> bicycle.currentSpeed = 15.0
-> print("Bicycle: \(bicycle.description)")
</ Bicycle: traveling at 15.0 miles per hour
```
-->
Subclasses can themselves be subclassed.
The next example creates a subclass of `Bicycle` for a two-seater bicycle
known as a “tandem”:
```swift
class Tandem: Bicycle {
var currentNumberOfPassengers = 0
}
```
<!--
- test: `inheritance`
```swifttest
-> class Tandem: Bicycle {
var currentNumberOfPassengers = 0
}
```
-->
`Tandem` inherits all of the properties and methods from `Bicycle`,
which in turn inherits all of the properties and methods from `Vehicle`.
The `Tandem` subclass also adds a new stored property called `currentNumberOfPassengers`,
with a default value of `0`.
If you create an instance of `Tandem`,
you can work with any of its new and inherited properties,
and query the read-only `description` property it inherits from `Vehicle`:
```swift
let tandem = Tandem()
tandem.hasBasket = true
tandem.currentNumberOfPassengers = 2
tandem.currentSpeed = 22.0
print("Tandem: \(tandem.description)")
// Tandem: traveling at 22.0 miles per hour
```
<!--
- test: `inheritance`
```swifttest
-> let tandem = Tandem()
-> tandem.hasBasket = true
-> tandem.currentNumberOfPassengers = 2
-> tandem.currentSpeed = 22.0
-> print("Tandem: \(tandem.description)")
</ Tandem: traveling at 22.0 miles per hour
```
-->
## Overriding
A subclass can provide its own custom implementation of
an instance method, type method, instance property, type property, or subscript
that it would otherwise inherit from a superclass.
This is known as *overriding*.
To override a characteristic that would otherwise be inherited,
you prefix your overriding definition with the `override` keyword.
Doing so clarifies that you intend to provide an override
and haven't provided a matching definition by mistake.
Overriding by accident can cause unexpected behavior,
and any overrides without the `override` keyword are
diagnosed as an error when your code is compiled.
The `override` keyword also prompts the Swift compiler
to check that your overriding class's superclass (or one of its parents)
has a declaration that matches the one you provided for the override.
This check ensures that your overriding definition is correct.
### Accessing Superclass Methods, Properties, and Subscripts
When you provide a method, property, or subscript override for a subclass,
it's sometimes useful to use the existing superclass implementation
as part of your override.
For example, you can refine the behavior of that existing implementation,
or store a modified value in an existing inherited variable.
Where this is appropriate,
you access the superclass version of a method, property, or subscript
by using the `super` prefix:
- An overridden method named `someMethod()` can call the superclass version of `someMethod()`
by calling `super.someMethod()` within the overriding method implementation.
- An overridden property called `someProperty` can access the superclass version of `someProperty`
as `super.someProperty` within the overriding getter or setter implementation.
- An overridden subscript for `someIndex` can access the superclass version of the same subscript
as `super[someIndex]` from within the overriding subscript implementation.
### Overriding Methods
You can override an inherited instance or type method
to provide a tailored or alternative implementation of the method within your subclass.
The following example defines a new subclass of `Vehicle` called `Train`,
which overrides the `makeNoise()` method that `Train` inherits from `Vehicle`:
```swift
class Train: Vehicle {
override func makeNoise() {
print("Choo Choo")
}
}
```
<!--
- test: `inheritance`
```swifttest
-> class Train: Vehicle {
override func makeNoise() {
print("Choo Choo")
}
}
```
-->
If you create a new instance of `Train` and call its `makeNoise()` method,
you can see that the `Train` subclass version of the method is called:
```swift
let train = Train()
train.makeNoise()
// Prints "Choo Choo".
```
<!--
- test: `inheritance`
```swifttest
-> let train = Train()
-> train.makeNoise()
<- Choo Choo
```
-->
### Overriding Properties
You can override an inherited instance or type property
to provide your own custom getter and setter for that property,
or to add property observers to enable the overriding property
to observe when the underlying property value changes.
#### Overriding Property Getters and Setters
You can provide a custom getter (and setter, if appropriate)
to override *any* inherited property,
regardless of whether the inherited property is implemented as
a stored or computed property at source.
The stored or computed nature of an inherited property isn't known by a subclass ---
it only knows that the inherited property has a certain name and type.
You must always state both the name and the type of the property you are overriding,
to enable the compiler to check that your override matches
a superclass property with the same name and type.
You can present an inherited read-only property as a read-write property
by providing both a getter and a setter in your subclass property override.
You can't, however, present an inherited read-write property as a read-only property.
> Note: If you provide a setter as part of a property override,
> you must also provide a getter for that override.
> If you don't want to modify the inherited property's value within the overriding getter,
> you can simply pass through the inherited value
> by returning `super.someProperty` from the getter,
> where `someProperty` is the name of the property you are overriding.
The following example defines a new class called `Car`,
which is a subclass of `Vehicle`.
The `Car` class introduces a new stored property called `gear`,
with a default integer value of `1`.
The `Car` class also overrides the `description` property it inherits from `Vehicle`,
to provide a custom description that includes the current gear:
```swift
class Car: Vehicle {
var gear = 1
override var description: String {
return super.description + " in gear \(gear)"
}
}
```
<!--
- test: `inheritance`
```swifttest
-> class Car: Vehicle {
var gear = 1
override var description: String {
return super.description + " in gear \(gear)"
}
}
```
-->
The override of the `description` property starts by calling `super.description`,
which returns the `Vehicle` class's `description` property.
The `Car` class's version of `description` then adds some extra text onto
the end of this description to provide information about the current gear.
If you create an instance of the `Car` class
and set its `gear` and `currentSpeed` properties,
you can see that its `description` property returns
the tailored description defined within the `Car` class:
```swift
let car = Car()
car.currentSpeed = 25.0
car.gear = 3
print("Car: \(car.description)")
// Car: traveling at 25.0 miles per hour in gear 3
```
<!--
- test: `inheritance`
```swifttest
-> let car = Car()
-> car.currentSpeed = 25.0
-> car.gear = 3
-> print("Car: \(car.description)")
</ Car: traveling at 25.0 miles per hour in gear 3
```
-->
#### Overriding Property Observers
You can use property overriding to add property observers to an inherited property.
This enables you to be notified when the value of an inherited property changes,
regardless of how that property was originally implemented.
For more information on property observers, see <doc:Properties#Property-Observers>.
> Note: You can't add property observers to
> inherited constant stored properties or inherited read-only computed properties.
> The value of these properties can't be set,
> and so it isn't appropriate to provide a `willSet` or `didSet` implementation
> as part of an override.
>
> Note also that you can't provide both
> an overriding setter and an overriding property observer for the same property.
> If you want to observe changes to a property's value,
> and you are already providing a custom setter for that property,
> you can simply observe any value changes from within the custom setter.
The following example defines a new class called `AutomaticCar`,
which is a subclass of `Car`.
The `AutomaticCar` class represents a car with an automatic gearbox,
which automatically selects an appropriate gear to use based on the current speed:
```swift
class AutomaticCar: Car {
override var currentSpeed: Double {
didSet {
gear = Int(currentSpeed / 10.0) + 1
}
}
}
```
<!--
- test: `inheritance`
```swifttest
-> class AutomaticCar: Car {
override var currentSpeed: Double {
didSet {
gear = Int(currentSpeed / 10.0) + 1
}
}
}
```
-->
Whenever you set the `currentSpeed` property of an `AutomaticCar` instance,
the property's `didSet` observer sets the instance's `gear` property to
an appropriate choice of gear for the new speed.
Specifically, the property observer chooses a gear that's
the new `currentSpeed` value divided by `10`,
rounded down to the nearest integer, plus `1`.
A speed of `35.0` produces a gear of `4`:
```swift
let automatic = AutomaticCar()
automatic.currentSpeed = 35.0
print("AutomaticCar: \(automatic.description)")
// AutomaticCar: traveling at 35.0 miles per hour in gear 4
```
<!--
- test: `inheritance`
```swifttest
-> let automatic = AutomaticCar()
-> automatic.currentSpeed = 35.0
-> print("AutomaticCar: \(automatic.description)")
</ AutomaticCar: traveling at 35.0 miles per hour in gear 4
```
-->
## Preventing Overrides
You can prevent a method, property, or subscript from being overridden
by marking it as *final*.
Do this by writing the `final` modifier before
the method, property, or subscript's introducer keyword
(such as `final var`, `final func`, `final class func`, and `final subscript`).
Any attempt to override a final method, property, or subscript in a subclass
is reported as a compile-time error.
Methods, properties, or subscripts that you add to a class in an extension
can also be marked as final within the extension's definition.
For more information, see <doc:Extensions>.
<!--
- test: `finalPreventsOverriding`
```swifttest
-> class C {
final var someVar = 0
final func someFunction() {
print("In someFunction")
}
}
-> class D : C {
override var someVar: Int {
get { return 1 }
set {}
}
override func someFunction() {
print("In overridden someFunction")
}
}
!$ error: property overrides a 'final' property
!! override var someVar: Int {
!! ^
!$ note: overridden declaration is here
!! final var someVar = 0
!! ^
!$ error: instance method overrides a 'final' instance method
!! override func someFunction() {
!! ^
!$ note: overridden declaration is here
!! final func someFunction() {
!! ^
```
-->
You can mark an entire class as final by writing the `final` modifier
before the `class` keyword in its class definition (`final class`).
Any attempt to subclass a final class is reported as a compile-time error.
<!--
- test: `finalClassPreventsOverriding`
```swifttest
-> final class C {
var someVar = 0
func someFunction() {
print("In someFunction")
}
}
-> class D : C {
override var someVar: Int {
get { return 1 }
set {}
}
override func someFunction() {
print("In overridden someFunction")
}
}
!$ error: property overrides a 'final' property
!! override var someVar: Int {
!! ^
!$ note: overridden declaration is here
!! var someVar = 0
!! ^
!$ error: instance method overrides a 'final' instance method
!! override func someFunction() {
!! ^
!$ note: overridden declaration is here
!! func someFunction() {
!! ^
!$ error: inheritance from a final class 'C'
!! class D : C {
!! ^
```
-->
<!--
TODO: I should probably provide an example here.
-->
<!--
TODO: provide more information about function signatures,
and what does / doesn't make them unique.
For example, the parameter names don't have to match
in order for a function to override a similar signature in its parent.
(This is true for both of the function declaration syntaxes.)
-->
<!--
TODO: Mention that you can return more-specific types, and take less-specific types,
when overriding methods that use optionals / unchecked optionals.
TODO: Overriding Type Methods
-->
<!--
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
-->

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,774 @@
# Macros
Use macros to generate code at compile time.
Macros transform your source code when you compile it,
letting you avoid writing repetitive code by hand.
During compilation,
Swift expands any macros in your code before building your code as usual.
![A diagram showing an overview of macro expansion. On the left, a stylized representation of Swift code. On the right, the same code with several lines added by the macro.](macro-expansion)
Expanding a macro is always an additive operation:
Macros add new code,
but they never delete or modify existing code.
Both the input to a macro and the output of macro expansion
are checked to ensure they're syntactically valid Swift code.
Likewise, the values you pass to a macro
and the values in code generated by a macro
are checked to ensure they have the correct types.
In addition,
if the macro's implementation encounters an error when expanding that macro,
the compiler treats this as a compilation error.
These guarantees make it easier to reason about code that uses macros,
and they make it easier to identify issues
like using a macro incorrectly
or a macro implementation that has a bug.
Swift has two kinds of macros:
- *Freestanding macros* appear on their own,
without being attached to a declaration.
- *Attached macros* modify the declaration that they're attached to.
You call attached and freestanding macros slightly differently,
but they both follow the same model for macro expansion,
and you implement them both using the same approach.
The following sections describe both kinds of macros in more detail.
## Freestanding Macros
To call a freestanding macro,
you write a number sign (`#`) before its name,
and you write any arguments to the macro in parentheses after its name.
For example:
```swift
func myFunction() {
print("Currently running \(#function)")
#warning("Something's wrong")
}
```
In the first line,
`#function` calls the [`function()`][] macro from the Swift standard library.
When you compile this code,
Swift calls that macro's implementation,
which replaces `#function` with the name of the current function.
When you run this code and call `myFunction()`,
it prints "Currently running myFunction()".
In the second line,
`#warning` calls the [`warning(_:)`][] macro from the Swift standard library
to produce a custom compile-time warning.
[`function()`]: https://developer.apple.com/documentation/swift/function()
[`warning(_:)`]: https://developer.apple.com/documentation/swift/warning(_:)
Freestanding macros can produce a value, like `#function` does,
or they can perform an action at compile time, like `#warning` does.
<!-- SE-0397: or they can generate new declarations. -->
## Attached Macros
To call an attached macro,
you write an at sign (`@`) before its name,
and you write any arguments to the macro in parentheses after its name.
Attached macros modify the declaration that they're attached to.
They add code to that declaration,
like defining a new method or adding conformance to a protocol.
For example, consider the following code
that doesn't use macros:
```swift
struct SundaeToppings: OptionSet {
let rawValue: Int
static let nuts = SundaeToppings(rawValue: 1 << 0)
static let cherry = SundaeToppings(rawValue: 1 << 1)
static let fudge = SundaeToppings(rawValue: 1 << 2)
}
```
In this code,
each of the options in the `SundaeToppings` option set
includes a call to the initializer,
which is repetitive and manual.
It would be easy to make a mistake when adding a new option,
like typing the wrong number at the end of the line.
Here's a version of this code that uses a macro instead:
```swift
@OptionSet<Int>
struct SundaeToppings {
private enum Options: Int {
case nuts
case cherry
case fudge
}
}
```
This version of `SundaeToppings` calls an `@OptionSet` macro.
The macro reads the list of cases in the private enumeration,
generates the list of constants for each option,
and adds a conformance to the [`OptionSet`][] protocol.
[`OptionSet`]: https://developer.apple.com/documentation/swift/optionset
<!--
When the @OptionSet macro comes back, change both links back:
[`@OptionSet`]: https://developer.apple.com/documentation/swift/optionset-swift.macro
[`OptionSet`]: https://developer.apple.com/documentation/swift/optionset-swift.protocol
-->
For comparison,
here's what the expanded version of the `@OptionSet` macro looks like.
You don't write this code,
and you would see it only if you specifically asked Swift
to show the macro's expansion.
```swift
struct SundaeToppings {
private enum Options: Int {
case nuts
case cherry
case fudge
}
typealias RawValue = Int
var rawValue: RawValue
init() { self.rawValue = 0 }
init(rawValue: RawValue) { self.rawValue = rawValue }
static let nuts: Self = Self(rawValue: 1 << Options.nuts.rawValue)
static let cherry: Self = Self(rawValue: 1 << Options.cherry.rawValue)
static let fudge: Self = Self(rawValue: 1 << Options.fudge.rawValue)
}
extension SundaeToppings: OptionSet { }
```
All of the code after the private enumeration
comes from the `@OptionSet` macro.
The version of `SundaeToppings`
that uses a macro to generate all of the static variables
is easier to read and easier to maintain
than the manually coded version, earlier.
## Macro Declarations
In most Swift code,
when you implement a symbol, like a function or type,
there's no separate declaration.
However, for macros, the declaration and implementation are separate.
A macro's declaration contains its name,
the parameters it takes,
where it can be used,
and what kind of code it generates.
A macro's implementation contains the code
that expands the macro by generating Swift code.
You introduce a macro declaration with the `macro` keyword.
For example,
here's part of the declaration for
the `@OptionSet` macro used in the previous example:
```swift
public macro OptionSet<RawType>() =
#externalMacro(module: "SwiftMacros", type: "OptionSetMacro")
```
The first line
specifies the macro's name and its arguments ---
the name is `OptionSet`, and it doesn't take any arguments.
The second line
uses the [`externalMacro(module:type:)`][] macro from the Swift standard library
to tell Swift where the macro's implementation is located.
In this case,
the `SwiftMacros` module
contains a type named `OptionSetMacro`,
which implements the `@OptionSet` macro.
[`externalMacro(module:type:)`]: https://developer.apple.com/documentation/swift/externalmacro(module:type:)
Because `OptionSet` is an attached macro,
its name uses upper camel case,
like the names for structures and classes.
Freestanding macros have lower camel case names,
like the names for variables and functions.
> Note:
> Macros are always declared as `public`.
> Because the code that declares a macro
> is in a different module from code that uses that macro,
> there isn't anywhere you could apply a nonpublic macro.
A macro declaration defines the macro's *roles* ---
the places in source code where that macro can be called,
and the kinds of code the macro can generate.
Every macro has one or more roles,
which you write as part of the attributes
at the beginning of the macro declaration.
Here's a bit more of the declaration for `@OptionSet`,
including the attributes for its roles:
```swift
@attached(member)
@attached(extension, conformances: OptionSet)
public macro OptionSet<RawType>() =
#externalMacro(module: "SwiftMacros", type: "OptionSetMacro")
```
The `@attached` attribute appears twice in this declaration,
once for each macro role.
The first use, `@attached(member)`, indicates that the macro
adds new members to the type you apply it to.
The `@OptionSet` macro adds an `init(rawValue:)` initializer
that's required by the `OptionSet` protocol,
as well as some additional members.
The second use, `@attached(extension, conformances: OptionSet)`,
tells you that `@OptionSet`
adds conformance to the `OptionSet` protocol.
The `@OptionSet` macro
extends the type that you apply the macro to,
to add conformance to the `OptionSet` protocol.
For a freestanding macro,
you write the `@freestanding` attribute to specify its role:
```swift
@freestanding(expression)
public macro line<T: ExpressibleByIntegerLiteral>() -> T =
/* ... location of the macro implementation... */
```
<!--
Elided the implementation of #line above
because it's a compiler built-in:
public macro line<T: ExpressibleByIntegerLiteral>() -> T = Builtin.LineMacro
-->
The `#line` macro above has the `expression` role.
An expression macro produces a value,
or performs a compile-time action like generating a warning.
In addition to the macro's role,
a macro's declaration provides information about
the names of the symbols that the macro generates.
When a macro declaration provides a list of names,
it's guaranteed to produce only declarations that use those names,
which helps you understand and debug the generated code.
Here's the full declaration of `@OptionSet`:
```swift
@attached(member, names: named(RawValue), named(rawValue),
named(`init`), arbitrary)
@attached(extension, conformances: OptionSet)
public macro OptionSet<RawType>() =
#externalMacro(module: "SwiftMacros", type: "OptionSetMacro")
```
In the declaration above,
the `@attached(member)` macro includes arguments after the `names:` label
for each of the symbols that the `@OptionSet` macro generates.
The macro adds declarations for symbols named
`RawValue`, `rawValue`, and `init` ---
because those names are known ahead of time,
the macro declaration lists them explicitly.
The macro declaration also includes `arbitrary` after the list of names,
allowing the macro to generate declarations
whose names aren't known until you use the macro.
For example,
when the `@OptionSet` macro is applied to the `SundaeToppings` above,
it generates type properties that correspond to the enumeration cases,
`nuts`, `cherry`, and `fudge`.
For more information,
including a full list of macro roles,
see <doc:Attributes#attached> and <doc:Attributes#freestanding>
in <doc:Attributes>.
## Macro Expansion
When building Swift code that uses macros,
the compiler calls the macros' implementation to expand them.
![Diagram showing the four steps of expanding macros. The input is Swift source code. This becomes a tree, representing the code's structure. The macro implementation adds branches to the tree. The result is Swift source with additional code.](macro-expansion-full)
Specifically, Swift expands macros in the following way:
1. The compiler reads the code,
creating an in-memory representation of the syntax.
1. The compiler sends part of the in-memory representation
to the macro implementation,
which expands the macro.
1. The compiler replaces the macro call with its expanded form.
1. The compiler continues with compilation,
using the expanded source code.
To go through the specific steps, consider the following:
```swift
let magicNumber = #fourCharacterCode("ABCD")
```
The `#fourCharacterCode` macro takes a string that's four characters long
and returns an unsigned 32-bit integer
that corresponds to the ASCII values in the string joined together.
Some file formats use integers like this to identify data
because they're compact but still readable in a debugger.
The <doc:Macros#Implementing-a-Macro> section below
shows how to implement this macro.
To expand the macros in the code above,
the compiler reads the Swift file
and creates an in-memory representation of that code
known as an *abstract syntax tree*, or AST.
The AST makes the code's structure explicit,
which makes it easier to write code that interacts with that structure ---
like a compiler or a macro implementation.
Here's a representation of the AST for the code above,
slightly simplified by omitting some extra detail:
![A tree diagram, with a constant as the root element. The constant has a name, magic number, and a value. The constant's value is a macro call. The macro call has a name, fourCharacterCode, and arguments. The argument is a string literal, ABCD.](macro-ast-original)
The diagram above shows how the structure of this code
is represented in memory.
Each element in the AST
corresponds to a part of the source code.
The "Constant declaration" AST element
has two child elements under it,
which represent the two parts of a constant declaration:
its name and its value.
The "Macro call" element has child elements
that represent the macro's name
and the list of arguments being passed to the macro.
As part of constructing this AST,
the compiler checks that the source code is valid Swift.
For example, `#fourCharacterCode` takes a single argument,
which must be a string.
If you tried to pass an integer argument,
or forgot the quotation mark (`"`) at the end of the string literal,
you'd get an error at this point in the process.
The compiler finds the places in the code where you call a macro,
and loads the external binary that implements those macros.
For each macro call,
the compiler passes part of the AST to that macro's implementation.
Here's a representation of that partial AST:
![A tree diagram, with a macro call as the root element. The macro call has a name, fourCharacterCode, and arguments. The argument is a string literal, ABCD.](macro-ast-input)
The implementation of the `#fourCharacterCode` macro
reads this partial AST as its input when expanding the macro.
A macro's implementation
operates only on the partial AST that it receives as its input,
meaning a macro always expands the same way
regardless of what code comes before and after it.
This limitation helps make macro expansion easier to understand,
and helps your code build faster
because Swift can avoid expanding macros that haven't changed.
<!-- TODO TR: Confirm -->
Swift helps macro authors avoid accidentally reading other input
by restricting the code that implements macros:
- The AST passed to a macro implementation
contains only the AST elements that represent the macro,
not any of the code that comes before or after it.
- The macro implementation runs in a sandboxed environment
that prevents it from accessing the file system or the network.
In addition to these safeguards,
the macro's author is responsible for not reading or modifying anything
outside of the macro's inputs.
For example, a macro's expansion must not depend on the current time of day.
The implementation of `#fourCharacterCode`
generates a new AST containing the expanded code.
Here's what that code returns to the compiler:
![A tree diagram with the integer literal 1145258561 of type UInt32.](macro-ast-output)
When the compiler receives this expansion,
it replaces the AST element that contains the macro call
with the element that contains the macro's expansion.
After macro expansion,
the compiler checks again to ensure
the program is still syntactically valid Swift
and all the types are correct.
That produces a final AST that can be compiled as usual:
![A tree diagram, with a constant as the root element. The constant has a name, magic number, and a value. The constant's value is the integer literal 1145258561 of type UInt32.](macro-ast-result)
This AST corresponds to Swift code like this:
```swift
let magicNumber = 1145258561 as UInt32
```
In this example, the input source code has only one macro,
but a real program could have several instances of the same macro
and several calls to different macros.
The compiler expands macros one at a time.
If one macro appears inside another,
the outer macro is expanded first ---
this lets the outer macro modify the inner macro before it's expanded.
<!-- OUTLINE
- TR: Is there any limit to nesting?
TR: Is it valid to nest like this -- if so, anything to note about it?
```
let something = #someMacro {
struct A { }
@someMacro struct B { }
}
```
- Macro recursion is limited.
One macro can call another,
but a given macro can't directly or indirectly call itself.
The result of macro expansion can include other macros,
but it can't include a macro that uses this macro in its expansion
or declare a new macro.
(TR: Likely need to iterate on details here)
-->
## Implementing a Macro
To implement a macro, you make two components:
A type that performs the macro expansion,
and a library that declares the macro to expose it as API.
These parts are built separately from code that uses the macro,
even if you're developing the macro and its clients together,
because the macro implementation runs
as part of building the macro's clients.
To create a new macro using Swift Package Manager,
run `swift package init --type macro` ---
this creates several files,
including a template for a macro implementation and declaration.
To add macros to an existing project,
edit the beginning of your `Package.swift` file as follows:
- Set a Swift tools version of 5.9 or later in the `swift-tools-version` comment.
- Import the `CompilerPluginSupport` module.
- Include macOS 10.15 as a minimum deployment target in the `platforms` list.
The code below shows the beginning of an example `Package.swift` file.
```swift
// swift-tools-version: 5.9
import PackageDescription
import CompilerPluginSupport
let package = Package(
name: "MyPackage",
platforms: [ .iOS(.v17), .macOS(.v13)],
// ...
)
```
Next, add a target for the macro implementation
and a target for the macro library
to your existing `Package.swift` file.
For example,
you can add something like the following,
changing the names to match your project:
```swift
targets: [
// Macro implementation that performs the source transformations.
.macro(
name: "MyProjectMacros",
dependencies: [
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax")
]
),
// Library that exposes a macro as part of its API.
.target(name: "MyProject", dependencies: ["MyProjectMacros"]),
]
```
The code above defines two targets:
`MyProjectMacros` contains the implementation of the macros,
and `MyProject` makes those macros available.
The implementation of a macro
uses the [SwiftSyntax][] module to interact with Swift code
in a structured way, using an AST.
If you created a new macro package with Swift Package Manager,
the generated `Package.swift` file
automatically includes a dependency on SwiftSyntax.
If you're adding macros to an existing project,
add a dependency on SwiftSyntax in your `Package.swift` file:
[SwiftSyntax]: https://github.com/swiftlang/swift-syntax
```swift
dependencies: [
.package(url: "https://github.com/swiftlang/swift-syntax", from: "509.0.0")
],
```
Depending on your macro's role,
there's a corresponding protocol from SwiftSyntax
that the macro implementation conforms to.
For example,
consider `#fourCharacterCode` from the previous section.
Here's a structure that implements that macro:
```swift
import SwiftSyntax
import SwiftSyntaxMacros
public struct FourCharacterCode: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) throws -> ExprSyntax {
guard let argument = node.argumentList.first?.expression,
let segments = argument.as(StringLiteralExprSyntax.self)?.segments,
segments.count == 1,
case .stringSegment(let literalSegment)? = segments.first
else {
throw CustomError.message("Need a static string")
}
let string = literalSegment.content.text
guard let result = fourCharacterCode(for: string) else {
throw CustomError.message("Invalid four-character code")
}
return "\(raw: result) as UInt32"
}
}
private func fourCharacterCode(for characters: String) -> UInt32? {
guard characters.count == 4 else { return nil }
var result: UInt32 = 0
for character in characters {
result = result << 8
guard let asciiValue = character.asciiValue else { return nil }
result += UInt32(asciiValue)
}
return result
}
enum CustomError: Error { case message(String) }
```
If you're adding this macro to an existing Swift Package Manager project,
add a type that acts as the entry point for the macro target
and lists the macros that the target defines:
```swift
import SwiftCompilerPlugin
@main
struct MyProjectMacros: CompilerPlugin {
var providingMacros: [Macro.Type] = [FourCharacterCode.self]
}
```
The `#fourCharacterCode` macro
is a freestanding macro that produces an expression,
so the `FourCharacterCode` type that implements it
conforms to the `ExpressionMacro` protocol.
The `ExpressionMacro` protocol has one requirement,
an `expansion(of:in:)` method that expands the AST.
For the list of macro roles and their corresponding SwiftSyntax protocols,
see <doc:Attributes#attached> and <doc:Attributes#freestanding>
in <doc:Attributes>.
To expand the `#fourCharacterCode` macro,
Swift sends the AST for the code that uses this macro
to the library that contains the macro implementation.
Inside the library, Swift calls `FourCharacterCode.expansion(of:in:)`,
passing in the AST and the context as arguments to the method.
The implementation of `expansion(of:in:)`
finds the string that was passed as an argument to `#fourCharacterCode`
and calculates the corresponding 32-bit unsigned integer literal value.
In the example above,
the first `guard` block extracts the string literal from the AST,
assigning that AST element to `literalSegment`.
The second `guard` block
calls the private `fourCharacterCode(for:)` function.
Both of these blocks throw an error if the macro is used incorrectly ---
the error message becomes a compiler error
at the malformed call site.
For example,
if you try to call the macro as `#fourCharacterCode("AB" + "CD")`
the compiler shows the error "Need a static string".
The `expansion(of:in:)` method returns an instance of `ExprSyntax`,
a type from SwiftSyntax that represents an expression in an AST.
Because this type conforms to the `StringLiteralConvertible` protocol,
the macro implementation uses a string literal
as a lightweight syntax to create its result.
All of the SwiftSyntax types that you return from a macro implementation
conform to `StringLiteralConvertible`,
so you can use this approach when implementing any kind of macro.
<!-- TODO contrast the `\(raw:)` and non-raw version. -->
<!--
The return-a-string APIs come from here
https://github.com/swiftlang/swift-syntax/blob/main/Sources/SwiftSyntaxBuilder/Syntax%2BStringInterpolation.swift
-->
<!-- OUTLINE:
- Note:
Behind the scenes, Swift serializes and deserializes the AST,
to pass the data across process boundaries,
but your macro implementation doesn't need to deal with any of that.
- This method is also passed a macro-expansion context, which you use to:
+ Generate unique symbol names
+ Produce diagnostics (`Diagnostic` and `SimpleDiagnosticMessage`)
+ Find a node's location in source
- Macro expansion happens in their surrounding context.
A macro can affect that environment if it needs to ---
and a macro that has bugs can interfere with that environment.
(Give guidance on when you'd do this. It should be rare.)
- Generated symbol names let a macro
avoid accidentally interacting with symbols in that environment.
To generate a unique symbol name,
call the `MacroExpansionContext.makeUniqueName()` method.
- Ways to create a syntax node include
Making an instance of the `Syntax` struct,
or `SyntaxToken`
or `ExprSyntax`.
(Need to give folks some general ideas,
and enough guidance so they can sort through
all the various `SwiftSyntax` node types and find the right one.)
- Attached macros follow the same general model as expression macros,
but with more moving parts.
- Pick the subprotocol of `AttachedMacro` to conform to,
depending on which kind of attached macro you're making.
[This is probably a table]
+ `AccessorMacro` goes with `@attached(accessor)`
+ `ConformanceMacro` goes with `@attached(conformance)`
[missing from the list under Declaring a Macro]
+ `MemberMacro` goes with `@attached(member)`
+ `PeerMacro` goes with `@attached(peer)`
+ `MemberAttributeMacro` goes with `@member(memberAttribute)`
- Code example of conforming to `MemberMacro`.
```
static func expansion<
Declaration: DeclGroupSyntax,
Context: MacroExpansionContext
>(
of node: AttributeSyntax,
providingMembersOf declaration: Declaration,
in context: Context
) throws -> [DeclSyntax]
```
- Adding a new member by making an instance of `Declaration`,
and returning it as part of the `[DeclSyntax]` list.
-->
## Developing and Debugging Macros
Macros are well suited to development using tests:
They transform one AST into another AST
without depending on any external state,
and without making changes to any external state.
In addition, you can create syntax nodes from a string literal,
which simplifies setting up the input for a test.
You can also read the `description` property of an AST
to get a string to compare against an expected value.
For example,
here's a test of the `#fourCharacterCode` macro from previous sections:
```swift
let source: SourceFileSyntax =
"""
let abcd = #fourCharacterCode("ABCD")
"""
let file = BasicMacroExpansionContext.KnownSourceFile(
moduleName: "MyModule",
fullFilePath: "test.swift"
)
let context = BasicMacroExpansionContext(sourceFiles: [source: file])
let transformedSF = source.expand(
macros:["fourCharacterCode": FourCharacterCode.self],
in: context
)
let expectedDescription =
"""
let abcd = 1145258561 as UInt32
"""
precondition(transformedSF.description == expectedDescription)
```
The example above tests the macro using a precondition,
but you could use a testing framework instead.
<!-- OUTLINE:
- Ways to view the macro expansion while debugging.
The SE prototype provides `-Xfrontend -dump-macro-expansions` for this.
[TR: Is this flag what we should suggest folks use,
or will there be better command-line options coming?]
- Use diagnostics for macros that have constraints/requirements
so your code can give a meaningful error to users when those aren't met,
instead of letting the compiler try & fail to build the generated code.
Additional APIs and concepts to introduce in the future,
in no particular order:
- Using `SyntaxRewriter` and the visitor pattern for modifying the AST
- Adding a suggested correction using `FixIt`
- concept of trivia
- `TokenSyntax`
-->
<!--
This source file is part of the Swift.org open source project
Copyright (c) 2014 - 2023 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
-->

View File

@@ -0,0 +1,758 @@
# Memory Safety
Structure your code to avoid conflicts when accessing memory.
By default, Swift prevents unsafe behavior from happening in your code.
For example,
Swift ensures that variables are initialized before they're used,
memory isn't accessed after it's been deallocated,
and array indices are checked for out-of-bounds errors.
Swift also makes sure that multiple accesses
to the same area of memory don't conflict,
by requiring code that modifies a location in memory
to have exclusive access to that memory.
Because Swift manages memory automatically,
most of the time you don't have to think about accessing memory at all.
However,
it's important to understand where potential conflicts can occur,
so you can avoid writing code that has conflicting access to memory.
If your code does contain conflicts,
you'll get a compile-time or runtime error.
<!--
TODO: maybe re-introduce this text...
Memory safety refers to...
The term *safety* usually refers to :newTerm:`memory safety`...
Unsafe access to memory is available, if you ask for it explicitly...
-->
## Understanding Conflicting Access to Memory
Access to memory happens in your code
when you do things like set the value of a variable
or pass an argument to a function.
For example,
the following code contains both a read access and a write access:
```swift
// A write access to the memory where one is stored.
var one = 1
// A read access from the memory where one is stored.
print("We're number \(one)!")
```
<!--
- test: `memory-read-write`
```swifttest
// A write access to the memory where one is stored.
-> var one = 1
// A read access from the memory where one is stored.
-> print("We're number \(one)!")
<< We're number 1!
```
-->
<!--
Might be worth a different example,
or else I'm going to keep getting "We are Number One" stuck in my head.
-->
A conflicting access to memory can occur
when different parts of your code are trying
to access the same location in memory at the same time.
Multiple accesses to a location in memory at the same time
can produce unpredictable or inconsistent behavior.
In Swift, there are ways to modify a value
that span several lines of code,
making it possible to attempt to access a value
in the middle of its own modification.
You can see a similar problem
by thinking about how you update a budget
that's written on a piece of paper.
Updating the budget is a two-step process:
First you add the items' names and prices,
and then you change the total amount
to reflect the items currently on the list.
Before and after the update,
you can read any information from the budget
and get a correct answer,
as shown in the figure below.
![](memory_shopping)
While you're adding items to the budget,
it's in a temporary, invalid state
because the total amount hasn't been updated
to reflect the newly added items.
Reading the total amount
during the process of adding an item
gives you incorrect information.
This example also demonstrates
a challenge you may encounter
when fixing conflicting access to memory:
There are sometimes multiple ways to fix the conflict
that produce different answers,
and it's not always obvious which answer is correct.
In this example,
depending on whether you wanted the original total amount
or the updated total amount,
either $5 or $320 could be the correct answer.
Before you can fix the conflicting access,
you have to determine what it was intended to do.
> Note: If you've written concurrent or multithreaded code,
> conflicting access to memory might be a familiar problem.
> However,
> the conflicting access discussed here can happen
> on a single thread and
> *doesn't* involve concurrent or multithreaded code.
>
> If you have conflicting access to memory
> from within a single thread,
> Swift guarantees that you'll get an error
> at either compile time or runtime.
> For multithreaded code,
> use [Thread Sanitizer](https://developer.apple.com/documentation/xcode/diagnosing_memory_thread_and_crash_issues_early)
> to help detect conflicting access across threads.
<!--
TODO: The xref above doesn't seem to give enough information.
What should I be looking for when I get to the linked page?
-->
### Characteristics of Memory Access
There are three characteristics of memory access
to consider in the context of conflicting access:
whether the access is a read or a write,
the duration of the access,
and the location in memory being accessed.
Specifically,
a conflict occurs if you have two accesses
that meet all of the following conditions:
- The accesses aren't both reads, and aren't both atomic.
- They access the same location in memory.
- Their durations overlap.
The difference between a read and write access
is usually obvious:
a write access changes the location in memory,
but a read access doesn't.
The location in memory
refers to what is being accessed ---
for example, a variable, constant, or property.
The duration of a memory access
is either instantaneous or long-term.
An access is *atomic* if
it's a call to an atomic operation on [`Atomic`] or [`AtomicLazyReference`],
or it it uses only C atomic operations;
otherwise it's nonatomic.
For a list of C atomic functions, see the `stdatomic(3)` man page.
[`Atomic`]: https://developer.apple.com/documentation/synchronization/atomic
[`AtomicLazyReference`]: https://developer.apple.com/documentation/synchronization/atomiclazyreference
<!--
Using the C atomic functions from Swift
requires some shimming that's out of scope for TSPL - for example:
https://github.com/apple/swift-atomics/tree/main/Sources/_AtomicsShims
-->
An access is *instantaneous*
if it's not possible for other code to run
after that access starts but before it ends.
By their nature, two instantaneous accesses can't happen at the same time.
Most memory access is instantaneous.
For example,
all the read and write accesses in the code listing below are instantaneous:
```swift
func oneMore(than number: Int) -> Int {
return number + 1
}
var myNumber = 1
myNumber = oneMore(than: myNumber)
print(myNumber)
// Prints "2".
```
<!--
- test: `memory-instantaneous`
```swifttest
-> func oneMore(than number: Int) -> Int {
return number + 1
}
-> var myNumber = 1
-> myNumber = oneMore(than: myNumber)
-> print(myNumber)
<- 2
```
-->
However,
there are several ways to access memory,
called *long-term* accesses,
that span the execution of other code.
The difference between instantaneous access and long-term access
is that its possible for other code to run
after a long-term access starts but before it ends,
which is called *overlap*.
A long-term access can overlap
with other long-term accesses and instantaneous accesses.
Overlapping accesses appear primarily in code that uses
in-out parameters in functions and methods
or mutating methods of a structure.
The specific kinds of Swift code that use long-term accesses
are discussed in the sections below.
## Conflicting Access to In-Out Parameters
A function has long-term write access
to all of its in-out parameters.
The write access for an in-out parameter starts
after all of the non-in-out parameters have been evaluated
and lasts for the entire duration of that function call.
If there are multiple in-out parameters,
the write accesses start in the same order as the parameters appear.
One consequence of this long-term write access
is that you can't access the original
variable that was passed as in-out,
even if scoping rules and access control would otherwise permit it ---
any access to the original creates a conflict.
For example:
```swift
var stepSize = 1
func increment(_ number: inout Int) {
number += stepSize
}
increment(&stepSize)
// Error: Conflicting accesses to stepSize.
```
<!--
- test: `memory-increment`
```swifttest
-> var stepSize = 1
-> func increment(_ number: inout Int) {
number += stepSize
}
-> increment(&stepSize)
// Error: Conflicting accesses to stepSize.
xx Simultaneous accesses to 0x10e8667d8, but modification requires exclusive access.
xx Previous access (a modification) started at (0x10e86b032).
xx Current access (a read) started at:
```
-->
In the code above,
`stepSize` is a global variable,
and it's normally accessible from within `increment(_:)`.
However,
the read access to `stepSize` overlaps with
the write access to `number`.
As shown in the figure below,
both `number` and `stepSize` refer to the same location in memory.
The read and write accesses
refer to the same memory and they overlap,
producing a conflict.
![](memory_increment)
One way to solve this conflict
is to make an explicit copy of `stepSize`:
```swift
// Make an explicit copy.
var copyOfStepSize = stepSize
increment(&copyOfStepSize)
// Update the original.
stepSize = copyOfStepSize
// stepSize is now 2
```
<!--
- test: `memory-increment-copy`
```swifttest
>> var stepSize = 1
>> func increment(_ number: inout Int) {
>> number += stepSize
>> }
// Make an explicit copy.
-> var copyOfStepSize = stepSize
-> increment(&copyOfStepSize)
// Update the original.
-> stepSize = copyOfStepSize
/> stepSize is now \(stepSize)
</ stepSize is now 2
```
-->
When you make a copy of `stepSize` before calling `increment(_:)`,
it's clear that the value of `copyOfStepSize` is incremented
by the current step size.
The read access ends before the write access starts,
so there isn't a conflict.
Another consequence of long-term write access
to in-out parameters is that
passing a single variable
as the argument for multiple in-out parameters
of the same function
produces a conflict.
For example:
```swift
func balance(_ x: inout Int, _ y: inout Int) {
let sum = x + y
x = sum / 2
y = sum - x
}
var playerOneScore = 42
var playerTwoScore = 30
balance(&playerOneScore, &playerTwoScore) // OK
balance(&playerOneScore, &playerOneScore)
// Error: Conflicting accesses to playerOneScore.
```
<!--
- test: `memory-balance`
```swifttest
-> func balance(_ x: inout Int, _ y: inout Int) {
let sum = x + y
x = sum / 2
y = sum - x
}
-> var playerOneScore = 42
-> var playerTwoScore = 30
-> balance(&playerOneScore, &playerTwoScore) // OK
-> balance(&playerOneScore, &playerOneScore)
// Error: Conflicting accesses to playerOneScore.
!$ error: inout arguments are not allowed to alias each other
!! balance(&playerOneScore, &playerOneScore)
!! ^~~~~~~~~~~~~~~
!$ note: previous aliasing argument
!! balance(&playerOneScore, &playerOneScore)
!! ^~~~~~~~~~~~~~~
!$ error: overlapping accesses to 'playerOneScore', but modification requires exclusive access; consider copying to a local variable
!! balance(&playerOneScore, &playerOneScore)
!! ^~~~~~~~~~~~~~~
!$ note: conflicting access is here
!! balance(&playerOneScore, &playerOneScore)
!! ^~~~~~~~~~~~~~~
```
-->
The `balance(_:_:)` function above
modifies its two parameters
to divide the total value evenly between them.
Calling it with `playerOneScore` and `playerTwoScore` as arguments
doesn't produce a conflict ---
there are two write accesses that overlap in time,
but they access different locations in memory.
In contrast,
passing `playerOneScore` as the value for both parameters
produces a conflict
because it tries to perform two write accesses
to the same location in memory at the same time.
> Note: Because operators are functions,
> they can also have long-term accesses to their in-out parameters.
> For example, if `balance(_:_:)` was an operator function named `<^>`,
> writing `playerOneScore <^> playerOneScore`
> would result in the same conflict
> as `balance(&playerOneScore, &playerOneScore)`.
## Conflicting Access to self in Methods
<!--
This (probably?) applies to all value types,
but structures are the only place you can observe it.
Enumerations can have mutating methods
but you can't mutate their associated values in place,
and tuples can't have methods.
-->
<!--
Methods behave like self is passed to the method as inout
because, under the hood, that's exactly what happens.
-->
A mutating method on a structure has write access to `self`
for the duration of the method call.
For example, consider a game where each player
has a health amount, which decreases when taking damage,
and an energy amount, which decreases when using special abilities.
```swift
struct Player {
var name: String
var health: Int
var energy: Int
static let maxHealth = 10
mutating func restoreHealth() {
health = Player.maxHealth
}
}
```
<!--
- test: `memory-player-share-with-self`
```swifttest
>> func balance(_ x: inout Int, _ y: inout Int) {
>> let sum = x + y
>> x = sum / 2
>> y = sum - x
>> }
-> struct Player {
var name: String
var health: Int
var energy: Int
static let maxHealth = 10
mutating func restoreHealth() {
health = Player.maxHealth
}
}
```
-->
In the `restoreHealth()` method above,
a write access to `self` starts at the beginning of the method
and lasts until the method returns.
In this case, there's no other code
inside `restoreHealth()`
that could have an overlapping access to the properties of a `Player` instance.
The `shareHealth(with:)` method below
takes another `Player` instance as an in-out parameter,
creating the possibility of overlapping accesses.
```swift
extension Player {
mutating func shareHealth(with teammate: inout Player) {
balance(&teammate.health, &health)
}
}
var oscar = Player(name: "Oscar", health: 10, energy: 10)
var maria = Player(name: "Maria", health: 5, energy: 10)
oscar.shareHealth(with: &maria) // OK
```
<!--
- test: `memory-player-share-with-self`
```swifttest
-> extension Player {
mutating func shareHealth(with teammate: inout Player) {
balance(&teammate.health, &health)
}
}
-> var oscar = Player(name: "Oscar", health: 10, energy: 10)
-> var maria = Player(name: "Maria", health: 5, energy: 10)
-> oscar.shareHealth(with: &maria) // OK
```
-->
In the example above,
calling the `shareHealth(with:)` method
for Oscar's player to share health with Maria's player
doesn't cause a conflict.
There's a write access to `oscar` during the method call
because `oscar` is the value of `self` in a mutating method,
and there's a write access to `maria`
for the same duration
because `maria` was passed as an in-out parameter.
As shown in the figure below,
they access different locations in memory.
Even though the two write accesses overlap in time,
they don't conflict.
![](memory_share_health_maria)
However,
if you pass `oscar` as the argument to `shareHealth(with:)`,
there's a conflict:
```swift
oscar.shareHealth(with: &oscar)
// Error: Conflicting accesses to oscar.
```
<!--
- test: `memory-player-share-with-self`
```swifttest
-> oscar.shareHealth(with: &oscar)
// Error: Conflicting accesses to oscar.
!$ error: inout arguments are not allowed to alias each other
!! oscar.shareHealth(with: &oscar)
!! ^~~~~~
!$ note: previous aliasing argument
!! oscar.shareHealth(with: &oscar)
!! ^~~~~
!$ error: overlapping accesses to 'oscar', but modification requires exclusive access; consider copying to a local variable
!! oscar.shareHealth(with: &oscar)
!! ^~~~~
!$ note: conflicting access is here
!! oscar.shareHealth(with: &oscar)
!! ^~~~~~
```
-->
The mutating method needs write access to `self`
for the duration of the method,
and the in-out parameter needs write access to `teammate`
for the same duration.
Within the method,
both `self` and `teammate` refer to
the same location in memory ---
as shown in the figure below.
The two write accesses
refer to the same memory and they overlap,
producing a conflict.
![](memory_share_health_oscar)
## Conflicting Access to Properties
Types like structures, tuples, and enumerations
are made up of individual constituent values,
such as the properties of a structure or the elements of a tuple.
Because these are value types, mutating any piece of the value
mutates the whole value,
meaning read or write access to one of the properties
requires read or write access to the whole value.
For example,
overlapping write accesses to the elements of a tuple
produces a conflict:
```swift
var playerInformation = (health: 10, energy: 20)
balance(&playerInformation.health, &playerInformation.energy)
// Error: Conflicting access to properties of playerInformation.
```
<!--
- test: `memory-tuple`
```swifttest
>> func balance(_ x: inout Int, _ y: inout Int) {
>> let sum = x + y
>> x = sum / 2
>> y = sum - x
>> }
-> var playerInformation = (health: 10, energy: 20)
-> balance(&playerInformation.health, &playerInformation.energy)
// Error: Conflicting access to properties of playerInformation.
xx Simultaneous accesses to 0x10794d848, but modification requires exclusive access.
xx Previous access (a modification) started at (0x107952037).
xx Current access (a modification) started at:
```
-->
In the example above,
calling `balance(_:_:)` on the elements of a tuple
produces a conflict
because there are overlapping write accesses to `playerInformation`.
Both `playerInformation.health` and `playerInformation.energy`
are passed as in-out parameters,
which means `balance(_:_:)` needs write access to them
for the duration of the function call.
In both cases, a write access to the tuple element
requires a write access to the entire tuple.
This means there are two write accesses to `playerInformation`
with durations that overlap,
causing a conflict.
The code below shows that the same error appears
for overlapping write accesses
to the properties of a structure
that's stored in a global variable.
```swift
var holly = Player(name: "Holly", health: 10, energy: 10)
balance(&holly.health, &holly.energy) // Error
```
<!--
- test: `memory-share-health-global`
```swifttest
>> struct Player {
>> var name: String
>> var health: Int
>> var energy: Int
>> }
>> func balance(_ x: inout Int, _ y: inout Int) {
>> let sum = x + y
>> x = sum / 2
>> y = sum - x
>> }
-> var holly = Player(name: "Holly", health: 10, energy: 10)
-> balance(&holly.health, &holly.energy) // Error
xx Simultaneous accesses to 0x10794d848, but modification requires exclusive access.
xx Previous access (a modification) started at (0x107952037).
xx Current access (a modification) started at:
```
-->
In practice,
most access to the properties of a structure
can overlap safely.
For example,
if the variable `holly` in the example above
is changed to a local variable instead of a global variable,
the compiler can prove that overlapping access
to stored properties of the structure is safe:
```swift
func someFunction() {
var oscar = Player(name: "Oscar", health: 10, energy: 10)
balance(&oscar.health, &oscar.energy) // OK
}
```
<!--
- test: `memory-share-health-local`
```swifttest
>> struct Player {
>> var name: String
>> var health: Int
>> var energy: Int
>> }
>> func balance(_ x: inout Int, _ y: inout Int) {
>> let sum = x + y
>> x = sum / 2
>> y = sum - x
>> }
-> func someFunction() {
var oscar = Player(name: "Oscar", health: 10, energy: 10)
balance(&oscar.health, &oscar.energy) // OK
}
>> someFunction()
```
-->
In the example above,
Oscar's health and energy are passed
as the two in-out parameters to `balance(_:_:)`.
The compiler can prove that memory safety is preserved
because the two stored properties don't interact in any way.
The restriction against
overlapping access to properties of a structure
isn't always necessary to preserve memory safety.
Memory safety is the desired guarantee,
but exclusive access is a stricter requirement than memory safety ---
which means some code preserves memory safety,
even though it violates exclusive access to memory.
Swift allows this memory-safe code if the compiler can prove
that the nonexclusive access to memory is still safe.
Specifically, it can prove
that overlapping access to properties of a structure is safe
if the following conditions apply:
- You're accessing only stored properties of an instance,
not computed properties or class properties.
- The structure is the value of a local variable,
not a global variable.
- The structure is either not captured by any closures,
or it's captured only by nonescaping closures.
If the compiler can't prove the access is safe,
it doesn't allow the access.
<!--
Because there's no syntax
to mutate an enum's associated value in place,
we can't show that overlapping mutations
to two different associated values on the same enum
would violate exclusivity.
Otherwise, we'd want an example of that
in this section too --
it's the moral equivalent of property access.
-->
<!--
.. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. .. ..
-->
<!--
In Swift 4, the only way to create a long-term read
is to use implicit pointer conversion
when passing a value as a nonmutating unsafe pointer parameter,
as in the example below.
There's discussion in <rdar://problem/33115142>
about changing the semantics of nonmutating method calls
to be long-term reads,
but it's not clear if/when that change will land.
::
var global = 4
func foo(_ x: UnsafePointer<Int>){
global = 7
}
foo(&global)
print(global)
// Simultaneous accesses to 0x106761618, but modification requires exclusive access.
// Previous access (a read) started at temp2`main + 87 (0x10675e417).
// Current access (a modification) started at:
// 0 libswiftCore.dylib 0x0000000106ac7b90 swift_beginAccess + 605
// 1 temp2 0x000000010675e500 foo(_:) + 39
// 2 temp2 0x000000010675e3c0 main + 102
// 3 libdyld.dylib 0x00007fff69c75144 start + 1
// Fatal access conflict detected.
-->
<!--
TEXT FOR THE FUTURE
Versions of Swift before Swift 5 ensure memory safety
by aggressively making a copy of the shared mutable state
when a conflicting access is possible.
The copy is no longer shared, preventing the possibility of conflicts.
However, the copying approach has a negative impact
on performance and memory usage.
-->
<!--
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
-->

View File

@@ -0,0 +1,661 @@
# Methods
Define and call functions that are part of an instance or type.
*Methods* are functions that are associated with a particular type.
Classes, structures, and enumerations can all define instance methods,
which encapsulate specific tasks and functionality for working with an instance of a given type.
Classes, structures, and enumerations can also define type methods,
which are associated with the type itself.
Type methods are similar to class methods in Objective-C.
The fact that structures and enumerations can define methods in Swift
is a major difference from C and Objective-C.
In Objective-C, classes are the only types that can define methods.
In Swift, you can choose whether to define a class, structure, or enumeration,
and still have the flexibility to define methods on the type you create.
## Instance Methods
*Instance methods* are functions that belong to instances of
a particular class, structure, or enumeration.
They support the functionality of those instances,
either by providing ways to access and modify instance properties,
or by providing functionality related to the instance's purpose.
Instance methods have exactly the same syntax as functions,
as described in <doc:Functions>.
You write an instance method within the opening and closing braces of the type it belongs to.
An instance method has implicit access to all other instance methods and properties of that type.
An instance method can be called only on a specific instance of the type it belongs to.
It can't be called in isolation without an existing instance.
Here's an example that defines a simple `Counter` class,
which can be used to count the number of times an action occurs:
```swift
class Counter {
var count = 0
func increment() {
count += 1
}
func increment(by amount: Int) {
count += amount
}
func reset() {
count = 0
}
}
```
<!--
- test: `instanceMethods`
```swifttest
-> class Counter {
var count = 0
func increment() {
count += 1
}
func increment(by amount: Int) {
count += amount
}
func reset() {
count = 0
}
}
```
-->
The `Counter` class defines three instance methods:
- `increment()` increments the counter by `1`.
- `increment(by: Int)` increments the counter by a specified integer amount.
- `reset()` resets the counter to zero.
The `Counter` class also declares a variable property, `count`,
to keep track of the current counter value.
You call instance methods with the same dot syntax as properties:
```swift
let counter = Counter()
// the initial counter value is 0
counter.increment()
// the counter's value is now 1
counter.increment(by: 5)
// the counter's value is now 6
counter.reset()
// the counter's value is now 0
```
<!--
- test: `instanceMethods`
```swifttest
-> let counter = Counter()
/> the initial counter value is \(counter.count)
</ the initial counter value is 0
-> counter.increment()
/> the counter's value is now \(counter.count)
</ the counter's value is now 1
-> counter.increment(by: 5)
/> the counter's value is now \(counter.count)
</ the counter's value is now 6
-> counter.reset()
/> the counter's value is now \(counter.count)
</ the counter's value is now 0
```
-->
Function parameters can have both a name (for use within the function's body)
and an argument label (for use when calling the function),
as described in <doc:Functions#Function-Argument-Labels-and-Parameter-Names>.
The same is true for method parameters,
because methods are just functions that are associated with a type.
### The self Property
Every instance of a type has an implicit property called `self`,
which is exactly equivalent to the instance itself.
You use the `self` property to refer to the current instance
within its own instance methods.
The `increment()` method in the example above could have been written like this:
```swift
func increment() {
self.count += 1
}
```
<!--
- test: `instanceMethodsIncrement`
```swifttest
>> class Counter {
>> var count: Int = 0
func increment() {
self.count += 1
}
>> }
```
-->
<!--
NOTE: I'm slightly cheating with my testing of this excerpt, but it works!
-->
In practice, you don't need to write `self` in your code very often.
If you don't explicitly write `self`,
Swift assumes that you are referring to a property or method of the current instance
whenever you use a known property or method name within a method.
This assumption is demonstrated by the use of `count` (rather than `self.count`)
inside the three instance methods for `Counter`.
The main exception to this rule occurs when a parameter name for an instance method
has the same name as a property of that instance.
In this situation, the parameter name takes precedence,
and it becomes necessary to refer to the property in a more qualified way.
You use the `self` property to
distinguish between the parameter name and the property name.
Here, `self` disambiguates between
a method parameter called `x` and an instance property that's also called `x`:
```swift
struct Point {
var x = 0.0, y = 0.0
func isToTheRightOf(x: Double) -> Bool {
return self.x > x
}
}
let somePoint = Point(x: 4.0, y: 5.0)
if somePoint.isToTheRightOf(x: 1.0) {
print("This point is to the right of the line where x == 1.0")
}
// Prints "This point is to the right of the line where x == 1.0".
```
<!--
- test: `self`
```swifttest
-> struct Point {
var x = 0.0, y = 0.0
func isToTheRightOf(x: Double) -> Bool {
return self.x > x
}
}
-> let somePoint = Point(x: 4.0, y: 5.0)
-> if somePoint.isToTheRightOf(x: 1.0) {
print("This point is to the right of the line where x == 1.0")
}
<- This point is to the right of the line where x == 1.0
```
-->
Without the `self` prefix,
Swift would assume that both uses of `x` referred to the method parameter called `x`.
### Modifying Value Types from Within Instance Methods
Structures and enumerations are *value types*.
By default, the properties of a value type can't be modified from within its instance methods.
<!--
TODO: find out why. once I actually know why, explain it.
-->
However, if you need to modify the properties of your structure or enumeration
within a particular method,
you can opt in to *mutating* behavior for that method.
The method can then mutate (that is, change)
its properties from within the method,
and any changes that it makes are written back to the original structure when the method ends.
The method can also assign a completely new instance to its implicit `self` property,
and this new instance will replace the existing one when the method ends.
You can opt in to this behavior by placing the `mutating` keyword
before the `func` keyword for that method:
```swift
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveBy(x: 2.0, y: 3.0)
print("The point is now at (\(somePoint.x), \(somePoint.y))")
// Prints "The point is now at (3.0, 4.0)".
```
<!--
- test: `selfStructures`
```swifttest
-> struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
}
-> var somePoint = Point(x: 1.0, y: 1.0)
-> somePoint.moveBy(x: 2.0, y: 3.0)
-> print("The point is now at (\(somePoint.x), \(somePoint.y))")
<- The point is now at (3.0, 4.0)
```
-->
The `Point` structure above defines a mutating `moveBy(x:y:)` method,
which moves a `Point` instance by a certain amount.
Instead of returning a new point,
this method actually modifies the point on which it's called.
The `mutating` keyword is added to its definition
to enable it to modify its properties.
Note that you can't call a mutating method on a constant of structure type,
because its properties can't be changed, even if they're variable properties,
as described in <doc:Properties#Stored-Properties-of-Constant-Structure-Instances>:
```swift
let fixedPoint = Point(x: 3.0, y: 3.0)
fixedPoint.moveBy(x: 2.0, y: 3.0)
// this will report an error
```
<!--
- test: `selfStructures-err`
```swifttest
>> struct Point {
>> var x = 0.0, y = 0.0
>> mutating func moveBy(x deltaX: Double, y deltaY: Double) {
>> x += deltaX
>> y += deltaY
>> }
>> }
-> let fixedPoint = Point(x: 3.0, y: 3.0)
-> fixedPoint.moveBy(x: 2.0, y: 3.0)
!$ error: cannot use mutating member on immutable value: 'fixedPoint' is a 'let' constant
!! fixedPoint.moveBy(x: 2.0, y: 3.0)
!! ~~~~~~~~~~ ^
!$ note: change 'let' to 'var' to make it mutable
!! let fixedPoint = Point(x: 3.0, y: 3.0)
!! ^~~
!! var
// this will report an error
```
-->
<!--
TODO: talk about nonmutating as well.
Struct setters are implicitly 'mutating' by default and thus don't work on 'let's.
However, JoeG says that this ought to work
if the setter for the computed property is explicitly defined as @!mutating.
-->
### Assigning to self Within a Mutating Method
Mutating methods can assign an entirely new instance to the implicit `self` property.
The `Point` example shown above could have been written in the following way instead:
```swift
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
self = Point(x: x + deltaX, y: y + deltaY)
}
}
```
<!--
- test: `selfStructuresAssign`
```swifttest
-> struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
self = Point(x: x + deltaX, y: y + deltaY)
}
}
>> var somePoint = Point(x: 1.0, y: 1.0)
>> somePoint.moveBy(x: 2.0, y: 3.0)
>> print("The point is now at (\(somePoint.x), \(somePoint.y))")
<< The point is now at (3.0, 4.0)
```
-->
This version of the mutating `moveBy(x:y:)` method creates a new structure
whose `x` and `y` values are set to the target location.
The end result of calling this alternative version of the method
will be exactly the same as for calling the earlier version.
Mutating methods for enumerations can set the implicit `self` parameter to be
a different case from the same enumeration:
```swift
enum TriStateSwitch {
case off, low, high
mutating func next() {
switch self {
case .off:
self = .low
case .low:
self = .high
case .high:
self = .off
}
}
}
var ovenLight = TriStateSwitch.low
ovenLight.next()
// ovenLight is now equal to .high
ovenLight.next()
// ovenLight is now equal to .off
```
<!--
- test: `selfEnumerations`
```swifttest
-> enum TriStateSwitch {
case off, low, high
mutating func next() {
switch self {
case .off:
self = .low
case .low:
self = .high
case .high:
self = .off
}
}
}
-> var ovenLight = TriStateSwitch.low
-> ovenLight.next()
// ovenLight is now equal to .high
-> ovenLight.next()
// ovenLight is now equal to .off
```
-->
This example defines an enumeration for a three-state switch.
The switch cycles between three different power states
(`off`, `low` and `high`)
every time its `next()` method is called.
## Type Methods
Instance methods, as described above,
are methods that you call on an instance of a particular type.
You can also define methods that are called on the type itself.
These kinds of methods are called *type methods*.
You indicate type methods by writing
the `static` keyword before the method's `func` keyword.
Classes can use the `class` keyword instead,
to allow subclasses to override the superclasss implementation of that method.
> Note: In Objective-C, you can define type-level methods only for Objective-C classes.
> In Swift, you can define type-level methods for all classes, structures, and enumerations.
> Each type method is explicitly scoped to the type it supports.
Type methods are called with dot syntax, like instance methods.
However, you call type methods on the type, not on an instance of that type.
Here's how you call a type method on a class called `SomeClass`:
```swift
class SomeClass {
class func someTypeMethod() {
// type method implementation goes here
}
}
SomeClass.someTypeMethod()
```
<!--
- test: `typeMethods`
```swifttest
-> class SomeClass {
class func someTypeMethod() {
// type method implementation goes here
}
}
-> SomeClass.someTypeMethod()
```
-->
Within the body of a type method,
the implicit `self` property refers to the type itself,
rather than an instance of that type.
This means that you can use `self` to disambiguate between
type properties and type method parameters,
just as you do for instance properties and instance method parameters.
More generally, any unqualified method and property names that you use
within the body of a type method will refer to other type-level methods and properties.
A type method can call another type method with the other method's name,
without needing to prefix it with the type name.
Similarly, type methods on structures and enumerations can access type properties
by using the type property's name without a type name prefix.
The example below defines a structure called `LevelTracker`,
which tracks a player's progress through the different levels or stages of a game.
It's a single-player game,
but can store information for multiple players on a single device.
All of the game's levels (apart from level one) are locked when the game is first played.
Every time a player finishes a level,
that level is unlocked for all players on the device.
The `LevelTracker` structure uses type properties and methods
to keep track of which levels of the game have been unlocked.
It also tracks the current level for an individual player.
```swift
struct LevelTracker {
static var highestUnlockedLevel = 1
var currentLevel = 1
static func unlock(_ level: Int) {
if level > highestUnlockedLevel { highestUnlockedLevel = level }
}
static func isUnlocked(_ level: Int) -> Bool {
return level <= highestUnlockedLevel
}
@discardableResult
mutating func advance(to level: Int) -> Bool {
if LevelTracker.isUnlocked(level) {
currentLevel = level
return true
} else {
return false
}
}
}
```
<!--
- test: `typeMethods`
```swifttest
-> struct LevelTracker {
static var highestUnlockedLevel = 1
var currentLevel = 1
-> static func unlock(_ level: Int) {
if level > highestUnlockedLevel { highestUnlockedLevel = level }
}
-> static func isUnlocked(_ level: Int) -> Bool {
return level <= highestUnlockedLevel
}
-> @discardableResult
mutating func advance(to level: Int) -> Bool {
if LevelTracker.isUnlocked(level) {
currentLevel = level
return true
} else {
return false
}
}
}
```
-->
The `LevelTracker` structure keeps track of the highest level that any player has unlocked.
This value is stored in a type property called `highestUnlockedLevel`.
`LevelTracker` also defines two type functions to work with
the `highestUnlockedLevel` property.
The first is a type function called `unlock(_:)`,
which updates the value of `highestUnlockedLevel` whenever a new level is unlocked.
The second is a convenience type function called `isUnlocked(_:)`,
which returns `true` if a particular level number is already unlocked.
(Note that these type methods can access the `highestUnlockedLevel` type property
without your needing to write it as `LevelTracker.highestUnlockedLevel`.)
In addition to its type property and type methods,
`LevelTracker` tracks an individual player's progress through the game.
It uses an instance property called `currentLevel` to track
the level that a player is currently playing.
To help manage the `currentLevel` property,
`LevelTracker` defines an instance method called `advance(to:)`.
Before updating `currentLevel`,
this method checks whether the requested new level is already unlocked.
The `advance(to:)` method returns a Boolean value to indicate
whether or not it was actually able to set `currentLevel`.
Because it's not necessarily a mistake for
code that calls the `advance(to:)` method
to ignore the return value,
this function is marked with the `@discardableResult` attribute.
For more information about this attribute,
see <doc:Attributes>.
The `LevelTracker` structure is used with the `Player` class, shown below,
to track and update the progress of an individual player:
```swift
class Player {
var tracker = LevelTracker()
let playerName: String
func complete(level: Int) {
LevelTracker.unlock(level + 1)
tracker.advance(to: level + 1)
}
init(name: String) {
playerName = name
}
}
```
<!--
- test: `typeMethods`
```swifttest
-> class Player {
var tracker = LevelTracker()
let playerName: String
func complete(level: Int) {
LevelTracker.unlock(level + 1)
tracker.advance(to: level + 1)
}
init(name: String) {
playerName = name
}
}
```
-->
The `Player` class creates a new instance of `LevelTracker`
to track that player's progress.
It also provides a method called `complete(level:)`,
which is called whenever a player completes a particular level.
This method unlocks the next level for all players
and updates the player's progress to move them to the next level.
(The Boolean return value of `advance(to:)` is ignored,
because the level is known to have been unlocked
by the call to `LevelTracker.unlock(_:)` on the previous line.)
You can create an instance of the `Player` class for a new player,
and see what happens when the player completes level one:
```swift
var player = Player(name: "Argyrios")
player.complete(level: 1)
print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
// Prints "highest unlocked level is now 2".
```
<!--
- test: `typeMethods`
```swifttest
-> var player = Player(name: "Argyrios")
-> player.complete(level: 1)
-> print("highest unlocked level is now \(LevelTracker.highestUnlockedLevel)")
<- highest unlocked level is now 2
```
-->
If you create a second player, whom you try to move to a level
that's not yet unlocked by any player in the game,
the attempt to set the player's current level fails:
```swift
player = Player(name: "Beto")
if player.tracker.advance(to: 6) {
print("player is now on level 6")
} else {
print("level 6 hasn't yet been unlocked")
}
// Prints "level 6 hasn't yet been unlocked".
```
<!--
- test: `typeMethods`
```swifttest
-> player = Player(name: "Beto")
-> if player.tracker.advance(to: 6) {
print("player is now on level 6")
} else {
print("level 6 hasn't yet been unlocked")
}
<- level 6 hasn't yet been unlocked
```
-->
<!--
TODO: Method Binding
--------------------
TODO: you can get a function that refers to a method, either with or without the 'self' argument already being bound:
class C {
func foo(x: Int) -> Float { ... }
}
var c = C()
var boundFunc = c.foo // a function with type (Int) -> Float
var unboundFunc = C.foo // a function with type (C) -> (Int) -> Float
TODO: selector-style methods can be referenced as foo.bar:bas:
(see Doug's comments from the 2014-03-12 release notes)
-->
<!--
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
-->

View File

@@ -0,0 +1,203 @@
# Nested Types
Define types inside the scope of another type.
Enumerations are often created to support a specific class or structure's functionality.
Similarly, it can be convenient to define utility structures
purely for use within the context of a more complex type,
and protocols that are normally used in conjunction with a specific type.
To accomplish this, Swift enables you to define *nested types*,
whereby you nest supporting types like enumerations, structures, and protocols
within the definition of the type they support.
To nest a type within another type,
write its definition within the outer braces of the type it supports.
Types can be nested to as many levels as are required.
## Nested Types in Action
The example below defines a structure called `BlackjackCard`,
which models a playing card as used in the game of Blackjack.
The `BlackjackCard` structure contains two nested enumeration types
called `Suit` and `Rank`.
In Blackjack, the Ace cards have a value of either one or eleven.
This feature is represented by a structure called `Values`,
which is nested within the `Rank` enumeration:
```swift
struct BlackjackCard {
// nested Suit enumeration
enum Suit: Character {
case spades = "", hearts = "", diamonds = "", clubs = ""
}
// nested Rank enumeration
enum Rank: Int {
case two = 2, three, four, five, six, seven, eight, nine, ten
case jack, queen, king, ace
struct Values {
let first: Int, second: Int?
}
var values: Values {
switch self {
case .ace:
return Values(first: 1, second: 11)
case .jack, .queen, .king:
return Values(first: 10, second: nil)
default:
return Values(first: self.rawValue, second: nil)
}
}
}
// BlackjackCard properties and methods
let rank: Rank, suit: Suit
var description: String {
var output = "suit is \(suit.rawValue),"
output += " value is \(rank.values.first)"
if let second = rank.values.second {
output += " or \(second)"
}
return output
}
}
```
<!--
- test: `nestedTypes`
```swifttest
-> struct BlackjackCard {
// nested Suit enumeration
enum Suit: Character {
case spades = "♠", hearts = "♡", diamonds = "♢", clubs = "♣"
}
// nested Rank enumeration
enum Rank: Int {
case two = 2, three, four, five, six, seven, eight, nine, ten
case jack, queen, king, ace
struct Values {
let first: Int, second: Int?
}
var values: Values {
switch self {
case .ace:
return Values(first: 1, second: 11)
case .jack, .queen, .king:
return Values(first: 10, second: nil)
default:
return Values(first: self.rawValue, second: nil)
}
}
}
// BlackjackCard properties and methods
let rank: Rank, suit: Suit
var description: String {
var output = "suit is \(suit.rawValue),"
output += " value is \(rank.values.first)"
if let second = rank.values.second {
output += " or \(second)"
}
return output
}
}
```
-->
The `Suit` enumeration describes the four common playing card suits,
together with a raw `Character` value to represent their symbol.
The `Rank` enumeration describes the thirteen possible playing card ranks,
together with a raw `Int` value to represent their face value.
(This raw `Int` value isn't used for the Jack, Queen, King, and Ace cards.)
As mentioned above, the `Rank` enumeration defines
a further nested structure of its own, called `Values`.
This structure encapsulates the fact that most cards have one value,
but the Ace card has two values.
The `Values` structure defines two properties to represent this:
- `first`, of type `Int`
- `second`, of type `Int?`, or “optional `Int`”
`Rank` also defines a computed property, `values`,
which returns an instance of the `Values` structure.
This computed property considers the rank of the card
and initializes a new `Values` instance with appropriate values based on its rank.
It uses special values for `jack`, `queen`, `king`, and `ace`.
For the numeric cards, it uses the rank's raw `Int` value.
The `BlackjackCard` structure itself has two properties --- `rank` and `suit`.
It also defines a computed property called `description`,
which uses the values stored in `rank` and `suit` to build
a description of the name and value of the card.
The `description` property uses optional binding to check whether there's
a second value to display, and if so,
inserts additional description detail for that second value.
Because `BlackjackCard` is a structure with no custom initializers,
it has an implicit memberwise initializer,
as described in <doc:Initialization#Memberwise-Initializers-for-Structure-Types>.
You can use this initializer to initialize a new constant called `theAceOfSpades`:
```swift
let theAceOfSpades = BlackjackCard(rank: .ace, suit: .spades)
print("theAceOfSpades: \(theAceOfSpades.description)")
// Prints "theAceOfSpades: suit is ♠, value is 1 or 11".
```
<!--
- test: `nestedTypes`
```swifttest
-> let theAceOfSpades = BlackjackCard(rank: .ace, suit: .spades)
-> print("theAceOfSpades: \(theAceOfSpades.description)")
<- theAceOfSpades: suit is ♠, value is 1 or 11
```
-->
Even though `Rank` and `Suit` are nested within `BlackjackCard`,
their type can be inferred from context,
and so the initialization of this instance is able to refer to the enumeration cases
by their case names (`.ace` and `.spades`) alone.
In the example above, the `description` property correctly reports that
the Ace of Spades has a value of `1` or `11`.
## Referring to Nested Types
To use a nested type outside of its definition context,
prefix its name with the name of the type it's nested within:
```swift
let heartsSymbol = BlackjackCard.Suit.hearts.rawValue
// heartsSymbol is "♡"
```
<!--
- test: `nestedTypes`
```swifttest
-> let heartsSymbol = BlackjackCard.Suit.hearts.rawValue
/> heartsSymbol is \"\(heartsSymbol)\"
</ heartsSymbol is "♡"
```
-->
For the example above,
this enables the names of `Suit`, `Rank`, and `Values` to be kept deliberately short,
because their names are naturally qualified by the context in which they're defined.
<!--
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
-->

View File

@@ -0,0 +1,993 @@
# Opaque and Boxed Protocol Types
Hide implementation details about a value's type.
Swift provides two ways to hide details about a value's type:
opaque types and boxed protocol types.
Hiding type information
is useful at boundaries between
a module and code that calls into the module,
because the underlying type of the return value can remain private.
A function or method that returns an opaque type
hides its return value's type information.
Instead of providing a concrete type as the function's return type,
the return value is described in terms of the protocols it supports.
Opaque types preserve type identity ---
the compiler has access to the type information,
but clients of the module don't.
A boxed protocol type can store an instance of any type
that conforms to the given protocol.
Boxed protocol types don't preserve type identity ---
the value's specific type isn't known until runtime,
and it can change over time as different values are stored.
## The Problem that Opaque Types Solve
For example,
suppose you're writing a module that draws ASCII art shapes.
The basic characteristic of an ASCII art shape
is a `draw()` function that returns the string representation of that shape,
which you can use as the requirement for the `Shape` protocol:
```swift
protocol Shape {
func draw() -> String
}
struct Triangle: Shape {
var size: Int
func draw() -> String {
var result: [String] = []
for length in 1...size {
result.append(String(repeating: "*", count: length))
}
return result.joined(separator: "\n")
}
}
let smallTriangle = Triangle(size: 3)
print(smallTriangle.draw())
// *
// **
// ***
```
<!--
- test: `opaque-result`
```swifttest
-> protocol Shape {
func draw() -> String
}
-> struct Triangle: Shape {
var size: Int
func draw() -> String {
var result: [String] = []
for length in 1...size {
result.append(String(repeating: "*", count: length))
}
return result.joined(separator: "\n")
}
}
-> let smallTriangle = Triangle(size: 3)
-> print(smallTriangle.draw())
</ *
</ **
</ ***
```
-->
You could use generics to implement operations like flipping a shape vertically,
as shown in the code below.
However, there's an important limitation to this approach:
The flipped result exposes the exact generic types
that were used to create it.
```swift
struct FlippedShape<T: Shape>: Shape {
var shape: T
func draw() -> String {
let lines = shape.draw().split(separator: "\n")
return lines.reversed().joined(separator: "\n")
}
}
let flippedTriangle = FlippedShape(shape: smallTriangle)
print(flippedTriangle.draw())
// ***
// **
// *
```
<!--
- test: `opaque-result`
```swifttest
-> struct FlippedShape<T: Shape>: Shape {
var shape: T
func draw() -> String {
let lines = shape.draw().split(separator: "\n")
return lines.reversed().joined(separator: "\n")
}
}
-> let flippedTriangle = FlippedShape(shape: smallTriangle)
-> print(flippedTriangle.draw())
</ ***
</ **
</ *
```
-->
This approach to defining a `JoinedShape<T: Shape, U: Shape>` structure
that joins two shapes together vertically, like the code below shows,
results in types like `JoinedShape<Triangle, FlippedShape<Triangle>>`
from joining a triangle with a flipped triangle.
```swift
struct JoinedShape<T: Shape, U: Shape>: Shape {
var top: T
var bottom: U
func draw() -> String {
return top.draw() + "\n" + bottom.draw()
}
}
let joinedTriangles = JoinedShape(top: smallTriangle, bottom: flippedTriangle)
print(joinedTriangles.draw())
// *
// **
// ***
// ***
// **
// *
```
<!--
- test: `opaque-result`
```swifttest
-> struct JoinedShape<T: Shape, U: Shape>: Shape {
var top: T
var bottom: U
func draw() -> String {
return top.draw() + "\n" + bottom.draw()
}
}
-> let joinedTriangles = JoinedShape(top: smallTriangle, bottom: flippedTriangle)
-> print(joinedTriangles.draw())
</ *
</ **
</ ***
</ ***
</ **
</ *
```
-->
Exposing detailed information about the creation of a shape
allows types that aren't meant to be
part of the ASCII art module's public interface
to leak out because of the need to state the full return type.
The code inside the module
could build up the same shape in a variety of ways,
and other code outside the module
that uses the shape shouldn't have to account for
the implementation details about the list of transformations.
Wrapper types like `JoinedShape` and `FlippedShape`
don't matter to the module's users,
and they shouldn't be visible.
The module's public interface
consists of operations like joining and flipping a shape,
and those operations return another `Shape` value.
## Returning an Opaque Type
You can think of an opaque type like being the reverse of a generic type.
Generic types let the code that calls a function
pick the type for that function's parameters and return value
in a way that's abstracted away from the function implementation.
For example, the function in the following code
returns a type that depends on its caller:
```swift
func max<T>(_ x: T, _ y: T) -> T where T: Comparable { ... }
```
<!--
From https://developer.apple.com/documentation/swift/1538951-max
Not test code because it won't actually compile
and there's nothing to meaningfully test.
-->
The code that calls `max(_:_:)` chooses the values for `x` and `y`,
and the type of those values determines the concrete type of `T`.
The calling code can use any type
that conforms to the `Comparable` protocol.
The code inside the function is written in a general way
so it can handle whatever type the caller provides.
The implementation of `max(_:_:)` uses only functionality
that all `Comparable` types share.
Those roles are reversed for a function with an opaque return type.
An opaque type lets the function implementation
pick the type for the value it returns
in a way that's abstracted away from the code that calls the function.
For example, the function in the following example returns a trapezoid
without exposing the underlying type of that shape.
```swift
struct Square: Shape {
var size: Int
func draw() -> String {
let line = String(repeating: "*", count: size)
let result = Array<String>(repeating: line, count: size)
return result.joined(separator: "\n")
}
}
func makeTrapezoid() -> some Shape {
let top = Triangle(size: 2)
let middle = Square(size: 2)
let bottom = FlippedShape(shape: top)
let trapezoid = JoinedShape(
top: top,
bottom: JoinedShape(top: middle, bottom: bottom)
)
return trapezoid
}
let trapezoid = makeTrapezoid()
print(trapezoid.draw())
// *
// **
// **
// **
// **
// *
```
<!--
- test: `opaque-result`
```swifttest
-> struct Square: Shape {
var size: Int
func draw() -> String {
let line = String(repeating: "*", count: size)
let result = Array<String>(repeating: line, count: size)
return result.joined(separator: "\n")
}
}
-> func makeTrapezoid() -> some Shape {
let top = Triangle(size: 2)
let middle = Square(size: 2)
let bottom = FlippedShape(shape: top)
let trapezoid = JoinedShape(
top: top,
bottom: JoinedShape(top: middle, bottom: bottom)
)
return trapezoid
}
-> let trapezoid = makeTrapezoid()
-> print(trapezoid.draw())
</ *
</ **
</ **
</ **
</ **
</ *
```
-->
The `makeTrapezoid()` function in this example
declares its return type as `some Shape`;
as a result, the function
returns a value of some given type that conforms to the `Shape` protocol,
without specifying any particular concrete type.
Writing `makeTrapezoid()` this way lets it express
the fundamental aspect of its public interface ---
the value it returns is a shape ---
without making the specific types that the shape is made from
a part of its public interface.
This implementation uses two triangles and a square,
but the function could be rewritten to draw a trapezoid
in a variety of other ways
without changing its return type.
This example highlights the way that an opaque return type
is like the reverse of a generic type.
The code inside `makeTrapezoid()` can return any type it needs to,
as long as that type conforms to the `Shape` protocol,
like the calling code does for a generic function.
The code that calls the function needs to be written in a general way,
like the implementation of a generic function,
so that it can work with any `Shape` value
that's returned by `makeTrapezoid()`.
You can also combine opaque return types with generics.
The functions in the following code both return a value
of some type that conforms to the `Shape` protocol.
```swift
func flip<T: Shape>(_ shape: T) -> some Shape {
return FlippedShape(shape: shape)
}
func join<T: Shape, U: Shape>(_ top: T, _ bottom: U) -> some Shape {
JoinedShape(top: top, bottom: bottom)
}
let opaqueJoinedTriangles = join(smallTriangle, flip(smallTriangle))
print(opaqueJoinedTriangles.draw())
// *
// **
// ***
// ***
// **
// *
```
<!--
- test: `opaque-result`
```swifttest
-> func flip<T: Shape>(_ shape: T) -> some Shape {
return FlippedShape(shape: shape)
}
-> func join<T: Shape, U: Shape>(_ top: T, _ bottom: U) -> some Shape {
JoinedShape(top: top, bottom: bottom)
}
-> let opaqueJoinedTriangles = join(smallTriangle, flip(smallTriangle))
-> print(opaqueJoinedTriangles.draw())
</ *
</ **
</ ***
</ ***
</ **
</ *
```
-->
The value of `opaqueJoinedTriangles` in this example
is the same as `joinedTriangles` in the generics example
in the <doc:OpaqueTypes#The-Problem-that-Opaque-Types-Solve> section earlier in this chapter.
However, unlike the value in that example,
`flip(_:)` and `join(_:_:)` wrap the underlying types
that the generic shape operations return
in an opaque return type,
which prevents those types from being visible.
Both functions are generic because the types they rely on are generic,
and the type parameters to the function
pass along the type information needed by `FlippedShape` and `JoinedShape`.
If a function with an opaque return type
returns from multiple places,
all of the possible return values must have the same type.
For a generic function,
that return type can use the function's generic type parameters,
but it must still be a single type.
For example,
here's an *invalid* version of the shape-flipping function
that includes a special case for squares:
```swift
func invalidFlip<T: Shape>(_ shape: T) -> some Shape {
if shape is Square {
return shape // Error: Return types don't match.
}
return FlippedShape(shape: shape) // Error: Return types don't match.
}
```
<!--
- test: `opaque-result-err`
```swifttest
>> protocol Shape {
>> func draw() -> String
>> }
>> struct Square: Shape {
>> func draw() -> String { return "#" } // stub implementation
>> }
>> struct FlippedShape<T: Shape>: Shape {
>> var shape: T
>> func draw() -> String { return "#" } // stub implementation
>> }
-> func invalidFlip<T: Shape>(_ shape: T) -> some Shape {
if shape is Square {
return shape // Error: Return types don't match.
}
return FlippedShape(shape: shape) // Error: Return types don't match.
}
!$ error: function declares an opaque return type 'some Shape', but the return statements in its body do not have matching underlying types
!! func invalidFlip<T: Shape>(_ shape: T) -> some Shape {
!! ^ ~~~~~~~~~~
!$ note: return statement has underlying type 'T'
!! return shape // Error: Return types don't match.
!! ^
!$ note: return statement has underlying type 'FlippedShape<T>'
!! return FlippedShape(shape: shape) // Error: Return types don't match.
!! ^
```
-->
If you call this function with a `Square`, it returns a `Square`;
otherwise, it returns a `FlippedShape`.
This violates the requirement to return values of only one type
and makes `invalidFlip(_:)` invalid code.
One way to fix `invalidFlip(_:)` is to move the special case for squares
into the implementation of `FlippedShape`,
which lets this function always return a `FlippedShape` value:
```swift
struct FlippedShape<T: Shape>: Shape {
var shape: T
func draw() -> String {
if shape is Square {
return shape.draw()
}
let lines = shape.draw().split(separator: "\n")
return lines.reversed().joined(separator: "\n")
}
}
```
<!--
- test: `opaque-result-special-flip`
```swifttest
>> protocol Shape { func draw() -> String }
>> struct Square: Shape {
>> func draw() -> String { return "#" } // stub implementation
>> }
-> struct FlippedShape<T: Shape>: Shape {
var shape: T
func draw() -> String {
if shape is Square {
return shape.draw()
}
let lines = shape.draw().split(separator: "\n")
return lines.reversed().joined(separator: "\n")
}
}
```
-->
<!--
Another way to fix it is with type erasure.
Define a wrapper called AnyShape,
and wrap whatever shape you created inside invalidFlip(_:)
before returning it.
That example is long enough that it breaks the flow here.
-->
The requirement to always return a single type
doesn't prevent you from using generics in an opaque return type.
Here's an example of a function that incorporates its type parameter
into the underlying type of the value it returns:
```swift
func `repeat`<T: Shape>(shape: T, count: Int) -> some Collection {
return Array<T>(repeating: shape, count: count)
}
```
<!--
- test: `opaque-result`
```swifttest
-> func `repeat`<T: Shape>(shape: T, count: Int) -> some Collection {
return Array<T>(repeating: shape, count: count)
}
```
-->
In this case,
the underlying type of the return value
varies depending on `T`:
Whatever shape is passed it,
`repeat(shape:count:)` creates and returns an array of that shape.
Nevertheless,
the return value always has the same underlying type of `[T]`,
so it follows the requirement that functions with opaque return types
must return values of only a single type.
## Boxed Protocol Types
A boxed protocol type is also sometimes called an *existential type*,
which comes from the phrase
"there exists a type *T* such that *T* conforms to the protocol".
To make a boxed protocol type,
write `any` before the name of a protocol.
Here's an example:
```swift
struct VerticalShapes: Shape {
var shapes: [any Shape]
func draw() -> String {
return shapes.map { $0.draw() }.joined(separator: "\n\n")
}
}
let largeTriangle = Triangle(size: 5)
let largeSquare = Square(size: 5)
let vertical = VerticalShapes(shapes: [largeTriangle, largeSquare])
print(vertical.draw())
```
<!--
- test: `boxed-protocol-types`
```swifttest
>> protocol Shape {
>> func draw() -> String
>> }
>> struct Triangle: Shape {
>> var size: Int
>> func draw() -> String {
>> var result: [String] = []
>> for length in 1...size {
>> result.append(String(repeating: "*", count: length))
>> }
>> return result.joined(separator: "\n")
>> }
>> }
>> struct Square: Shape {
>> var size: Int
>> func draw() -> String {
>> let line = String(repeating: "*", count: size)
>> let result = Array<String>(repeating: line, count: size)
>> return result.joined(separator: "\n")
>> }
>
-> struct VerticalShapes: Shape {
var shapes: [any Shape]
func draw() -> String {
return shapes.map { $0.draw() }.joined(separator: "\n\n")
}
}
->
-> let largeTriangle = Triangle(size: 5)
-> let largeSquare = Square(size: 5)
-> let vertical = VerticalShapes(shapes: [largeTriangle, largeSquare])
-> print(vertical.draw())
<< *
<< **
<< ***
<< ****
<< *****
<<-
<< *****
<< *****
<< *****
<< *****
<< *****
```
-->
In the example above,
`VerticalShapes` declares the type of `shapes` as `[any Shape]` ---
an array of boxed `Shape` elements.
Each element in the array can be a different type,
and each of those types must conform to the `Shape` protocol.
To support this runtime flexibility,
Swift adds a level of indirection when necessary ---
this indirection is called a *box*,
and it has a performance cost.
Within the `VerticalShapes` type,
the code can use methods, properties, and subscripts
that are required by the `Shape` protocol.
For example, the `draw()` method of `VerticalShapes`
calls the `draw()` method on each element of the array.
This method is available because `Shape` requires a `draw()` method.
In contrast,
trying to access the `size` property of the triangle,
or any other properties or methods that aren't required by `Shape`,
produces an error.
Contrast the three types you could use for `shapes`:
- Using generics,
by writing `struct VerticalShapes<S: Shape>` and `var shapes: [S]`,
makes an array whose elements are some specific shape type,
and where the identity of that specific type
is visible to any code that interacts with the array.
- Using an opaque type,
by writing `var shapes: [some Shape]`,
makes an array whose elements are some specific shape type,
and where that specific type's identity is hidden.
- Using a boxed protocol type,
by writing `var shapes: [any Shape]`,
makes an array that can store elements of different types,
and where those types' identities are hidden.
In this case,
a boxed protocol type is the only approach
that lets callers of `VerticalShapes` mix different kinds of shapes together.
You can use an `as` cast
when you know the underlying type of a boxed value.
For example:
```swift
if let downcastTriangle = vertical.shapes[0] as? Triangle {
print(downcastTriangle.size)
}
// Prints "5".
```
For more information, see <doc:TypeCasting#Downcasting>.
## Differences Between Opaque Types and Boxed Protocol Types
Returning an opaque type looks very similar
to using a boxed protocol type as the return type of a function,
but these two kinds of return type differ in
whether they preserve type identity.
An opaque type refers to one specific type,
although the caller of the function isn't able to see which type;
a boxed protocol type can refer to any type that conforms to the protocol.
Generally speaking,
boxed protocol types give you more flexibility
about the underlying types of the values they store,
and opaque types let you make stronger guarantees
about those underlying types.
For example,
here's a version of `flip(_:)`
that uses a boxed protocol type as its return type
instead of an opaque return type:
```swift
func protoFlip<T: Shape>(_ shape: T) -> Shape {
return FlippedShape(shape: shape)
}
```
<!--
- test: `opaque-result-existential-error`
```swifttest
>> protocol Shape {
>> func draw() -> String
>> }
>> struct Triangle: Shape {
>> var size: Int
>> func draw() -> String { return "#" } // stub implementation
>> }
>> struct Square: Shape {
>> var size: Int
>> func draw() -> String { return "#" } // stub implementation
>> }
>> struct FlippedShape<T: Shape>: Shape {
>> var shape: T
>> func draw() -> String { return "#" } // stub implementation
>> }
-> func protoFlip<T: Shape>(_ shape: T) -> Shape {
return FlippedShape(shape: shape)
}
```
-->
This version of `protoFlip(_:)`
has the same body as `flip(_:)`,
and it always returns a value of the same type.
Unlike `flip(_:)`,
the value that `protoFlip(_:)` returns isn't required
to always have the same type ---
it just has to conform to the `Shape` protocol.
Put another way,
`protoFlip(_:)` makes a much looser API contract with its caller
than `flip(_:)` makes.
It reserves the flexibility to return values of multiple types:
```swift
func protoFlip<T: Shape>(_ shape: T) -> Shape {
if shape is Square {
return shape
}
return FlippedShape(shape: shape)
}
```
<!--
- test: `opaque-result-existential-error`
```swifttest
-> func protoFlip<T: Shape>(_ shape: T) -> Shape {
if shape is Square {
return shape
}
return FlippedShape(shape: shape)
}
!$ error: invalid redeclaration of 'protoFlip'
!! func protoFlip<T: Shape>(_ shape: T) -> Shape {
!! ^
!$ note: 'protoFlip' previously declared here
!! func protoFlip<T: Shape>(_ shape: T) -> Shape {
!! ^
```
-->
The revised version of the code returns
an instance of `Square` or an instance of `FlippedShape`,
depending on what shape is passed in.
Two flipped shapes returned by this function
might have completely different types.
Other valid versions of this function could return values of different types
when flipping multiple instances of the same shape.
The less specific return type information from `protoFlip(_:)` means that
many operations that depend on type information
aren't available on the returned value.
For example, it's not possible to write an `==` operator
comparing results returned by this function.
```swift
let protoFlippedTriangle = protoFlip(smallTriangle)
let sameThing = protoFlip(smallTriangle)
protoFlippedTriangle == sameThing // Error
```
<!--
- test: `opaque-result-existential-error`
```swifttest
>> let smallTriangle = Triangle(size: 3)
-> let protoFlippedTriangle = protoFlip(smallTriangle)
-> let sameThing = protoFlip(smallTriangle)
-> protoFlippedTriangle == sameThing // Error
!$ error: binary operator '==' cannot be applied to two 'any Shape' operands
!! protoFlippedTriangle == sameThing // Error
!! ~~~~~~~~~~~~~~~~~~~~ ^ ~~~~~~~~~
```
-->
The error on the last line of the example occurs for several reasons.
The immediate issue is that the `Shape` doesn't include an `==` operator
as part of its protocol requirements.
If you try adding one, the next issue you'll encounter
is that the `==` operator needs to know
the types of its left-hand and right-hand arguments.
This sort of operator usually takes arguments of type `Self`,
matching whatever concrete type adopts the protocol,
but adding a `Self` requirement to the protocol
doesn't allow for the type erasure that happens
when you use the protocol as a type.
Using a boxed protocol type as the return type for a function
gives you the flexibility to return any type that conforms to the protocol.
However, the cost of that flexibility
is that some operations aren't possible on the returned values.
The example shows how the `==` operator isn't available ---
it depends on specific type information
that isn't preserved by using a boxed protocol type.
Another problem with this approach is that the shape transformations don't nest.
The result of flipping a triangle is a value of type `Shape`,
and the `protoFlip(_:)` function takes an argument
of some type that conforms to the `Shape` protocol.
However, a value of a boxed protocol type doesn't conform to that protocol;
the value returned by `protoFlip(_:)` doesn't conform to `Shape`.
This means code like `protoFlip(protoFlip(smallTriangle))`
that applies multiple transformations is invalid
because the flipped shape isn't a valid argument to `protoFlip(_:)`.
In contrast,
opaque types preserve the identity of the underlying type.
Swift can infer associated types,
which lets you use an opaque return value
in places where a boxed protocol type can't be used as a return value.
For example,
here's a version of the `Container` protocol from <doc:Generics>:
```swift
protocol Container {
associatedtype Item
var count: Int { get }
subscript(i: Int) -> Item { get }
}
extension Array: Container { }
```
<!--
- test: `opaque-result, opaque-result-existential-error`
```swifttest
-> protocol Container {
associatedtype Item
var count: Int { get }
subscript(i: Int) -> Item { get }
}
-> extension Array: Container { }
```
-->
You can't use `Container` as the return type of a function
because that protocol has an associated type.
You also can't use it as constraint in a generic return type
because there isn't enough information outside the function body
to infer what the generic type needs to be.
```swift
// Error: Protocol with associated types can't be used as a return type.
func makeProtocolContainer<T>(item: T) -> Container {
return [item]
}
// Error: Not enough information to infer C.
func makeProtocolContainer<T, C: Container>(item: T) -> C {
return [item]
}
```
<!--
- test: `opaque-result-existential-error`
```swifttest
// Error: Protocol with associated types can't be used as a return type.
-> func makeProtocolContainer<T>(item: T) -> Container {
return [item]
}
// Error: Not enough information to infer C.
-> func makeProtocolContainer<T, C: Container>(item: T) -> C {
return [item]
}
!$ error: use of protocol 'Container' as a type must be written 'any Container'
!! func makeProtocolContainer<T>(item: T) -> Container {
!! ^~~~~~~~~
!! any Container
!$ error: cannot convert return expression of type '[T]' to return type 'C'
!! return [item]
!! ^~~~~~
!! as! C
```
-->
Using the opaque type `some Container` as a return type
expresses the desired API contract --- the function returns a container,
but declines to specify the container's type:
```swift
func makeOpaqueContainer<T>(item: T) -> some Container {
return [item]
}
let opaqueContainer = makeOpaqueContainer(item: 12)
let twelve = opaqueContainer[0]
print(type(of: twelve))
// Prints "Int".
```
<!--
- test: `opaque-result`
```swifttest
-> func makeOpaqueContainer<T>(item: T) -> some Container {
return [item]
}
-> let opaqueContainer = makeOpaqueContainer(item: 12)
-> let twelve = opaqueContainer[0]
-> print(type(of: twelve))
<- Int
```
-->
The type of `twelve` is inferred to be `Int`,
which illustrates the fact that type inference works with opaque types.
In the implementation of `makeOpaqueContainer(item:)`,
the underlying type of the opaque container is `[T]`.
In this case, `T` is `Int`,
so the return value is an array of integers
and the `Item` associated type is inferred to be `Int`.
The subscript on `Container` returns `Item`,
which means that the type of `twelve` is also inferred to be `Int`.
<!--
TODO: Expansion for the future
You can combine the flexibility of returning a value of protocol type
with the API-boundary enforcement of opaque types
by using type erasure
like the Swift standard library uses in the
`AnySequence <//apple_ref/fake/AnySequence`_ type.
protocol P { func f() -> Int }
struct AnyP: P {
var p: P
func f() -> Int { return p.f() }
}
struct P1 {
func f() -> Int { return 100 }
}
struct P2 {
func f() -> Int { return 200 }
}
func opaque(x: Int) -> some P {
let result: P
if x > 100 {
result = P1()
} else {
result = P2()
}
return AnyP(p: result)
}
-->
## Opaque Parameter Types
In addition to writing `some` to return an opaque type,
you can also write `some` in the type for a parameter
to a function, subscript, or initializer.
However, when you write `some` in a parameter type
that's just a shorter syntax for generics, not an opaque type.
For example,
both of the functions below are equivalent:
```swift
func drawTwiceGeneric<SomeShape: Shape>(_ shape: SomeShape) -> String {
let drawn = shape.draw()
return drawn + "\n" + drawn
}
func drawTwiceSome(_ shape: some Shape) -> String {
let drawn = shape.draw()
return drawn + "\n" + drawn
}
```
The `drawTwiceGeneric(_:)` function
declares a generic type parameter named `SomeShape`,
with a constraint that requires `SomeShape` to conform to the `Shape` protocol.
The `drawTwiceSome(_:)` function
uses the type `some Shape` for its argument.
This creates a new, unnamed generic type parameter for the function
with a constraint that requires the type to conform to the `Shape` protocol.
Because the generic type doesn't have a name,
you can't refer to that type elsewhere in the function.
If you write `some` before more than one parameter's type,
each of the generic types are independent.
For example:
```swift
func combine(shape s1: some Shape, with s2: some Shape) -> String {
return s1.draw() + "\n" + s2.draw()
}
combine(smallTriangle, trapezoid)
```
In the `combine(shape:with:)` function,
the types of the first and second parameter
must both conform to the `Shape` protocol,
but there's no constraint that requires them to be the same type.
When you call `combine(shape:with)`,
you can pass two different shapes ---
in this case, one triangle and one trapezoid.
Unlike the syntax for named generic type parameters,
described in <doc:Generics> chapter,
this lightweight syntax can't include
a generic `where` clause or any same-type (`==`) constraints.
In addition,
using the lightweight syntax for very complex constraints
can be hard to read.
<!--
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
-->

View File

@@ -0,0 +1,894 @@
# Optional Chaining
Access members of an optional value without unwrapping.
*Optional chaining* is a process for querying and calling
properties, methods, and subscripts on an optional that might currently be `nil`.
If the optional contains a value,
the property, method, or subscript call succeeds;
if the optional is `nil`, the property, method, or subscript call returns `nil`.
Multiple queries can be chained together,
and the entire chain fails gracefully if any link in the chain is `nil`.
> Note: Optional chaining in Swift is similar to messaging `nil` in Objective-C,
> but in a way that works for any type, and that can be checked for success or failure.
## Optional Chaining as an Alternative to Forced Unwrapping
You specify optional chaining by placing a question mark (`?`)
after the optional value on which you wish to call a property, method or subscript
if the optional is non-`nil`.
This is very similar to placing an exclamation point (`!`)
after an optional value to force the unwrapping of its value.
The main difference is that optional chaining fails gracefully when the optional is `nil`,
whereas forced unwrapping triggers a runtime error when the optional is `nil`.
To reflect the fact that optional chaining can be called on a `nil` value,
the result of an optional chaining call is always an optional value,
even if the property, method, or subscript you are querying returns a non-optional value.
You can use this optional return value to check whether
the optional chaining call was successful
(the returned optional contains a value),
or didn't succeed due to a `nil` value in the chain
(the returned optional value is `nil`).
Specifically, the result of an optional chaining call
is of the same type as the expected return value, but wrapped in an optional.
A property that normally returns an `Int` will return an `Int?`
when accessed through optional chaining.
The next several code snippets demonstrate
how optional chaining differs from forced unwrapping
and enables you to check for success.
First, two classes called `Person` and `Residence` are defined:
```swift
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
```
<!--
- test: `optionalChainingIntro, optionalChainingIntroAssert`
```swifttest
-> class Person {
var residence: Residence?
}
-> class Residence {
var numberOfRooms = 1
}
```
-->
`Residence` instances have a single `Int` property called `numberOfRooms`,
with a default value of `1`.
`Person` instances have an optional `residence` property of type `Residence?`.
If you create a new `Person` instance,
its `residence` property is default initialized to `nil`,
by virtue of being optional.
In the code below, `john` has a `residence` property value of `nil`:
```swift
let john = Person()
```
<!--
- test: `optionalChainingIntro, optionalChainingIntroAssert`
```swifttest
-> let john = Person()
```
-->
If you try to access the `numberOfRooms` property of this person's `residence`,
by placing an exclamation point after `residence` to force the unwrapping of its value,
you trigger a runtime error,
because there's no `residence` value to unwrap:
```swift
let roomCount = john.residence!.numberOfRooms
// this triggers a runtime error
```
<!--
- test: `optionalChainingIntroAssert`
```swifttest
-> let roomCount = john.residence!.numberOfRooms
xx assert
// this triggers a runtime error
```
-->
The code above succeeds when `john.residence` has a non-`nil` value
and will set `roomCount` to an `Int` value containing the appropriate number of rooms.
However, this code always triggers a runtime error when `residence` is `nil`,
as illustrated above.
Optional chaining provides an alternative way to access the value of `numberOfRooms`.
To use optional chaining, use a question mark in place of the exclamation point:
```swift
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
// Prints "Unable to retrieve the number of rooms."
```
<!--
- test: `optionalChainingIntro`
```swifttest
-> if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
<- Unable to retrieve the number of rooms.
```
-->
This tells Swift to “chain” on the optional `residence` property
and to retrieve the value of `numberOfRooms` if `residence` exists.
Because the attempt to access `numberOfRooms` has the potential to fail,
the optional chaining attempt returns a value of type `Int?`, or “optional `Int`”.
When `residence` is `nil`, as in the example above,
this optional `Int` will also be `nil`,
to reflect the fact that it was not possible to access `numberOfRooms`.
The optional `Int` is accessed through optional binding
to unwrap the integer and assign the non-optional value
to the `roomCount` constant.
Note that this is true even though `numberOfRooms` is a non-optional `Int`.
The fact that it's queried through an optional chain
means that the call to `numberOfRooms`
will always return an `Int?` instead of an `Int`.
You can assign a `Residence` instance to `john.residence`,
so that it no longer has a `nil` value:
```swift
john.residence = Residence()
```
<!--
- test: `optionalChainingIntro`
```swifttest
-> john.residence = Residence()
```
-->
`john.residence` now contains an actual `Residence` instance, rather than `nil`.
If you try to access `numberOfRooms` with the same optional chaining as before,
it will now return an `Int?` that contains
the default `numberOfRooms` value of `1`:
```swift
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
// Prints "John's residence has 1 room(s)."
```
<!--
- test: `optionalChainingIntro`
```swifttest
-> if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
<- John's residence has 1 room(s).
```
-->
## Defining Model Classes for Optional Chaining
You can use optional chaining with calls to properties, methods, and subscripts
that are more than one level deep.
This enables you to drill down into subproperties
within complex models of interrelated types,
and to check whether it's possible to access
properties, methods, and subscripts on those subproperties.
The code snippets below define four model classes
for use in several subsequent examples,
including examples of multilevel optional chaining.
These classes expand upon the `Person` and `Residence` model from above
by adding a `Room` and `Address` class,
with associated properties, methods, and subscripts.
The `Person` class is defined in the same way as before:
```swift
class Person {
var residence: Residence?
}
```
<!--
- test: `optionalChaining`
```swifttest
-> class Person {
var residence: Residence?
}
```
-->
The `Residence` class is more complex than before.
This time, the `Residence` class defines a variable property called `rooms`,
which is initialized with an empty array of type `[Room]`:
```swift
class Residence {
var rooms: [Room] = []
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
get {
return rooms[i]
}
set {
rooms[i] = newValue
}
}
func printNumberOfRooms() {
print("The number of rooms is \(numberOfRooms)")
}
var address: Address?
}
```
<!--
- test: `optionalChaining`
```swifttest
-> class Residence {
var rooms: [Room] = []
var numberOfRooms: Int {
return rooms.count
}
subscript(i: Int) -> Room {
get {
return rooms[i]
}
set {
rooms[i] = newValue
}
}
func printNumberOfRooms() {
print("The number of rooms is \(numberOfRooms)")
}
var address: Address?
}
```
-->
Because this version of `Residence` stores an array of `Room` instances,
its `numberOfRooms` property is implemented as a computed property,
not a stored property.
The computed `numberOfRooms` property simply returns
the value of the `count` property from the `rooms` array.
As a shortcut to accessing its `rooms` array,
this version of `Residence` provides a read-write subscript that provides access to
the room at the requested index in the `rooms` array.
This version of `Residence` also provides a method called `printNumberOfRooms`,
which simply prints the number of rooms in the residence.
Finally, `Residence` defines an optional property called `address`,
with a type of `Address?`.
The `Address` class type for this property is defined below.
The `Room` class used for the `rooms` array is
a simple class with one property called `name`,
and an initializer to set that property to a suitable room name:
```swift
class Room {
let name: String
init(name: String) { self.name = name }
}
```
<!--
- test: `optionalChaining`
```swifttest
-> class Room {
let name: String
init(name: String) { self.name = name }
}
```
-->
The final class in this model is called `Address`.
This class has three optional properties of type `String?`.
The first two properties, `buildingName` and `buildingNumber`,
are alternative ways to identify a particular building as part of an address.
The third property, `street`, is used to name the street for that address:
```swift
class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if let buildingNumber = buildingNumber, let street = street {
return "\(buildingNumber) \(street)"
} else if buildingName != nil {
return buildingName
} else {
return nil
}
}
}
```
<!--
- test: `optionalChaining`
```swifttest
-> class Address {
var buildingName: String?
var buildingNumber: String?
var street: String?
func buildingIdentifier() -> String? {
if let buildingNumber, let street {
return "\(buildingNumber) \(street)"
} else if buildingName != nil {
return buildingName
} else {
return nil
}
}
}
```
-->
The `Address` class also provides a method called `buildingIdentifier()`,
which has a return type of `String?`.
This method checks the properties of the address
and returns `buildingName` if it has a value,
or `buildingNumber` concatenated with `street` if both have values,
or `nil` otherwise.
## Accessing Properties Through Optional Chaining
As demonstrated in <doc:OptionalChaining#Optional-Chaining-as-an-Alternative-to-Forced-Unwrapping>,
you can use optional chaining to access a property on an optional value,
and to check if that property access is successful.
Use the classes defined above to create a new `Person` instance,
and try to access its `numberOfRooms` property as before:
```swift
let john = Person()
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
// Prints "Unable to retrieve the number of rooms."
```
<!--
- test: `optionalChaining`
```swifttest
-> let john = Person()
-> if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
<- Unable to retrieve the number of rooms.
```
-->
Because `john.residence` is `nil`,
this optional chaining call fails in the same way as before.
You can also attempt to set a property's value through optional chaining:
```swift
let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
john.residence?.address = someAddress
```
<!--
- test: `optionalChaining`
```swifttest
-> let someAddress = Address()
-> someAddress.buildingNumber = "29"
-> someAddress.street = "Acacia Road"
-> john.residence?.address = someAddress
```
-->
In this example,
the attempt to set the `address` property of `john.residence` will fail,
because `john.residence` is currently `nil`.
The assignment is part of the optional chaining,
which means none of the code on the right-hand side of the `=` operator
is evaluated.
In the previous example,
it's not easy to see that `someAddress` is never evaluated,
because accessing a constant doesn't have any side effects.
The listing below does the same assignment,
but it uses a function to create the address.
The function prints "Function was called" before returning a value,
which lets you see
whether the right-hand side of the `=` operator was evaluated.
```swift
func createAddress() -> Address {
print("Function was called.")
let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
return someAddress
}
john.residence?.address = createAddress()
```
<!--
- test: `optionalChaining`
```swifttest
-> func createAddress() -> Address {
print("Function was called.")
let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
return someAddress
}
-> john.residence?.address = createAddress()
>> let _ = createAddress()
<< Function was called.
```
-->
You can tell that the `createAddress()` function isn't called,
because nothing is printed.
## Calling Methods Through Optional Chaining
You can use optional chaining to call a method on an optional value,
and to check whether that method call is successful.
You can do this even if that method doesn't define a return value.
The `printNumberOfRooms()` method on the `Residence` class
prints the current value of `numberOfRooms`.
Here's how the method looks:
```swift
func printNumberOfRooms() {
print("The number of rooms is \(numberOfRooms)")
}
```
<!--
- test: `optionalChainingCallouts`
```swifttest
-> func printNumberOfRooms() {
>> let numberOfRooms = 3
print("The number of rooms is \(numberOfRooms)")
}
```
-->
This method doesn't specify a return type.
However, functions and methods with no return type have an implicit return type of `Void`,
as described in <doc:Functions#Functions-Without-Return-Values>.
This means that they return a value of `()`, or an empty tuple.
If you call this method on an optional value with optional chaining,
the method's return type will be `Void?`, not `Void`,
because return values are always of an optional type when called through optional chaining.
This enables you to use an `if` statement
to check whether it was possible to call the `printNumberOfRooms()` method,
even though the method doesn't itself define a return value.
Compare the return value from the `printNumberOfRooms` call against `nil`
to see if the method call was successful:
```swift
if john.residence?.printNumberOfRooms() != nil {
print("It was possible to print the number of rooms.")
} else {
print("It was not possible to print the number of rooms.")
}
// Prints "It was not possible to print the number of rooms."
```
<!--
- test: `optionalChaining`
```swifttest
-> if john.residence?.printNumberOfRooms() != nil {
print("It was possible to print the number of rooms.")
} else {
print("It was not possible to print the number of rooms.")
}
<- It was not possible to print the number of rooms.
```
-->
The same is true if you attempt to set a property through optional chaining.
The example above in <doc:OptionalChaining#Accessing-Properties-Through-Optional-Chaining>
attempts to set an `address` value for `john.residence`,
even though the `residence` property is `nil`.
Any attempt to set a property through optional chaining returns a value of type `Void?`,
which enables you to compare against `nil` to see if the property was set successfully:
```swift
if (john.residence?.address = someAddress) != nil {
print("It was possible to set the address.")
} else {
print("It was not possible to set the address.")
}
// Prints "It was not possible to set the address."
```
<!--
- test: `optionalChaining`
```swifttest
-> if (john.residence?.address = someAddress) != nil {
print("It was possible to set the address.")
} else {
print("It was not possible to set the address.")
}
<- It was not possible to set the address.
```
-->
## Accessing Subscripts Through Optional Chaining
You can use optional chaining to try to retrieve and set
a value from a subscript on an optional value,
and to check whether that subscript call is successful.
> Note: When you access a subscript on an optional value through optional chaining,
> you place the question mark *before* the subscript's brackets, not after.
> The optional chaining question mark always follows immediately after
> the part of the expression that's optional.
The example below tries to retrieve the name of
the first room in the `rooms` array of the `john.residence` property
using the subscript defined on the `Residence` class.
Because `john.residence` is currently `nil`,
the subscript call fails:
```swift
if let firstRoomName = john.residence?[0].name {
print("The first room name is \(firstRoomName).")
} else {
print("Unable to retrieve the first room name.")
}
// Prints "Unable to retrieve the first room name."
```
<!--
- test: `optionalChaining`
```swifttest
-> if let firstRoomName = john.residence?[0].name {
print("The first room name is \(firstRoomName).")
} else {
print("Unable to retrieve the first room name.")
}
<- Unable to retrieve the first room name.
```
-->
The optional chaining question mark in this subscript call
is placed immediately after `john.residence`, before the subscript brackets,
because `john.residence` is the optional value
on which optional chaining is being attempted.
Similarly, you can try to set a new value through a subscript with optional chaining:
```swift
john.residence?[0] = Room(name: "Bathroom")
```
<!--
- test: `optionalChaining`
```swifttest
-> john.residence?[0] = Room(name: "Bathroom")
```
-->
This subscript setting attempt also fails, because `residence` is currently `nil`.
If you create and assign an actual `Residence` instance to `john.residence`,
with one or more `Room` instances in its `rooms` array,
you can use the `Residence` subscript to access
the actual items in the `rooms` array through optional chaining:
```swift
let johnsHouse = Residence()
johnsHouse.rooms.append(Room(name: "Living Room"))
johnsHouse.rooms.append(Room(name: "Kitchen"))
john.residence = johnsHouse
if let firstRoomName = john.residence?[0].name {
print("The first room name is \(firstRoomName).")
} else {
print("Unable to retrieve the first room name.")
}
// Prints "The first room name is Living Room."
```
<!--
- test: `optionalChaining`
```swifttest
-> let johnsHouse = Residence()
-> johnsHouse.rooms.append(Room(name: "Living Room"))
-> johnsHouse.rooms.append(Room(name: "Kitchen"))
-> john.residence = johnsHouse
-> if let firstRoomName = john.residence?[0].name {
print("The first room name is \(firstRoomName).")
} else {
print("Unable to retrieve the first room name.")
}
<- The first room name is Living Room.
```
-->
### Accessing Subscripts of Optional Type
If a subscript returns a value of optional type ---
such as the key subscript of Swift's `Dictionary` type ---
place a question mark *after* the subscript's closing bracket
to chain on its optional return value:
```swift
var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0] += 1
testScores["Brian"]?[0] = 72
// the "Dave" array is now [91, 82, 84] and the "Bev" array is now [80, 94, 81]
```
<!--
- test: `optionalChaining`
```swifttest
-> var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
-> testScores["Dave"]?[0] = 91
-> testScores["Bev"]?[0] += 1
-> testScores["Brian"]?[0] = 72
>> let dave = "Dave"
>> let bev = "Bev"
/> the \"Dave\" array is now [\(testScores[dave]![0]), \(testScores[dave]![1]), \(testScores[dave]![2])] and the \"Bev\" array is now [\(testScores[bev]![0]), \(testScores[bev]![1]), \(testScores[bev]![2])]
</ the "Dave" array is now [91, 82, 84] and the "Bev" array is now [80, 94, 81]
```
-->
The example above defines a dictionary called `testScores`,
which contains two key-value pairs that map a `String` key to an array of `Int` values.
The example uses optional chaining to set the first item in the `"Dave"` array to `91`;
to increment the first item in the `"Bev"` array by `1`;
and to try to set the first item in an array for a key of `"Brian"`.
The first two calls succeed, because the `testScores` dictionary
contains keys for `"Dave"` and `"Bev"`.
The third call fails, because the `testScores` dictionary
doesn't contain a key for `"Brian"`.
## Linking Multiple Levels of Chaining
You can link together multiple levels of optional chaining
to drill down to properties, methods, and subscripts deeper within a model.
However, multiple levels of optional chaining
don't add more levels of optionality to the returned value.
To put it another way:
- If the type you are trying to retrieve isn't optional,
it will become optional because of the optional chaining.
- If the type you are trying to retrieve is *already* optional,
it will not become *more* optional because of the chaining.
Therefore:
- If you try to retrieve an `Int` value through optional chaining,
an `Int?` is always returned,
no matter how many levels of chaining are used.
- Similarly, if you try to retrieve an `Int?` value through optional chaining,
an `Int?` is always returned,
no matter how many levels of chaining are used.
The example below tries to access the `street` property of the `address` property
of the `residence` property of `john`.
There are *two* levels of optional chaining in use here,
to chain through the `residence` and `address` properties,
both of which are of optional type:
```swift
if let johnsStreet = john.residence?.address?.street {
print("John's street name is \(johnsStreet).")
} else {
print("Unable to retrieve the address.")
}
// Prints "Unable to retrieve the address."
```
<!--
- test: `optionalChaining`
```swifttest
-> if let johnsStreet = john.residence?.address?.street {
print("John's street name is \(johnsStreet).")
} else {
print("Unable to retrieve the address.")
}
<- Unable to retrieve the address.
```
-->
The value of `john.residence` currently contains a valid `Residence` instance.
However, the value of `john.residence.address` is currently `nil`.
Because of this, the call to `john.residence?.address?.street` fails.
Note that in the example above,
you are trying to retrieve the value of the `street` property.
The type of this property is `String?`.
The return value of `john.residence?.address?.street` is therefore also `String?`,
even though two levels of optional chaining are applied in addition to
the underlying optional type of the property.
If you set an actual `Address` instance as the value for `john.residence.address`,
and set an actual value for the address's `street` property,
you can access the value of the `street` property through multilevel optional chaining:
```swift
let johnsAddress = Address()
johnsAddress.buildingName = "The Larches"
johnsAddress.street = "Laurel Street"
john.residence?.address = johnsAddress
if let johnsStreet = john.residence?.address?.street {
print("John's street name is \(johnsStreet).")
} else {
print("Unable to retrieve the address.")
}
// Prints "John's street name is Laurel Street."
```
<!--
- test: `optionalChaining`
```swifttest
-> let johnsAddress = Address()
-> johnsAddress.buildingName = "The Larches"
-> johnsAddress.street = "Laurel Street"
-> john.residence?.address = johnsAddress
-> if let johnsStreet = john.residence?.address?.street {
print("John's street name is \(johnsStreet).")
} else {
print("Unable to retrieve the address.")
}
<- John's street name is Laurel Street.
```
-->
In this example,
the attempt to set the `address` property of `john.residence` will succeed,
because the value of `john.residence`
currently contains a valid `Residence` instance.
## Chaining on Methods with Optional Return Values
The previous example shows how to retrieve the value of
a property of optional type through optional chaining.
You can also use optional chaining to call a method that returns a value of optional type,
and to chain on that method's return value if needed.
The example below calls the `Address` class's `buildingIdentifier()` method
through optional chaining. This method returns a value of type `String?`.
As described above, the ultimate return type of this method call after optional chaining
is also `String?`:
```swift
if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
print("John's building identifier is \(buildingIdentifier).")
}
// Prints "John's building identifier is The Larches."
```
<!--
- test: `optionalChaining`
```swifttest
-> if let buildingIdentifier = john.residence?.address?.buildingIdentifier() {
print("John's building identifier is \(buildingIdentifier).")
}
<- John's building identifier is The Larches.
```
-->
If you want to perform further optional chaining on this method's return value,
place the optional chaining question mark *after* the method's parentheses:
```swift
if let beginsWithThe =
john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
if beginsWithThe {
print("John's building identifier begins with \"The\".")
} else {
print("John's building identifier doesn't begin with \"The\".")
}
}
// Prints "John's building identifier begins with "The"."
```
<!--
- test: `optionalChaining`
```swifttest
-> if let beginsWithThe =
john.residence?.address?.buildingIdentifier()?.hasPrefix("The") {
if beginsWithThe {
print("John's building identifier begins with \"The\".")
} else {
print("John's building identifier doesn't begin with \"The\".")
}
}
<- John's building identifier begins with "The".
```
-->
> Note: In the example above,
> you place the optional chaining question mark *after* the parentheses,
> because the optional value you are chaining on is
> the `buildingIdentifier()` method's return value,
> and not the `buildingIdentifier()` method itself.
<!--
TODO: add an example of chaining on a property of optional function type.
This can then be tied in to a revised description of how
the sugar for optional protocol requirements works.
-->
<!--
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
-->

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,433 @@
# Subscripts
Access the elements of a collection.
Classes, structures, and enumerations can define *subscripts*,
which are shortcuts for accessing the member elements of a collection, list, or sequence.
You use subscripts to set and retrieve values by index without needing
separate methods for setting and retrieval.
For example, you access elements in an `Array` instance as `someArray[index]`
and elements in a `Dictionary` instance as `someDictionary[key]`.
You can define multiple subscripts for a single type,
and the appropriate subscript overload to use is selected
based on the type of index value you pass to the subscript.
Subscripts aren't limited to a single dimension,
and you can define subscripts with multiple input parameters
to suit your custom type's needs.
<!--
TODO: this chapter should provide an example of subscripting an enumeration,
as per Joe Groff's example from rdar://16555559.
-->
## Subscript Syntax
Subscripts enable you to query instances of a type
by writing one or more values in square brackets after the instance name.
Their syntax is similar to both instance method syntax and computed property syntax.
You write subscript definitions with the `subscript` keyword,
and specify one or more input parameters and a return type,
in the same way as instance methods.
Unlike instance methods, subscripts can be read-write or read-only.
This behavior is communicated by a getter and setter
in the same way as for computed properties:
```swift
subscript(index: Int) -> Int {
get {
// Return an appropriate subscript value here.
}
set(newValue) {
// Perform a suitable setting action here.
}
}
```
<!--
- test: `subscriptSyntax`
```swifttest
>> class Test1 {
-> subscript(index: Int) -> Int {
get {
// Return an appropriate subscript value here.
>> return 1
}
set(newValue) {
// Perform a suitable setting action here.
}
}
>> }
```
-->
The type of `newValue` is the same as the return value of the subscript.
As with computed properties, you can choose not to specify
the setter's `(newValue)` parameter.
A default parameter called `newValue` is provided to your setter
if you don't provide one yourself.
As with read-only computed properties,
you can simplify the declaration of a read-only subscript
by removing the `get` keyword and its braces:
```swift
subscript(index: Int) -> Int {
// Return an appropriate subscript value here.
}
```
<!--
- test: `subscriptSyntax`
```swifttest
>> class Test2 {
-> subscript(index: Int) -> Int {
// Return an appropriate subscript value here.
>> return 1
}
>> }
```
-->
Here's an example of a read-only subscript implementation,
which defines a `TimesTable` structure to represent an *n*-times-table of integers:
```swift
struct TimesTable {
let multiplier: Int
subscript(index: Int) -> Int {
return multiplier * index
}
}
let threeTimesTable = TimesTable(multiplier: 3)
print("six times three is \(threeTimesTable[6])")
// Prints "six times three is 18".
```
<!--
- test: `timesTable`
```swifttest
-> struct TimesTable {
let multiplier: Int
subscript(index: Int) -> Int {
return multiplier * index
}
}
-> let threeTimesTable = TimesTable(multiplier: 3)
-> print("six times three is \(threeTimesTable[6])")
<- six times three is 18
```
-->
In this example, a new instance of `TimesTable` is created
to represent the three-times-table.
This is indicated by passing a value of `3` to the structure's `initializer`
as the value to use for the instance's `multiplier` parameter.
You can query the `threeTimesTable` instance by calling its subscript,
as shown in the call to `threeTimesTable[6]`.
This requests the sixth entry in the three-times-table,
which returns a value of `18`, or `3` times `6`.
> Note: An *n*-times-table is based on a fixed mathematical rule.
> It isn't appropriate to set `threeTimesTable[someIndex]` to a new value,
> and so the subscript for `TimesTable` is defined as a read-only subscript.
## Subscript Usage
The exact meaning of “subscript” depends on the context in which it's used.
Subscripts are typically used as a shortcut for accessing
the member elements in a collection, list, or sequence.
You are free to implement subscripts in the most appropriate way for
your particular class or structure's functionality.
For example, Swift's `Dictionary` type implements a subscript
to set and retrieve the values stored in a `Dictionary` instance.
You can set a value in a dictionary
by providing a key of the dictionary's key type within subscript brackets,
and assigning a value of the dictionary's value type to the subscript:
```swift
var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
numberOfLegs["bird"] = 2
```
<!--
- test: `dictionarySubscript`
```swifttest
-> var numberOfLegs = ["spider": 8, "ant": 6, "cat": 4]
-> numberOfLegs["bird"] = 2
```
-->
The example above defines a variable called `numberOfLegs`
and initializes it with a dictionary literal containing three key-value pairs.
The type of the `numberOfLegs` dictionary is inferred to be `[String: Int]`.
After creating the dictionary,
this example uses subscript assignment to add
a `String` key of `"bird"` and an `Int` value of `2` to the dictionary.
For more information about `Dictionary` subscripting,
see <doc:CollectionTypes#Accessing-and-Modifying-a-Dictionary>.
> Note: Swift's `Dictionary` type implements its key-value subscripting
> as a subscript that takes and returns an *optional* type.
> For the `numberOfLegs` dictionary above,
> the key-value subscript takes and returns a value of type `Int?`,
> or “optional int”.
> The `Dictionary` type uses an optional subscript type to model the fact that
> not every key will have a value, and to give a way to delete a value for a key
> by assigning a `nil` value for that key.
## Subscript Options
Subscripts can take any number of input parameters,
and these input parameters can be of any type.
Subscripts can also return a value of any type.
Like functions,
subscripts can take a varying number of parameters
and provide default values for their parameters,
as discussed in <doc:Functions#Variadic-Parameters>
and <doc:Functions#Default-Parameter-Values>.
However, unlike functions,
subscripts can't use in-out parameters.
<!--
- test: `subscripts-can-have-default-arguments`
```swifttest
>> struct Subscriptable {
>> subscript(x: Int, y: Int = 0) -> Int {
>> return 100
>> }
>> }
>> let s = Subscriptable()
>> print(s[0])
<< 100
```
-->
A class or structure can provide as many subscript implementations as it needs,
and the appropriate subscript to be used will be inferred based on
the types of the value or values that are contained within the subscript brackets
at the point that the subscript is used.
This definition of multiple subscripts is known as *subscript overloading*.
While it's most common for a subscript to take a single parameter,
you can also define a subscript with multiple parameters
if it's appropriate for your type.
The following example defines a `Matrix` structure,
which represents a two-dimensional matrix of `Double` values.
The `Matrix` structure's subscript takes two integer parameters:
```swift
struct Matrix {
let rows: Int, columns: Int
var grid: [Double]
init(rows: Int, columns: Int) {
self.rows = rows
self.columns = columns
grid = Array(repeating: 0.0, count: rows * columns)
}
func indexIsValid(row: Int, column: Int) -> Bool {
return row >= 0 && row < rows && column >= 0 && column < columns
}
subscript(row: Int, column: Int) -> Double {
get {
assert(indexIsValid(row: row, column: column), "Index out of range")
return grid[(row * columns) + column]
}
set {
assert(indexIsValid(row: row, column: column), "Index out of range")
grid[(row * columns) + column] = newValue
}
}
}
```
<!--
- test: `matrixSubscript, matrixSubscriptAssert`
```swifttest
-> struct Matrix {
let rows: Int, columns: Int
var grid: [Double]
init(rows: Int, columns: Int) {
self.rows = rows
self.columns = columns
grid = Array(repeating: 0.0, count: rows * columns)
}
func indexIsValid(row: Int, column: Int) -> Bool {
return row >= 0 && row < rows && column >= 0 && column < columns
}
subscript(row: Int, column: Int) -> Double {
get {
assert(indexIsValid(row: row, column: column), "Index out of range")
return grid[(row * columns) + column]
}
set {
assert(indexIsValid(row: row, column: column), "Index out of range")
grid[(row * columns) + column] = newValue
}
}
}
```
-->
`Matrix` provides an initializer that takes two parameters called `rows` and `columns`,
and creates an array that's large enough to store `rows * columns` values of type `Double`.
Each position in the matrix is given an initial value of `0.0`.
To achieve this, the array's size, and an initial cell value of `0.0`,
are passed to an array initializer that creates and initializes a new array of the correct size.
This initializer is described in more detail
in <doc:CollectionTypes#Creating-an-Array-with-a-Default-Value>.
You can construct a new `Matrix` instance by passing
an appropriate row and column count to its initializer:
```swift
var matrix = Matrix(rows: 2, columns: 2)
```
<!--
- test: `matrixSubscript, matrixSubscriptAssert`
```swifttest
-> var matrix = Matrix(rows: 2, columns: 2)
>> assert(matrix.grid == [0.0, 0.0, 0.0, 0.0])
```
-->
The example above creates a new `Matrix` instance with two rows and two columns.
The `grid` array for this `Matrix` instance
is effectively a flattened version of the matrix,
as read from top left to bottom right:
![](subscriptMatrix01)
Values in the matrix can be set by passing row and column values into the subscript,
separated by a comma:
```swift
matrix[0, 1] = 1.5
matrix[1, 0] = 3.2
```
<!--
- test: `matrixSubscript, matrixSubscriptAssert`
```swifttest
-> matrix[0, 1] = 1.5
>> print(matrix[0, 1])
<< 1.5
-> matrix[1, 0] = 3.2
>> print(matrix[1, 0])
<< 3.2
```
-->
These two statements call the subscript's setter to set
a value of `1.5` in the top right position of the matrix
(where `row` is `0` and `column` is `1`),
and `3.2` in the bottom left position
(where `row` is `1` and `column` is `0`):
![](subscriptMatrix02)
The `Matrix` subscript's getter and setter both contain an assertion
to check that the subscript's `row` and `column` values are valid.
To assist with these assertions,
`Matrix` includes a convenience method called `indexIsValid(row:column:)`,
which checks whether the requested `row` and `column`
are inside the bounds of the matrix:
```swift
func indexIsValid(row: Int, column: Int) -> Bool {
return row >= 0 && row < rows && column >= 0 && column < columns
}
```
<!--
- test: `matrixSubscript`
```swifttest
>> var rows = 2
>> var columns = 2
-> func indexIsValid(row: Int, column: Int) -> Bool {
return row >= 0 && row < rows && column >= 0 && column < columns
}
```
-->
An assertion is triggered if you try to access a subscript
that's outside of the matrix bounds:
```swift
let someValue = matrix[2, 2]
// This triggers an assert, because [2, 2] is outside of the matrix bounds.
```
<!--
- test: `matrixSubscriptAssert`
```swifttest
-> let someValue = matrix[2, 2]
xx assert
// This triggers an assert, because [2, 2] is outside of the matrix bounds.
```
-->
## Type Subscripts
Instance subscripts, as described above,
are subscripts that you call on an instance of a particular type.
You can also define subscripts that are called on the type itself.
This kind of subscript is called a *type subscript*.
You indicate a type subscript
by writing the `static` keyword before the `subscript` keyword.
Classes can use the `class` keyword instead,
to allow subclasses to override the superclasss implementation of that subscript.
The example below shows how you define and call a type subscript:
```swift
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
static subscript(n: Int) -> Planet {
return Planet(rawValue: n)!
}
}
let mars = Planet[4]
print(mars)
```
<!--
- test: `static-subscript`
```swifttest
-> enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
static subscript(n: Int) -> Planet {
return Planet(rawValue: n)!
}
}
-> let mars = Planet[4]
>> assert(mars == Planet.mars)
-> print(mars)
<< mars
```
-->
<!--
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
-->

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,533 @@
# Type Casting
Determine a value's runtime type and give it more specific type information.
*Type casting* is a way to check the type of an instance,
or to treat that instance as a different
superclass or subclass from somewhere else in its own class hierarchy.
Type casting in Swift is implemented with the `is` and `as` operators.
These two operators provide a simple and expressive way
to check the type of a value or cast a value to a different type.
You can also use type casting to check whether a type conforms to a protocol,
as described in <doc:Protocols#Checking-for-Protocol-Conformance>.
## Defining a Class Hierarchy for Type Casting
You can use type casting with a hierarchy of classes and subclasses
to check the type of a particular class instance
and to cast that instance to another class within the same hierarchy.
The three code snippets below define a hierarchy of classes
and an array containing instances of those classes,
for use in an example of type casting.
The first snippet defines a new base class called `MediaItem`.
This class provides basic functionality for any kind of item that appears
in a digital media library.
Specifically, it declares a `name` property of type `String`,
and an `init(name:)` initializer.
(It's assumed that all media items, including all movies and songs, will have a name.)
```swift
class MediaItem {
var name: String
init(name: String) {
self.name = name
}
}
```
<!--
- test: `typeCasting, typeCasting-err`
```swifttest
-> class MediaItem {
var name: String
init(name: String) {
self.name = name
}
}
```
-->
The next snippet defines two subclasses of `MediaItem`.
The first subclass, `Movie`, encapsulates additional information about a movie or film.
It adds a `director` property on top of the base `MediaItem` class,
with a corresponding initializer.
The second subclass, `Song`, adds an `artist` property and initializer
on top of the base class:
```swift
class Movie: MediaItem {
var director: String
init(name: String, director: String) {
self.director = director
super.init(name: name)
}
}
class Song: MediaItem {
var artist: String
init(name: String, artist: String) {
self.artist = artist
super.init(name: name)
}
}
```
<!--
- test: `typeCasting, typeCasting-err`
```swifttest
-> class Movie: MediaItem {
var director: String
init(name: String, director: String) {
self.director = director
super.init(name: name)
}
}
-> class Song: MediaItem {
var artist: String
init(name: String, artist: String) {
self.artist = artist
super.init(name: name)
}
}
```
-->
The final snippet creates a constant array called `library`,
which contains two `Movie` instances and three `Song` instances.
The type of the `library` array is inferred
by initializing it with the contents of an array literal.
Swift's type checker is able to deduce that `Movie` and `Song` have
a common superclass of `MediaItem`,
and so it infers a type of `[MediaItem]` for the `library` array:
```swift
let library = [
Movie(name: "Casablanca", director: "Michael Curtiz"),
Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
Movie(name: "Citizen Kane", director: "Orson Welles"),
Song(name: "The One And Only", artist: "Chesney Hawkes"),
Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
]
// the type of "library" is inferred to be [MediaItem]
```
<!--
- test: `typeCasting`
```swifttest
-> let library = [
Movie(name: "Casablanca", director: "Michael Curtiz"),
Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
Movie(name: "Citizen Kane", director: "Orson Welles"),
Song(name: "The One And Only", artist: "Chesney Hawkes"),
Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
]
>> print(type(of: library))
<< Array<MediaItem>
// the type of "library" is inferred to be [MediaItem]
```
-->
The items stored in `library` are still `Movie` and `Song` instances behind the scenes.
However, if you iterate over the contents of this array,
the items you receive back are typed as `MediaItem`,
and not as `Movie` or `Song`.
In order to work with them as their native type,
you need to *check* their type,
or *downcast* them to a different type,
as described below.
## Checking Type
Use the *type check operator* (`is`) to check
whether an instance is of a certain subclass type.
The type check operator returns `true` if the instance is of that subclass type
and `false` if it's not.
The example below defines two variables, `movieCount` and `songCount`,
which count the number of `Movie` and `Song` instances in the `library` array:
```swift
var movieCount = 0
var songCount = 0
for item in library {
if item is Movie {
movieCount += 1
} else if item is Song {
songCount += 1
}
}
print("Media library contains \(movieCount) movies and \(songCount) songs")
// Prints "Media library contains 2 movies and 3 songs".
```
<!--
- test: `typeCasting`
```swifttest
-> var movieCount = 0
-> var songCount = 0
-> for item in library {
if item is Movie {
movieCount += 1
} else if item is Song {
songCount += 1
}
}
-> print("Media library contains \(movieCount) movies and \(songCount) songs")
<- Media library contains 2 movies and 3 songs
```
-->
This example iterates through all items in the `library` array.
On each pass, the `for`-`in` loop sets the `item` constant
to the next `MediaItem` in the array.
`item is Movie` returns `true` if the current `MediaItem`
is a `Movie` instance and `false` if it's not.
Similarly, `item is Song` checks whether the item is a `Song` instance.
At the end of the `for`-`in` loop, the values of `movieCount` and `songCount`
contain a count of how many `MediaItem` instances were found of each type.
## Downcasting
A constant or variable of a certain class type may actually refer to
an instance of a subclass behind the scenes.
Where you believe this is the case,
you can try to *downcast* to the subclass type
with a *type cast operator* (`as?` or `as!`).
Because downcasting can fail,
the type cast operator comes in two different forms.
The conditional form, `as?`, returns an optional value of the type you are trying to downcast to.
The forced form, `as!`, attempts the downcast and force-unwraps the result
as a single compound action.
Use the conditional form of the type cast operator (`as?`)
when you aren't sure if the downcast will succeed.
This form of the operator will always return an optional value,
and the value will be `nil` if the downcast was not possible.
This enables you to check for a successful downcast.
Use the forced form of the type cast operator (`as!`)
only when you are sure that the downcast will always succeed.
This form of the operator will trigger a runtime error
if you try to downcast to an incorrect class type.
The example below iterates over each `MediaItem` in `library`,
and prints an appropriate description for each item.
To do this, it needs to access each item as a true `Movie` or `Song`,
and not just as a `MediaItem`.
This is necessary in order for it to be able to access
the `director` or `artist` property of a `Movie` or `Song`
for use in the description.
In this example, each item in the array might be a `Movie`,
or it might be a `Song`.
You don't know in advance which actual class to use for each item,
and so it's appropriate to use the conditional form of the type cast operator (`as?`)
to check the downcast each time through the loop:
```swift
for item in library {
if let movie = item as? Movie {
print("Movie: \(movie.name), dir. \(movie.director)")
} else if let song = item as? Song {
print("Song: \(song.name), by \(song.artist)")
}
}
// Movie: Casablanca, dir. Michael Curtiz
// Song: Blue Suede Shoes, by Elvis Presley
// Movie: Citizen Kane, dir. Orson Welles
// Song: The One And Only, by Chesney Hawkes
// Song: Never Gonna Give You Up, by Rick Astley
```
<!--
- test: `typeCasting`
```swifttest
-> for item in library {
if let movie = item as? Movie {
print("Movie: \(movie.name), dir. \(movie.director)")
} else if let song = item as? Song {
print("Song: \(song.name), by \(song.artist)")
}
}
</ Movie: Casablanca, dir. Michael Curtiz
</ Song: Blue Suede Shoes, by Elvis Presley
</ Movie: Citizen Kane, dir. Orson Welles
</ Song: The One And Only, by Chesney Hawkes
</ Song: Never Gonna Give You Up, by Rick Astley
```
-->
The example starts by trying to downcast the current `item` as a `Movie`.
Because `item` is a `MediaItem` instance, it's possible that it *might* be a `Movie`;
equally, it's also possible that it might be a `Song`,
or even just a base `MediaItem`.
Because of this uncertainty, the `as?` form of the type cast operator returns an *optional* value
when attempting to downcast to a subclass type.
The result of `item as? Movie` is of type `Movie?`, or “optional `Movie`”.
Downcasting to `Movie` fails when applied to
the `Song` instances in the library array.
To cope with this, the example above uses optional binding
to check whether the optional `Movie` actually contains a value
(that is, to find out whether the downcast succeeded.)
This optional binding is written “`if let movie = item as? Movie`”,
which can be read as:
“Try to access `item` as a `Movie`.
If this is successful,
set a new temporary constant called `movie` to
the value stored in the returned optional `Movie`.”
If the downcasting succeeds, the properties of `movie` are then used
to print a description for that `Movie` instance, including the name of its `director`.
A similar principle is used to check for `Song` instances,
and to print an appropriate description (including `artist` name)
whenever a `Song` is found in the library.
> Note: Casting doesn't actually modify the instance or change its values.
> The underlying instance remains the same; it's simply treated and accessed
> as an instance of the type to which it has been cast.
<!--
TODO: This example should be followed by the same example written with switch,
to introduce type casting in a pattern matching context
and to set up the crazy Any example at the end of the chapter.
-->
<!--
TODO: No section on upcasting because nobody can come up with
an example that isn't excessively contrived.
The reference shows the behavior in a contrived example.
-->
## Type Casting for Any and AnyObject
Swift provides two special types for working with nonspecific types:
- `Any` can represent an instance of any type at all, including function types.
- `AnyObject` can represent an instance of any class type.
Use `Any` and `AnyObject` only when you explicitly need
the behavior and capabilities they provide.
It's always better to be specific about the types you expect to work with in your code.
Here's an example of using `Any` to work with a mix of different types,
including function types and nonclass types.
The example creates an array called `things`, which can store values of type `Any`:
```swift
var things: [Any] = []
things.append(0)
things.append(0.0)
things.append(42)
things.append(3.14159)
things.append("hello")
things.append((3.0, 5.0))
things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
things.append({ (name: String) -> String in "Hello, \(name)" })
```
<!--
- test: `typeCasting, typeCasting-err`
```swifttest
-> var things: [Any] = []
-> things.append(0)
-> things.append(0.0)
-> things.append(42)
-> things.append(3.14159)
-> things.append("hello")
-> things.append((3.0, 5.0))
-> things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman"))
-> things.append({ (name: String) -> String in "Hello, \(name)" })
```
-->
The `things` array contains
two `Int` values, two `Double` values, a `String` value,
a tuple of type `(Double, Double)`,
the movie “Ghostbusters”,
and a closure expression that takes a `String` value
and returns another `String` value.
To discover the specific type of a constant or variable
that's known only to be of type `Any` or `AnyObject`,
you can use an `is` or `as` pattern in a `switch` statement's cases.
The example below iterates over the items in the `things` array
and queries the type of each item with a `switch` statement.
Several of the `switch` statement's cases bind their matched value to
a constant of the specified type to enable its value to be printed:
```swift
for thing in things {
switch thing {
case 0 as Int:
print("zero as an Int")
case 0 as Double:
print("zero as a Double")
case let someInt as Int:
print("an integer value of \(someInt)")
case let someDouble as Double where someDouble > 0:
print("a positive double value of \(someDouble)")
case is Double:
print("some other double value that I don't want to print")
case let someString as String:
print("a string value of \"\(someString)\"")
case let (x, y) as (Double, Double):
print("an (x, y) point at \(x), \(y)")
case let movie as Movie:
print("a movie called \(movie.name), dir. \(movie.director)")
case let stringConverter as (String) -> String:
print(stringConverter("Michael"))
default:
print("something else")
}
}
// zero as an Int
// zero as a Double
// an integer value of 42
// a positive double value of 3.14159
// a string value of "hello"
// an (x, y) point at 3.0, 5.0
// a movie called Ghostbusters, dir. Ivan Reitman
// Hello, Michael
```
<!--
- test: `typeCasting`
```swifttest
-> for thing in things {
switch thing {
case 0 as Int:
print("zero as an Int")
case 0 as Double:
print("zero as a Double")
case let someInt as Int:
print("an integer value of \(someInt)")
case let someDouble as Double where someDouble > 0:
print("a positive double value of \(someDouble)")
case is Double:
print("some other double value that I don't want to print")
case let someString as String:
print("a string value of \"\(someString)\"")
case let (x, y) as (Double, Double):
print("an (x, y) point at \(x), \(y)")
case let movie as Movie:
print("a movie called \(movie.name), dir. \(movie.director)")
case let stringConverter as (String) -> String:
print(stringConverter("Michael"))
default:
print("something else")
}
}
</ zero as an Int
</ zero as a Double
</ an integer value of 42
</ a positive double value of 3.14159
</ a string value of "hello"
</ an (x, y) point at 3.0, 5.0
</ a movie called Ghostbusters, dir. Ivan Reitman
</ Hello, Michael
```
-->
> Note: The `Any` type represents values of any type, including optional types.
> Swift gives you a warning if you use an optional value
> where a value of type `Any` is expected.
> If you really do need to use an optional value as an `Any` value,
> you can use the `as` operator to explicitly cast the optional to `Any`,
> as shown below.
>
> ```swift
> let optionalNumber: Int? = 3
> things.append(optionalNumber) // Warning
> things.append(optionalNumber as Any) // No warning
> ```
<!--
- test: `typeCasting-err`
```swifttest
-> let optionalNumber: Int? = 3
-> things.append(optionalNumber) // Warning
!$ warning: expression implicitly coerced from 'Int?' to 'Any'
!! things.append(optionalNumber) // Warning
!! ^~~~~~~~~~~~~~
!$ note: provide a default value to avoid this warning
!! things.append(optionalNumber) // Warning
!! ^~~~~~~~~~~~~~
!! ?? <#default value#>
!$ note: force-unwrap the value to avoid this warning
!! things.append(optionalNumber) // Warning
!! ^~~~~~~~~~~~~~
!! !
!$ note: explicitly cast to 'Any' with 'as Any' to silence this warning
!! things.append(optionalNumber) // Warning
!! ^~~~~~~~~~~~~~
!! as Any
-> things.append(optionalNumber as Any) // No warning
```
-->
<!--
Rejected examples to illustrate AnyObject:
Array of delegates which may conform to one or more of the class's delegate protocols.
```
protocol MovieDelegate {
func willPlay(movie: Movie)
}
class Library {
var delegates = [AnyObject]
...
}
for delegate in delegates {
guard let delegate = delegate as MovieDelegate else { continue }
delegate.willPlay(movie: m)
}
```
A userData object for associating some opaque piece of data or state with an API call.
```
class C {
// Not userInfo -- that's usually a Dictionary
let userData: AnyObject? // In Cocoa APIs, userData is a void*
}
```
-->
<!--
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
-->

View File

@@ -0,0 +1,59 @@
# About the Language Reference
Read the notation that the formal grammar uses.
This part of the book describes the formal grammar of the Swift programming language.
The grammar described here is intended to help you understand the language in more
detail, rather than to allow you to directly implement a parser or compiler.
The Swift language is relatively small, because many common types, functions, and operators
that appear virtually everywhere in Swift code
are actually defined in the Swift standard library. Although these types, functions,
and operators aren't part of the Swift language itself,
they're used extensively in the discussions and code examples in this part of the book.
## How to Read the Grammar
The notation used to describe the formal grammar of the Swift programming language
follows a few conventions:
- An arrow (→) is used to mark grammar productions and can be read as "can consist of."
- Syntactic categories are indicated by *italic* text and appear on both sides
of a grammar production rule.
- Literal words and punctuation are indicated by **`boldface constant width`** text
and appear only on the right-hand side of a grammar production rule.
- Alternative grammar productions are separated by vertical
bars (|). When alternative productions are too long to read easily,
they're broken into multiple grammar production rules on new lines.
- In a few cases, regular font text is used to describe the right-hand side
of a grammar production rule.
- Optional syntactic categories and literals are marked by a trailing
question mark, *?*.
As an example, the grammar of a getter-setter block is defined as follows:
> Grammar of a getter-setter block:
>
> *getter-setter-block* → **`{`** *getter-clause* *setter-clause*_?_ **`}`** | **`{`** *setter-clause* *getter-clause* **`}`**
This definition indicates that a getter-setter block can consist of a getter clause
followed by an optional setter clause, enclosed in braces,
*or* a setter clause followed by a getter clause, enclosed in braces.
The grammar production above is equivalent to the following two productions,
where the alternatives are spelled out explicitly:
> Grammar of a getter-setter block:
>
>
> *getter-setter-block* → **`{`** *getter-clause* *setter-clause*_?_ **`}`** \
> *getter-setter-block* → **`{`** *setter-clause* *getter-clause* **`}`**
<!--
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
-->

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,372 @@
# 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
-->

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,500 @@
# Patterns
Match and destructure values.
A *pattern* represents the structure of a single value
or a composite value.
For example, the structure of a tuple `(1, 2)` is a comma-separated list of two
elements. Because patterns represent the structure of a value rather than any
one particular value, you can match them with a variety of values.
For instance, the pattern `(x, y)` matches the tuple `(1, 2)` and any other
two-element tuple. In addition to matching a pattern with a value,
you can extract part or all of a composite value and bind each part
to a constant or variable name.
In Swift, there are two basic kinds of patterns:
those that successfully match any kind of value,
and those that may fail to match a specified value at runtime.
The first kind of pattern is used for destructuring values
in simple variable, constant, and optional bindings.
These include wildcard patterns, identifier patterns,
and any value binding or tuple patterns containing
them. You can specify a type annotation for these patterns
to constrain them to match only values of a certain type.
The second kind of pattern is used for full pattern matching,
where the values you're trying to match against may not be there at runtime.
These include enumeration case patterns, optional patterns, expression patterns,
and type-casting patterns. You use these patterns in a case label of a `switch`
statement, a `catch` clause of a `do` statement,
or in the case condition of an `if`, `while`,
`guard`, or `for`-`in` statement.
> Grammar of a pattern:
>
> *pattern* → *wildcard-pattern* *type-annotation*_?_ \
> *pattern* → *identifier-pattern* *type-annotation*_?_ \
> *pattern* → *value-binding-pattern* \
> *pattern* → *tuple-pattern* *type-annotation*_?_ \
> *pattern* → *enum-case-pattern* \
> *pattern* → *optional-pattern* \
> *pattern* → *type-casting-pattern* \
> *pattern* → *expression-pattern*
## Wildcard Pattern
A *wildcard pattern* matches and ignores any value and consists of an underscore
(`_`). Use a wildcard pattern when you don't care about the values being
matched against. For example, the following code iterates through the closed range `1...3`,
ignoring the current value of the range on each iteration of the loop:
```swift
for _ in 1...3 {
// Do something three times.
}
```
<!--
- test: `wildcard-pattern`
```swifttest
-> for _ in 1...3 {
// Do something three times.
}
```
-->
> Grammar of a wildcard pattern:
>
> *wildcard-pattern* → **`_`**
## Identifier Pattern
An *identifier pattern* matches any value and binds the matched value to a
variable or constant name.
For example, in the following constant declaration, `someValue` is an identifier pattern
that matches the value `42` of type `Int`:
```swift
let someValue = 42
```
<!--
- test: `identifier-pattern`
```swifttest
-> let someValue = 42
```
-->
When the match succeeds, the value `42` is bound (assigned)
to the constant name `someValue`.
When the pattern on the left-hand side of a variable or constant declaration
is an identifier pattern,
the identifier pattern is implicitly a subpattern of a value-binding pattern.
> Grammar of an identifier pattern:
>
> *identifier-pattern* → *identifier*
## Value-Binding Pattern
A *value-binding pattern* binds matched values to variable or constant names.
Value-binding patterns that bind a matched value to the name of a constant
begin with the `let` keyword; those that bind to the name of variable
begin with the `var` keyword.
Identifiers patterns within a value-binding pattern
bind new named variables or constants to their matching values. For example,
you can decompose the elements of a tuple and bind the value of each element to a
corresponding identifier pattern.
```swift
let point = (3, 2)
switch point {
// Bind x and y to the elements of point.
case let (x, y):
print("The point is at (\(x), \(y)).")
}
// Prints "The point is at (3, 2)."
```
<!--
- test: `value-binding-pattern`
```swifttest
-> let point = (3, 2)
-> switch point {
// Bind x and y to the elements of point.
case let (x, y):
print("The point is at (\(x), \(y)).")
}
<- The point is at (3, 2).
```
-->
In the example above, `let` distributes to each identifier pattern in the
tuple pattern `(x, y)`. Because of this behavior, the `switch` cases
`case let (x, y):` and `case (let x, let y):` match the same values.
> Grammar of a value-binding pattern:
>
> *value-binding-pattern* → **`var`** *pattern* | **`let`** *pattern*
<!--
NOTE: We chose to call this "value-binding pattern"
instead of "variable pattern",
because it's a pattern that binds values to either variables or constants,
not a pattern that varies.
"Variable pattern" is ambiguous between those two meanings.
-->
## Tuple Pattern
A *tuple pattern* is a comma-separated list of zero or more patterns, enclosed in
parentheses. Tuple patterns match values of corresponding tuple types.
You can constrain a tuple pattern to match certain kinds of tuple types
by using type annotations.
For example, the tuple pattern `(x, y): (Int, Int)` in the constant declaration
`let (x, y): (Int, Int) = (1, 2)` matches only tuple types in which
both elements are of type `Int`.
When a tuple pattern is used as the pattern in a `for`-`in` statement
or in a variable or constant declaration, it can contain only wildcard patterns,
identifier patterns, optional patterns, or other tuple patterns that contain those.
For example,
the following code isn't valid because the element `0` in the tuple pattern `(x, 0)` is
an expression pattern:
```swift
let points = [(0, 0), (1, 0), (1, 1), (2, 0), (2, 1)]
// This code isn't valid.
for (x, 0) in points {
/* ... */
}
```
<!--
- test: `tuple-pattern`
```swifttest
-> let points = [(0, 0), (1, 0), (1, 1), (2, 0), (2, 1)]
-> // This code isn't valid.
-> for (x, 0) in points {
>> _ = x
/* ... */
}
!$ error: expected pattern
!! for (x, 0) in points {
!! ^
```
-->
The parentheses around a tuple pattern that contains a single element have no effect.
The pattern matches values of that single element's type. For example, the following are
equivalent:
<!--
This test needs to be compiled.
The error message in the REPL is unpredictable as of
Swift version 1.1 (swift-600.0.54.20)
-->
```swift
let a = 2 // a: Int = 2
let (a) = 2 // a: Int = 2
let (a): Int = 2 // a: Int = 2
```
<!--
- test: `single-element-tuple-pattern`
```swifttest
-> let a = 2 // a: Int = 2
-> let (a) = 2 // a: Int = 2
-> let (a): Int = 2 // a: Int = 2
!$ error: invalid redeclaration of 'a'
!! let (a) = 2 // a: Int = 2
!! ^
!$ note: 'a' previously declared here
!! let a = 2 // a: Int = 2
!! ^
!$ error: invalid redeclaration of 'a'
!! let (a): Int = 2 // a: Int = 2
!! ^
!$ note: 'a' previously declared here
!! let a = 2 // a: Int = 2
!! ^
```
-->
> Grammar of a tuple pattern:
>
> *tuple-pattern* → **`(`** *tuple-pattern-element-list*_?_ **`)`** \
> *tuple-pattern-element-list* → *tuple-pattern-element* | *tuple-pattern-element* **`,`** *tuple-pattern-element-list* \
> *tuple-pattern-element* → *pattern* | *identifier* **`:`** *pattern*
## Enumeration Case Pattern
An *enumeration case pattern* matches a case of an existing enumeration type.
Enumeration case patterns appear in `switch` statement
case labels and in the case conditions of `if`, `while`, `guard`, and `for`-`in`
statements.
If the enumeration case you're trying to match has any associated values,
the corresponding enumeration case pattern must specify a tuple pattern that contains
one element for each associated value. For an example that uses a `switch` statement
to match enumeration cases containing associated values,
see <doc:Enumerations#Associated-Values>.
An enumeration case pattern also matches
values of that case wrapped in an optional.
This simplified syntax lets you omit an optional pattern.
Note that,
because `Optional` is implemented as an enumeration,
`.none` and `.some` can appear
in the same switch as the cases of the enumeration type.
```swift
enum SomeEnum { case left, right }
let x: SomeEnum? = .left
switch x {
case .left:
print("Turn left")
case .right:
print("Turn right")
case nil:
print("Keep going straight")
}
// Prints "Turn left".
```
<!--
- test: `enum-pattern-matching-optional`
```swifttest
-> enum SomeEnum { case left, right }
-> let x: SomeEnum? = .left
-> switch x {
case .left:
print("Turn left")
case .right:
print("Turn right")
case nil:
print("Keep going straight")
}
<- Turn left
```
-->
> Grammar of an enumeration case pattern:
>
> *enum-case-pattern* → *type-identifier*_?_ **`.`** *enum-case-name* *tuple-pattern*_?_
## Optional Pattern
An *optional pattern* matches values wrapped in a `some(Wrapped)` case
of an `Optional<Wrapped>` enumeration.
Optional patterns consist of an identifier pattern followed immediately by a question mark
and appear in the same places as enumeration case patterns.
Because optional patterns are syntactic sugar for `Optional`
enumeration case patterns,
the following are equivalent:
```swift
let someOptional: Int? = 42
// Match using an enumeration case pattern.
if case .some(let x) = someOptional {
print(x)
}
// Match using an optional pattern.
if case let x? = someOptional {
print(x)
}
```
<!--
- test: `optional-pattern`
```swifttest
-> let someOptional: Int? = 42
-> // Match using an enumeration case pattern.
-> if case .some(let x) = someOptional {
print(x)
}
<< 42
-> // Match using an optional pattern.
-> if case let x? = someOptional {
print(x)
}
<< 42
```
-->
The optional pattern provides a convenient way to
iterate over an array of optional values in a `for`-`in` statement,
executing the body of the loop only for non-`nil` elements.
```swift
let arrayOfOptionalInts: [Int?] = [nil, 2, 3, nil, 5]
// Match only non-nil values.
for case let number? in arrayOfOptionalInts {
print("Found a \(number)")
}
// Found a 2
// Found a 3
// Found a 5
```
<!--
- test: `optional-pattern-for-in`
```swifttest
-> let arrayOfOptionalInts: [Int?] = [nil, 2, 3, nil, 5]
-> // Match only non-nil values.
-> for case let number? in arrayOfOptionalInts {
print("Found a \(number)")
}
</ Found a 2
</ Found a 3
</ Found a 5
```
-->
> Grammar of an optional pattern:
>
> *optional-pattern* → *identifier-pattern* **`?`**
## Type-Casting Patterns
There are two type-casting patterns, the `is` pattern and the `as` pattern.
The `is` pattern appears only in `switch` statement
case labels. The `is` and `as` patterns have the following form:
```swift
is <#type#>
<#pattern#> as <#type#>
```
The `is` pattern matches a value if the type of that value at runtime is the same as
the type specified in the right-hand side of the `is` pattern --- or a subclass of that type.
The `is` pattern behaves like the `is` operator in that they both perform a type cast
but discard the returned type.
The `as` pattern matches a value if the type of that value at runtime is the same as
the type specified in the right-hand side of the `as` pattern --- or a subclass of that type.
If the match succeeds,
the type of the matched value is cast to the *pattern* specified in the right-hand side
of the `as` pattern.
For an example that uses a `switch` statement
to match values with `is` and `as` patterns,
see <doc:TypeCasting#Type-Casting-for-Any-and-AnyObject>.
> Grammar of a type casting pattern:
>
> *type-casting-pattern* → *is-pattern* | *as-pattern* \
> *is-pattern* → **`is`** *type* \
> *as-pattern* → *pattern* **`as`** *type*
## Expression Pattern
An *expression pattern* represents the value of an expression.
Expression patterns appear only in `switch` statement
case labels.
The expression represented by the expression pattern
is compared with the value of an input expression
using the pattern-matching operator (`~=`) from the Swift standard library.
The matches succeeds
if the `~=` operator returns `true`. By default, the `~=` operator compares
two values of the same type using the `==` operator.
It can also match a value with a range of values,
by checking whether the value is contained within the range,
as the following example shows.
```swift
let point = (1, 2)
switch point {
case (0, 0):
print("(0, 0) is at the origin.")
case (-2...2, -2...2):
print("(\(point.0), \(point.1)) is near the origin.")
default:
print("The point is at (\(point.0), \(point.1)).")
}
// Prints "(1, 2) is near the origin."
```
<!--
- test: `expression-pattern`
```swifttest
-> let point = (1, 2)
-> switch point {
case (0, 0):
print("(0, 0) is at the origin.")
case (-2...2, -2...2):
print("(\(point.0), \(point.1)) is near the origin.")
default:
print("The point is at (\(point.0), \(point.1)).")
}
<- (1, 2) is near the origin.
```
-->
You can overload the `~=` operator to provide custom expression matching behavior.
For example, you can rewrite the above example to compare the `point` expression
with a string representations of points.
```swift
// Overload the ~= operator to match a string with an integer.
func ~= (pattern: String, value: Int) -> Bool {
return pattern == "\(value)"
}
switch point {
case ("0", "0"):
print("(0, 0) is at the origin.")
default:
print("The point is at (\(point.0), \(point.1)).")
}
// Prints "The point is at (1, 2)."
```
<!--
- test: `expression-pattern`
```swifttest
-> // Overload the ~= operator to match a string with an integer.
-> func ~= (pattern: String, value: Int) -> Bool {
return pattern == "\(value)"
}
-> switch point {
case ("0", "0"):
print("(0, 0) is at the origin.")
default:
print("The point is at (\(point.0), \(point.1)).")
}
<- The point is at (1, 2).
```
-->
> Grammar of an expression pattern:
>
> *expression-pattern* → *expression*
<!--
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
-->

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,73 @@
---
name: programming-swift
description: Provides the complete content of 'The Swift Programming Language (6.2.1)' book by Apple. Use this skill when you need to verify Swift syntax, look up language features, understand concurrency, resolve compiler errors, or consult the formal language reference.
---
# The Swift Programming Language
The entire content of The Swift Programming Language (6.2.1) book by Apple. This is a comprehensive language reference and guide to the Swift programming language.
## Documentation Structure
### Getting Started (GuidedTour)
- **About Swift** ([GuidedTour/AboutSwift.md](GuidedTour/AboutSwift.md)): Understand the high-level goals of the language.
- **Version Compatibility** ([GuidedTour/Compatibility.md](GuidedTour/Compatibility.md)): Learn what functionality is available in older language modes.
- **A Swift Tour** ([GuidedTour/GuidedTour.md](GuidedTour/GuidedTour.md)): Explore the features and syntax of Swift.
### Language Guide
- **The Basics** ([LanguageGuide/TheBasics.md](LanguageGuide/TheBasics.md)): Work with common kinds of data and write basic syntax.
- **Basic Operators** ([LanguageGuide/BasicOperators.md](LanguageGuide/BasicOperators.md)): Perform operations like assignment, arithmetic, and comparison.
- **Strings and Characters** ([LanguageGuide/StringsAndCharacters.md](LanguageGuide/StringsAndCharacters.md)): Store and manipulate text.
- **Collection Types** ([LanguageGuide/CollectionTypes.md](LanguageGuide/CollectionTypes.md)): Organize data using arrays, sets, and dictionaries.
- **Control Flow** ([LanguageGuide/ControlFlow.md](LanguageGuide/ControlFlow.md)): Structure code with branches, loops, and early exits.
- **Functions** ([LanguageGuide/Functions.md](LanguageGuide/Functions.md)): Define and call functions, label their arguments, and use their return values.
- **Closures** ([LanguageGuide/Closures.md](LanguageGuide/Closures.md)): Group code that executes together, without creating a named function.
- **Enumerations** ([LanguageGuide/Enumerations.md](LanguageGuide/Enumerations.md)): Model custom types that define a list of possible values.
- **Structures and Classes** ([LanguageGuide/ClassesAndStructures.md](LanguageGuide/ClassesAndStructures.md)): Model custom types that encapsulate data.
- **Properties** ([LanguageGuide/Properties.md](LanguageGuide/Properties.md)): Access stored and computed values that are part of an instance or type.
- **Methods** ([LanguageGuide/Methods.md](LanguageGuide/Methods.md)): Define and call functions that are part of an instance or type.
- **Subscripts** ([LanguageGuide/Subscripts.md](LanguageGuide/Subscripts.md)): Access the elements of a collection.
- **Inheritance** ([LanguageGuide/Inheritance.md](LanguageGuide/Inheritance.md)): Subclass to add or override functionality.
- **Initialization** ([LanguageGuide/Initialization.md](LanguageGuide/Initialization.md)): Set the initial values for a type's stored properties and perform one-time setup.
- **Deinitialization** ([LanguageGuide/Deinitialization.md](LanguageGuide/Deinitialization.md)): Release resources that require custom cleanup.
- **Optional Chaining** ([LanguageGuide/OptionalChaining.md](LanguageGuide/OptionalChaining.md)): Access members of an optional value without unwrapping.
- **Error Handling** ([LanguageGuide/ErrorHandling.md](LanguageGuide/ErrorHandling.md)): Respond to and recover from errors.
- **Concurrency** ([LanguageGuide/Concurrency.md](LanguageGuide/Concurrency.md)): Perform asynchronous operations.
- **Macros** ([LanguageGuide/Macros.md](LanguageGuide/Macros.md)): Use macros to generate code at compile time.
- **Type Casting** ([LanguageGuide/TypeCasting.md](LanguageGuide/TypeCasting.md)): Determine a value's runtime type and give it more specific type information.
- **Nested Types** ([LanguageGuide/NestedTypes.md](LanguageGuide/NestedTypes.md)): Define types inside the scope of another type.
- **Extensions** ([LanguageGuide/Extensions.md](LanguageGuide/Extensions.md)): Add functionality to an existing type.
- **Protocols** ([LanguageGuide/Protocols.md](LanguageGuide/Protocols.md)): Define requirements that conforming types must implement.
- **Generics** ([LanguageGuide/Generics.md](LanguageGuide/Generics.md)): Write code that works for multiple types and specify requirements for those types.
- **Opaque and Boxed Protocol Types** ([LanguageGuide/OpaqueTypes.md](LanguageGuide/OpaqueTypes.md)): Hide implementation details about a value's type.
- **Automatic Reference Counting** ([LanguageGuide/AutomaticReferenceCounting.md](LanguageGuide/AutomaticReferenceCounting.md)): Model the lifetime of objects and their relationships.
- **Memory Safety** ([LanguageGuide/MemorySafety.md](LanguageGuide/MemorySafety.md)): Structure your code to avoid conflicts when accessing memory.
- **Access Control** ([LanguageGuide/AccessControl.md](LanguageGuide/AccessControl.md)): Manage the visibility of code by declaration, file, and module.
- **Advanced Operators** ([LanguageGuide/AdvancedOperators.md](LanguageGuide/AdvancedOperators.md)): Define custom operators, perform bitwise operations, and use builder syntax.
### Reference Manual
- **About the Language Reference** ([ReferenceManual/AboutTheLanguageReference.md](ReferenceManual/AboutTheLanguageReference.md)): Read the notation that the formal grammar uses.
- **Lexical Structure** ([ReferenceManual/LexicalStructure.md](ReferenceManual/LexicalStructure.md)): Use the lowest-level components of the syntax.
- **Types** ([ReferenceManual/Types.md](ReferenceManual/Types.md)): Use built-in named and compound types.
- **Expressions** ([ReferenceManual/Expressions.md](ReferenceManual/Expressions.md)): Access, modify, and assign values.
- **Statements** ([ReferenceManual/Statements.md](ReferenceManual/Statements.md)): Group expressions and control the flow of execution.
- **Declarations** ([ReferenceManual/Declarations.md](ReferenceManual/Declarations.md)): Introduce types, operators, variables, and other names and constructs.
- **Attributes** ([ReferenceManual/Attributes.md](ReferenceManual/Attributes.md)): Add information to declarations and types.
- **Patterns** ([ReferenceManual/Patterns.md](ReferenceManual/Patterns.md)): Match and destructure values.
- **Generic Parameters and Arguments** ([ReferenceManual/GenericParametersAndArguments.md](ReferenceManual/GenericParametersAndArguments.md)): Generalize declarations to abstract away concrete types.
- **Summary of the Grammar** ([ReferenceManual/SummaryOfTheGrammar.md](ReferenceManual/SummaryOfTheGrammar.md)): Read the whole formal grammar.
## Usage Notes
- Organized progressively: GuidedTour → LanguageGuide → ReferenceManual
## License & Attribution
This skill contains content from [The Swift Programming Language](https://github.com/swiftlang/swift-book.git), distributed under the **Apache 2.0 License**.
Copyright © Apple Inc. and the Swift project authors.
This package is a derivative work that aggregates the original markdown content into a structure optimized for LLM context.