`sending` is not allowed for parameters in associated values

Not sure, if this has already been covered elsewhere. I stumbled over this issue:

struct Foo<Event> {
    enum Error: Swift.Error {
        case dropped(Event) // Error: "Associated value 'dropped' of 'Sendable'-conforming enum 'Error' has non-sendable type 'Event'"
    }
    
    func send(_ event: sending Event) throws {}
}

OK, this is because the protocol Swift.Error conforms to Sendable.
Trying to apply the "obvious" fix:

struct Foo<Event> {
    enum Error: Swift.Error {
        case dropped(_ event: sending Event)  // Error: "'sending' may only be used on parameters and results"
    }
    
    func send(_ event: sending Event) throws {}
}

unfortunately, emits the compiler error
'sending' may only be used on parameters and results',
which IMHO looks like some unimplemented feature in the compiler.

A possible solution is to constrain type parameter Event to be Sendable. However, this is a rather strong constraint and I would rather prefer sending which IMHO should be preferred when possible, especially when this is an API of a library.

Is there a better approach today?

Is it possible to fix this in the compiler, so that parameters in associated values can have a sending specifier?

I don't think this can be fixed, and the reason it can't can be explained by looking at a struct.

Consider this struct:

struct MyError: Error { }

This compiles just fine. Now, imagine we want it to hold an Event:

struct MyError: Error {
    var event: Event
}

This will now fail to compile:

6 | struct MyError: Error {
7 |     var event: Event
  |         `- error: stored property 'event' of 'Sendable'-conforming struct 'MyError' has non-sendable type 'Event'
8 | }

This error is not resolved by providing a sending init:

struct MyError: Error {
    var event: Event

    init(_ event: sending Event) {
        self.event = event
    }
}

This fails in the same way.

The reason is that while you can pass the value into MyEvent using sending, once it's there the containing value is marked Sendable. That allows it to be shared freely across concurrency domains, which Event doesn't support.

So no, this is not a missing language feature: it's legitimately not possible.

1 Like

Ok, understood it with your struct example. In case of an enum, the same rules apply: the underlying storage of a sendable enum requires to be sendable. This consequently means, all associated data need to be sendable as well. Makes sense ;)

1 Like

The language could support this in limited ways but yes it would necessarily be restricted.

1 Like