Understanding Results and Tasks, how to return from a Result?

I want to store a task instance so that I can later on check for it's result, to see if it successfully completed or if it thrown an error.
I'm not sure of two things:

  1. How do I return from a Task? I know about the Result<Success,Fail> enum, but I'm not sure I'm using it correct.
  2. I get a compilation error when I try to store the task, because the types don't match

my code:

class MessageDispatcher {
        private var listeningForMessagesTask: Task<Void,InvalidDataError>


      func listenForMessages() {
        **self.listeningForMessagesTask = Task.detached(priority: .background) {** // ERROR Line
                      // with the message: Cannot assign value of type 'Task<Void, Error>' to type 'Task<Void,          ///. InvalidDataError>'
            var isDone = false
            repeat {
                guard let (message, done) = try await self.connection.receive() else {
                    return Result.failure(InvalidDataError.BadServer)
                }
                isDone = done
                DispatchQueue.main.async {
                    self.rooms[0].chat += message
                }

            } while !isDone
            print("Done!")
        }
    }


enum InvalidDataError : Error {
    case NoData
    case BadEncoding
    case BadServer
}

Look at the declaration of Task:

struct Task<Success, Failure> where Failure : Error

Now look at the declaration of the Task.detached(priority: .background) method that you are calling:

static func detached(
    priority: TaskPriority? = nil,
    operation: @escaping @Sendable () async throws -> Success
) -> Task<Success, Failure>                        // ^^^^^^^

The return type of the operation closure must be the generic type Success. In your declaration of listeningForMessagesTask, you've specified Void as the concrete type for Success:

private var listeningForMessagesTask: Task<Void,InvalidDataError>
                                        // ^^^^

Therefore, the return type of the operation closure passed to Task.detached(priority: .background) must be Void, but you're trying to return an instance of Result:

return Result.failure(InvalidDataError.BadServer)

Here's what you should do instead:

Change the type of listeningForMessagesTask to Task<Void, Error> and throw the error instead of trying to return it wrapped in a Result:

class MessageDispatcher {
    
    private var listeningForMessagesTask: Task<Void, Error>
    
    func listenForMessages() {
        self.listeningForMessagesTask = Task.detached(priority: .background) {
            var isDone = false
            repeat {
                guard let (message, done) = try await self.connection.receive() else {
                    throw InvalidDataError.BadServer
                }
                isDone = done
                DispatchQueue.main.async {
                    self.rooms[0].chat += message
                }
                
            } while !isDone
            print("Done!")
        }
    }
    
}
2 Likes

Thanks Peter, you've been very generous with the response and I really appreciate that. I have a better understanding of it now, and I understand that I can basically only return the specified type of Success (which as pointed out by you, was Void).
I guess then, that in the background, aka behind the scenes, the static detached method will take whatever I throw in the closure and manipulate that back into a Result<Void,Error> basically returning back a Failure (. failure(_)) for me, if my closure throws.

Hope I got it right, this time. And thank you for your generosity! You've explained it like a pro.

Specific methods, such as Task.result will return an instance of Result<Success, Failure>.

1 Like