Cannot send non-sendable value from synchronous method to an actor

Hi everyone,

I am having an issue where I want to have a synchronous method taking a non sendable value that I want to use in an actor and as of now cannot understand why I cannot do it/how to do it. If the handle method is async, it works.

Testing the following code in a new Swift Package with swiftLanguageModes: [.v6]

final class NonSendable { }

@available(iOS 13.0, *)
actor TestActor {
    func store(nonSendable: sending NonSendable) { }
}

@available(macOS 10.15, *)
@available(iOS 13.0, *)
func handle(nonSendable: sending NonSendable) {
    let actor = TestActor()

    Task {
        await actor.store(nonSendable: nonSendable) // Sending task-isolated 'nonSendable' to actor-isolated instance method 'store(nonSendable:)' risks causing data races between actor-isolated and task-isolated uses
    }
}

@available(macOS 10.15, *)
@available(iOS 13.0, *)
func testUsage() {
    let nonSendable = NonSendable()
    handle(nonSendable: nonSendable)
//    handle(nonSendable: nonSendable) // This should be prevented - and is, stops compiling
}

Would appreciate any help. Of course, creating a Sendable box works, but I would like to find a correct solution without having to resort to that - if possible.

This is expected. To better illustrate the problem, take a look at this example:

class NS {} // Non-Sendable tpye

func sendAway(_ ns: sending NS) {} // "Consume" `ns`'s isolation region

func main() {
  let ns = NS()

  let awayWithYou = {
    sendAway(ns)
  }

  awayWithYou() // "Consumed" once

  awayWithYou() // "Consumed" twice   
}

The problem is the lack of a call-once feature. Inside the stored closure, ns’s isolation region is “consumed” by the sendAway function. However, without call-once support, the awayWithYou closure could be called more than once, which would effectively “consume” ns’s isolation region multiple times. You can work around this by using a custom disconnected type or nonisolated(unsafe).

1 Like

This problem is specific to non-sendable classes, right? The compiler is worried there could be other references to the instance that could be called from other tasks.

I’ve run into similar problems and ended up making the unsendable class into a struct. But of course that's not always an option.

Good catch – yes, works for structs. As of now I also opted for SendableBox

Could you elaborate on this? The same error should occur with a non-sendable value type, AFAIU.