`cancelOnGracefulShutdown` unavailable on `any AsyncSequence` after `share()`

Hi there, I’m trying out share() (from AsyncAlgorithms) in my app and want to pipe the resulting sequence through ServiceLifecycle’s cancelOnGracefulShutdown() before consuming it. As soon as the shared sequence is stored behind an existential, I hit a compile-time error:

struct SharedSequenceHolder {
    private let shared: any Sendable & AsyncSequence<Int, Never>

    init() {
        let base = [1, 2, 3].async
        self.shared = base.share()
    }

    func consume() async {
        // error here:
        // Member 'cancelOnGracefulShutdown' cannot be used on value of type
        // 'any Sendable & AsyncSequence<Int, Never>'; consider using a generic constraint instead
        for await value in shared.cancelOnGracefulShutdown() {
            _ = value
        }
    }
}

Relevant APIs:

// ServiceLifecycle
extension AsyncSequence where Self: Sendable, Element: Sendable {
  public func cancelOnGracefulShutdown() -> AsyncCancelOnGracefulShutdownSequence<Self> { ... }
}

// AsyncAlgorithms
extension AsyncSequence {
  public func share(
    bufferingPolicy: AsyncBufferSequencePolicy = .bounded(1)
  ) -> some AsyncSequence<Element, Failure> & Sendable
}

As far as I can tell, this fails because cancelOnGracefulShutdown() is constrained on Self and returns a type that mentions Self. That makes it unavailable on an existential like
any Sendable & AsyncSequence<Int, Never>.

I tried the usual “open existential via a generic parameter” trick:

struct SharedSequenceHolder {
    private let shared: any Sendable & AsyncSequence<Int, Never>

    init() {
        let base = [1, 2, 3].async
        self.shared = base.share()
    }

    func consume() async {
        await _consume(shared)
    }

    private func _consume<S: AsyncSequence & Sendable>(_ seq: S) async
    where S.Element == Int {
        for await value in seq.cancelOnGracefulShutdown() {
            _ = value
        }
    }
}

But this does not build and produces a “Call can throw, but the error is not handled” diagnostic (which seems a compiler bug).

Is there a recommended pattern for storing the result of share() (opaque return) while still keeping methods that mention Self available without having to immediately “open” via a generic?

Should ServiceLifecycle also offer an overload that works on
any AsyncSequence & Sendable where Element: Sendable?

Thanks a lot in advance!

What if you add S.Failure == Never to the where clause for _consume?

1 Like

That seems to work! I lost track of what things I tried and didn’t try :slight_smile:. Thanks!

That said, it would be nice to make this easier.

1 Like