Using MainActor classes as default parameters to a function

I cannot figure out how to get this to work. I have a class (A) that must be initialized on the main actor. An instance of that class is used as the default argument for another type's initializer:

    @MainActor func testMainActor() {
        
        class A {
            // A's initializer must be on the main actor
            @MainActor init() {}
        }
        
        class B {
            // But A is a default parameter for a different initializer.
            init(a: A = A()) {}  // error: call to main actor-isolated initializer 'init()' in a synchronous nonisolated context

            // Adding MainActor doesn't work either
            @MainActor init(i: Int, a: A = A()) {}  // error: call to main actor-isolated initializer 'init()' in a synchronous nonisolated context
        }
        
        _ = B()
    }
3 Likes

I just ran into this as well. I think the issue is that default arguments use something similar to autoclosure to defer execution until needed, and that closure doesn't inherit the MainActor context.

The workaround I used was to make the parameter optional in the affected function, default it to nil, and fill it in in the function body (which will be bound to the MainActor) if it's nil:

class A {
    @MainActor init() {}
}

class B {
    @MainActor init(a: A? = nil) {
        let sureA: A
        if let a {
            sureA = a
        } else {
            sureA = A()
        }
    }
}

This is obviously far less than ideal. I think the compiler has enough information available (e.g. is the closure escaping?) to enforce the actor isolation guarantees. Can somebody on the compiler team weigh in here?

Could this not be simplified to just let a = a ?? A()? I'm pretty sure I've done something similar to this before.

1 Like

Ah, yep. You're right. I had seen an error message in one version of this I was playing around with ("Main actor-isolated … can not be referenced from a non-isolated autoclosure"), but that was before I added the @MainActor annotation to the initializer.

Regardless of the approach used for the workaround, this seems like something the language should support without having to do the workaround.

1 Like

Any thoughts here? Is there something blocking the ability for the compiler to understand the interaction between default arguments and actor isolation?

1 Like

Commenting to get notified on updated. I'd be very interested in this.