Fixing “reference to var myVariable just isn’t concurrency-safe as a result of it includes shared mutable state” in Swift


Revealed on: August 15, 2024

When you begin migrating to the Swift 6 language mode, you may most certainly activate strict concurrency first. As soon as you have achieved this there can be a number of warings and errors that you’re going to encounter and these errors will be complicated at occasions.

I will begin by saying that having a strong understanding of actors, sendable, and information races is a large benefit if you wish to undertake the Swift 6 language mode. Just about all the warnings you may get in strict concurrency mode will inform you about potential points associated to operating code concurrently. For an in-depth understanding of actors, sendability and information races I extremely suggest that you just check out my Swift Concurrency course which can get you entry to a collection of movies, workouts, and my Sensible Swift Concurrency e book with a single buy.

WIth that out of the way in which, let’s check out the next warning that you just may encounter in your mission:

reference to var myVariable just isn’t concurrency-safe as a result of it includes shared mutable state

There are a number of causes for this warning to pop up in Xcode. For instance, the code beneath would trigger Xcode to warn us:

// Var 'myVariable' just isn't concurrency-safe as a result of it's nonisolated world shared mutable state; that is an error within the Swift 6 language mode
var myVariable = UUID()

func randomCharacter() async -> Character {
    myVariable = UUID()
    return myVariable.uuidString.randomElement() ?? "1"
}

The next code makes myVariable a static var which ends up in the identical warning being proven:

struct CharacterMaker {
    // Var 'myVariable' just isn't concurrency-safe as a result of it's nonisolated world shared mutable state; that is an error within the Swift 6 language mode
    static var myVariable = UUID()

    static func randomCharacter() async -> Character {
        myVariable = UUID()
        return myVariable.uuidString.randomElement() ?? "1"
    }
}

The Swift compiler considers any globally accessible var to be unsafe from a concurrency viewpoint. The rationale for that’s that nothing is stopping us from making a number of calls to randomCharacter concurrently which might lead to an information race on myVariable. We might find yourself with a number of learn and write operations on the identical time.

To repair this, myVariable ought to both be moved into an actor or be remoted to a worldwide actor.

For instance, you could possibly isolate myVariable to @MainActor like this:

// with a worldwide variable
@MainActor
var myVariable = UUID()

// or as a static property
struct CharacterMaker {
    @MainActor
    static var myVariable = UUID()
    // ...
}

The draw back of that is, after all, that we must be on the primary actor to work together with the variable. You’ll be able to work round this by defining your personal (empty) world actor which can be certain that our accesses are on the worldwide executor as an alternative of the primary actor:

@globalActor
actor GlobalIsolator {
  static let shared = GlobalIsolator()
}

@GlobalIsolator
var myVariable = UUID()

// or as a static property
struct CharacterMaker {
    @GlobalIsolator
    static var myVariable = UUID()
    // ...
}

This makes accessing myVariable a bit much less handy since you’ll want to position your self on the GlobalIsolator actor when interacting with myVariable:

@GlobalIsolator
static func randomCharacter() async -> Character {
    myVariable = UUID()
    return myVariable.uuidString.randomElement() ?? "1"
}

In some instances you may know that although the compiler does not like your shared mutable state, you know that it is high-quality because of the means your code is structured.

If that is the case, and also you’re completely 100% positive that you just will not have any points associated to your shared mutable state, you need to use nonisolated(unsafe) in your variable to inform the compiler that the dearth of isolation is intentional and that you just’re conscious of its information issues of safety:

// with a worldwide variable
nonisolated(unsafe) var myVariable = UUID()

// or as a static property
struct CharacterMaker {
    nonisolated(unsafe) static var myVariable = UUID()
    // ...
}

You must solely use nonisolated(unsafe) as a last-resort answer as a result of the compiler will not find a way that can assist you detect doable information races round myVariable.

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles