swiftswift-concurrencyswift6

Swift default init behaving differently than empty init


I'm using Swift 6.1.2, and stumbled across something weird. Let's take the below example:

private let myClass = MyClass()

@MainActor
final class MyClass {
    private var data: [String: Int] = [:]

    init() {}
}

When I try to compile, I get the following error on the first line:

Main actor-isolated default value in a nonisolated context

However, when I remove the default init method, it compiles:

private let myClass = MyClass()

@MainActor
final class MyClass {
    private var data: [String: Int] = [:]
}

Why is this? Is it possible to keep the init method in case I want to initiaze data with something else?

Thank you very much!


Solution

  • The init you wrote is implicitly isolated to the @MainActor, because the class is marked @MainActor. On the other hand, the initialiser implicitly generated by Swift is non-isolated, because the compiler can see that it doesn't need to be isolated.

    So in the line

    private let myClass = MyClass()
    

    you are calling a main actor-isolated initialiser in a non-isolated environment. If this were allowed, situation like this could occur:

    actor SomeOtherIsolation {
        func f() {
            // suppose this is the first time 'myClass' is accessed
            // as a result, MyClass.init is called here
            // but we can't do that! MyClass.init is main actor isolated, but this code is isolated to SomeOtherIsolation
            print(myClass)
        }
    }
    

    You can write nonisolated init() {} instead, but based on the fact that MyClass itself is main actor isolated, it makes more sense to make myClass main actor isolated too.

    @MainActor
    private let myClass = MyClass()
    

    Since myClass is now main actor isolated, you are allowed to call a main actor isolated initialiser here.