Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 144 additions & 4 deletions TSPL.docc/LanguageGuide/Concurrency.md
Original file line number Diff line number Diff line change
Expand Up @@ -1371,16 +1371,16 @@ if you try to add concurrent code to this function,
introducing a possible suspension point,
you'll get a compile-time error instead of introducing a bug.


## Global Actors

The main actor is a global singleton instance of the [`MainActor`][] type.
An actor can normally have multiple instances,
Normally, an actor can have multiple instances,
each of which provides independent isolation.
This is why you declare all of an actor's isolated data
The possibility of multiple instances
is why you declare all of an actor's isolated data
as instance properties of that actor.
However, because `MainActor` is a singleton ---
there is only ever a single instance of this type ---
there's 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
Expand All @@ -1392,6 +1392,146 @@ You can define your own singleton global actors
using the `@globalActor` attribute,

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 MainActor is 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."

Copy link
Member

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?

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.
}
}

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 ---

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
the `Vehicle` class is isolated to the main actor ---
the `Vehicle` class isolates to the main actor ---

Copy link
Member

@amartini51 amartini51 Sep 29, 2025

Choose a reason for hiding this comment

The 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.
}
}

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 only infers isolation from protocols
when you write the conformance as part of the type's declaration.
If you write the conformance in an extension,
then isolation inference only applies to
requirements that are part of that extension.
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()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this example the function toggle is isolated to the main actor but isOn is not isolated to the main actor, is it? And if that’s the case, isn’t it not allowed to use it here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this example, isOn is nonisolated, which is fine to access from a main actor isolated method.

Choose a reason for hiding this comment

The 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.

Copy link
Member

Choose a reason for hiding this comment

The 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 isOn is nonisolated? Or ok to make this conversation thread as resolved?

}
}
```

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
Expand Down