-
Notifications
You must be signed in to change notification settings - Fork 232
Add discussion of global actor isolation inference. [SE-0316] #371
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 6 commits
5e5349e
0f1df57
bee33b2
bfd27fd
6841627
0d7cf4a
481f72e
223091d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -1371,7 +1371,6 @@ if you try to add concurrent code to this function, | |||||
| introducing a possible suspension point, | ||||||
| you'll get compile-time error instead of introducing a bug. | ||||||
|
|
||||||
|
|
||||||
| ## Global Actors | ||||||
|
|
||||||
| The main actor is a global singleton instance of the [`MainActor`][] type. | ||||||
|
|
@@ -1392,6 +1391,146 @@ You can define your own singleton global actors | |||||
| using the `@globalActor` attribute, | ||||||
| as described in <doc:Attributes#globalActor>. | ||||||
|
|
||||||
| ## Isolation Inference | ||||||
|
|
||||||
| When you subclass an actor-isolated class | ||||||
| and conform to an actor-isolated protocol, | ||||||
| Swift infers the actor isolation for you. | ||||||
| These inference rules apply to the main actor and to other global actors. | ||||||
|
|
||||||
| For classes that are isolated to a global actor, | ||||||
| Swift infers that their subclasses have the same isolation. | ||||||
| For example, | ||||||
| the code below declares a main-actor isolated class `Vehicle`, | ||||||
| and a subclass `Train` that inherits from `Vehicle`: | ||||||
|
|
||||||
| ```swift | ||||||
| @MainActor | ||||||
| class Vehicle { | ||||||
| var currentSpeed = 0.0 | ||||||
| func makeNoise() { | ||||||
| // do nothing - an arbitrary vehicle doesn't necessarily make a noise | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does this comment mean? "An arbitrary vehicle doesn't necessary make a noise"? What makes it "arbitrary"?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed The For additional context, this code listing follows the running code listing example from the Inheritance chapter earlier in the book. So we might also want to cross-apply edits there. Also, if there's an editorial preference for colons rather than dashes, I can spin up a tracking bug for that. (Thinking of this because I have a related fix to normalize "OK" vs "Ok" in comments, per Apple Style, on a different branch that includes similar punctuation.) |
||||||
| } | ||||||
| } | ||||||
|
|
||||||
| class Train: Vehicle { | ||||||
| func announceDeparture() { | ||||||
| print("All aboard!") | ||||||
| } | ||||||
| override func makeNoise() { | ||||||
| print("Choo Choo") | ||||||
| } | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| In the example above, | ||||||
| the `Vehicle` class is isolated to the main actor --- | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's discuss wording here. |
||||||
| writing `@MainActor` on the type | ||||||
| isolates all of its methods and properties to the main actor. | ||||||
| The `Train` subclass of `Vehicle` inherits this main-actor isolation, | ||||||
| which isolates all of the following to the main actor: | ||||||
|
|
||||||
| - Methods and properties it inherits, such as `currentSpeed`. | ||||||
| - Methods and properties it adds, such as `announceDeparture()`. | ||||||
| - Methods and properties in overrides, such as `makeNoise()`. | ||||||
|
|
||||||
| When subclassing a type that isn't isolated to a global actor, | ||||||
| Swift infers that overrides to any global-actor methods | ||||||
| are also isolated to that global actor. | ||||||
| For example, the following code isolates one method of the | ||||||
| `Vehicle` class to the main actor instead of the entire class: | ||||||
|
|
||||||
| ```swift | ||||||
| class Vehicle { | ||||||
| var currentSpeed = 0.0 | ||||||
|
|
||||||
| @MainActor | ||||||
| func makeNoise() { | ||||||
| // do nothing - an arbitrary vehicle doesn't necessarily make a noise | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, I'm not clear what this comment means.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed |
||||||
| } | ||||||
| } | ||||||
|
|
||||||
| class Train: Vehicle { | ||||||
| override func makeNoise() { | ||||||
| print("Choo Choo") | ||||||
| } | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| Because the `makeNoise()` method of `Vehicle` is marked `@MainActor`, | ||||||
| Swift infers that an override in a subclass | ||||||
| is also isolated to the main actor. | ||||||
| In the code above, the `makeNoise()` method of `Train` | ||||||
| isn't explicitly marked `@MainActor` | ||||||
| but its main-actor isolation is inferred | ||||||
| from the method on `Vehicle` that it overrides. | ||||||
|
|
||||||
| In addition to the places shown above | ||||||
| where Swift infers isolation from a superclass, | ||||||
| Swift also infers isolation from protocol conformances. | ||||||
| When a type conforms to a protocol, | ||||||
| Swift infers isolation from the protocol itself, | ||||||
| and from individual protocol requirements. | ||||||
| For example, | ||||||
| the following code has a main-actor isolated protocol `Togglable`, | ||||||
| and a conforming struct `Switch`: | ||||||
|
|
||||||
| ```swift | ||||||
| @MainActor | ||||||
| protocol Togglable { | ||||||
| mutating func toggle() | ||||||
| } | ||||||
|
|
||||||
| struct Switch: Togglable { | ||||||
| var isOn: Bool = false | ||||||
|
|
||||||
| mutating func toggle() { | ||||||
| isOn.toggle() | ||||||
| } | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| In the example above, | ||||||
| the `Togglable` protocol is marked `@MainActor` | ||||||
| to indicate that all of its requirements are isolated to the main actor. | ||||||
| Swift infers main-actor isolation on types that conform to `Togglable`, | ||||||
| so all methods and properties of `Switch` are isolated to the main actor, | ||||||
| including the `isOn` property and the `toggle` method. | ||||||
|
|
||||||
| Swift infers isolation from protocols | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed. My original placement of "only" is right next to the word that it modifies, to avoid ambiguity — do you think there's any risk of a reader misreading this? For example, to misread as "Swift infers isolation" but it doesn't do anything else. The original was based on editorial guidance I've gotten in the past, moving the "only" to the most specific location. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Revert to "only when...". Sounds good to me! |
||||||
| only when you write the conformance as part of the type's declaration. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed |
||||||
| If you write the conformance in an extension, | ||||||
| then isolation inference applies to | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed. Same reason for my original wording as above — although I think the risk of misreading is much lower here. |
||||||
| only requirements that are part of that the extension. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed |
||||||
| For example, the following code | ||||||
| implements a conformance of `Switch` to `Togglable` in an extension: | ||||||
|
|
||||||
| ```swift | ||||||
| @MainActor | ||||||
| protocol Togglable { | ||||||
| mutating func toggle() | ||||||
| } | ||||||
|
|
||||||
| struct Switch { | ||||||
| var isOn: Bool = false | ||||||
| } | ||||||
|
|
||||||
| extension Switch: Togglable { | ||||||
| mutating func toggle() { | ||||||
| isOn.toggle() | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this example the function
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this example, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, is that because you can only access the toggle function if your Switch is currently living in the main actor and thus any access to isOn would necessarily be on the main actor as well? I always forget about that. Very cool.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jasongregori Do you think it's worth calling out in the explanation of this code listing that |
||||||
| } | ||||||
| } | ||||||
| ``` | ||||||
|
|
||||||
| Because the declaration of `Switch` | ||||||
| doesn't include conformance to the `Togglable` protocol, | ||||||
| `Switch` is understood as `nonisolated`, | ||||||
| and the methods and properties declared inside it are also `nonisolated`. | ||||||
| However, | ||||||
| Swift infers main-actor isolation | ||||||
| for the extension because it implements `Togglable`, | ||||||
| which is a main-actor-isolated protocol. | ||||||
| This inference means the `toggle()` method is isolated to the main actor. | ||||||
|
|
||||||
| <!-- | ||||||
| OUTLINE | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The paragraph under "Global Actors" needs some work:
Global Actors
"The main actor is a global singleton instance of the [
MainActor][] type.Normally, an actor can have multiple instances,
each of which provide independent isolation.
The possibility of multiple instances is why you declare all of an actor's isolated data
as instance properties of that actor.
However, because
MainActoris singleton ---there is only ever a single instance of this type ---
the type alone is sufficient to identify the actor,
allowing you to mark main-actor isolation using just an attribute.
This approach gives you more flexibility to organize your code
in the way that works best for you."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed. Did you want to revisit this paragraph more together, or does the edit above fix the issues?