swifttaskgeneric-constraints

Swift Task extension: Success == Sendable, Failure == Error necessary?


Seeing in the Apple doc that the Task is defined as:

@frozen struct Task<Success, Failure> where Success : Sendable, Failure : Error

I've seen a number of code online where people add extensions to Task with constraint like:

extension Task where Failure == Error {}

and/or

extension Task where Success == Sendable {}

For example, the code here extends Task with the constraint Failure == Error. Given that Task is defined with constraint Failure: Error I wonder what's the specific need for adding Failure == Error?

Thanks!


Solution

  • A type satisfies the : Error constraint if it conforms to Error. For example, this type:

    enum MyError: Error {
        case somethingBadHappened
    }
    

    However, MyError does not satisfy an == Error constraint, because the only type that satisfies this constraint is Error itself. It says "equal to" Error, after all :)

    You can apply the same logic to Sendable.

    The versions of Task.init and Task.detached that take a throwing closure, and relayResult in your linked code, all requires == Error, because they all create tasks from a throwing closure.

    You don't know what specific type of Error a throwing closure is going to throw. The best you know is it throws Error. Therefore, these methods can only create a task whose failure type is exactly Error. They cannot create a task whose failure type is, say, MyError,

    // let's say that calling Task.init with Failure being MyError is allowed
    Task<SomeSuccessType, MyError> {
        // some code that throws AnotherError
    }
    

    because the throwing closure can throw AnotherError.

    But let's say the "throwing other types of errors" problem is magically handled somehow, this == Error also serves as an easy way for the compiler to infer the Failure type when you call these methods. You don't need to write the type of error the task can throw explicitly, like I did above.

    I can't think of when you would use exactly Sendable as the Success type off the top of my head though. That doesn't seem very useful.

    Sendable doesn't have any syntactic requirements, so it is functionally the same as using exactly Void (aka (), and also conforms to Sendable) as the Success type, to indicate "this task just "finishes" and has no result". If I were to choose, I would definitely choose Void instead of Sendable to express this idea.