Result Type: o que é, o tipo contêiner Result e como funciona no desenvolvimento móvel

Autor: IT Sectr Publicado: 2026-05-26 Tempo de leitura: 9 min

Result Type — um tipo contêiner que representa o resultado de uma operação que pode ser concluída com sucesso (Success) ou falha (Failure). Ao contrário das exceções, o Result passa o erro como um valor normal sem desenrolamento de pilha, tornando o tratamento de erros esperados mais seguro e composável. De acordo com Apple Swift Documentation (2026), Result<Success, Failure> em Swift permite encadear operações com tratamento automático de erros através de map e flatMap sem interromper a execução do programa.

Principais pontos

  • Result Type — um contêiner genérico com dois estados: Success (dados) e Failure (erro), sem desenrolamento de pilha
  • Em Swift Result<Success, Failure> — um tipo integrado com métodos map, flatMap, mapError e get
  • Em Kotlin Result<T> representa sucesso ou Throwable, com funções getOrNull, getOrDefault, fold
  • Em Dart Result<T> padrão disponível desde Dart 3.0, além de Either do pacote fpdart
  • Composição através de map e flatMap permite combinar múltiplas operações que retornam Result sem verificações aninhadas

O que é Result Type?

Result Type é um tipo genérico que encapsula o resultado de uma operação que pode ser bem-sucedida ou falhar. Ao contrário das exceções, onde um erro interrompe o fluxo normal e requer desenrolamento de pilha para encontrar um bloco catch, o Result passa o erro como um valor normal — o chamador sempre recebe um objeto e decide como tratá-lo. Isso é especialmente útil para erros esperados: entrada inválida, regras de negócio, rejeição do servidor, onde as exceções seriam um mecanismo muito pesado.

O conceito de Result origina-se da programação funcional, onde tipos semelhantes são chamados de Either (ou Left/Right). Em Swift, o Result tornou-se um tipo padrão no Swift 5.0; em Kotlin, Result<T> apareceu na biblioteca padrão; no Dart 3.0, o Result<T> integrado foi introduzido. Cada implementação fornece métodos para trabalhar com o contêiner: map (transformar o valor de sucesso), flatMap (encadear funções Result), mapError (transformar o erro), fold (tratar ambos os casos). Result Type é a pedra angular do tratamento funcional de erros no desenvolvimento móvel, uma alternativa ao try-catch para cenários esperados.

A principal vantagem do Result é a composabilidade. Você pode combinar várias operações, cada uma das quais pode falhar, em uma única cadeia sem blocos try-catch aninhados. Se alguma operação na cadeia falhar com Failure, toda a cadeia é interrompida e retorna Failure — sem uma única condição if ou bloco catch. Isso torna o código linear e legível, especialmente em cenários com múltiplas requisições sequenciais de API ou verificações de regras de negócio.

Result em Swift

Em Swift, Result<Success, Failure> é um enum com dois casos: .success(Success) e .failure(Failure), onde Failure é restrito pelo protocolo Error. O Result do Swift é um tipo totalmente funcional com métodos map, flatMap, mapError e get. get é um método especial: retorna o valor de Success se o resultado for bem-sucedido e lança o erro se for Failure. Isso permite que o Result atue como uma ponte entre os estilos funcional e baseado em exceções: tratar através de map/flatMap em uma cadeia e, no final, usar get com do-catch para integrar com código throws.

Enum Result e switch exaustivo

O Result do Swift é um enum com parâmetros genéricos, o que permite ao compilador verificar o tratamento exaustivo através de switch ou do-catch. Se você adicionar um novo caso ao enum NetworkError, o compilador produzirá um erro em todas as expressões switch onde esse caso não for tratado. A verificação exaustiva é a principal vantagem do Result sobre as exceções: o compilador garante que todos os erros possíveis sejam considerados em tempo de compilação. Em contraste, as exceções não são verificadas pelo compilador em Swift (apenas throws é declarado, mas não o tipo de erro).

swift
enum NetworkError: Error {
    case badURL
    case requestFailed(String)
    case decodingFailed
}

func fetchUser(id: Int) -> Result<User, NetworkError> {
    guard let url = URL(string: "https://api.example.com/users/\(id)") else {
        return .failure(.badURL)
    }
    let result = performRequest(url: url)
    switch result {
    case let .success(data):
        if let user = try? JSONDecoder().decode(User.self, from: data) {
            return .success(user)
        }
        return .failure(.decodingFailed)
    case let .failure(error):
        return .failure(.requestFailed(error.localizedDescription))
    }
}

// Uso com switch
let result = fetchUser(id: 42)
switch result {
case .success(let user):
    showUser(user)
case .failure(.badURL):
    logError("Invalid URL")
case .failure(.requestFailed(let msg)):
    showAlert(msg)
case .failure(.decodingFailed):
    logError("Decoding error")
}

Result<Success, Failure> permite tipificar o erro no nível do tipo: a função fetchUser retorna Result<User, NetworkError>, onde NetworkError é um enum concreto com três casos. O chamador trata cada caso via switch com cobertura exaustiva — o compilador verifica se todas as variantes foram tratadas. O switch exaustivo é uma vantagem chave do Result sobre as exceções: o compilador garante que você não esqueça de tratar .badURL, .requestFailed ou .decodingFailed. Com as exceções, o compilador não exige tratamento, e um catch(.badURL) ausente pode facilmente passar despercebido na revisão de código.

Result em Kotlin

Em Kotlin, Result<T> é um tipo integrado da biblioteca padrão que representa sucesso (T) ou erro (Throwable). Ao contrário do Swift, o Result do Kotlin não permite especificar um tipo de erro concreto — apenas Throwable. Isso é feito por simplicidade, mas requer verificação adicional do tipo de erro através da expressão when. O Result do Kotlin suporta fold (tratar ambos os casos), getOrNull (sucesso ou null), getOrDefault (sucesso ou padrão), map, recover, andThen. Uma peculiaridade do Kotlin: o Result não foi projetado para propagação direta através dos limites de funções — não pode ser usado como tipo de retorno para métodos do Android SDK ou da API Kotlin Coroutines sem adaptação adicional.

kotlin
fun parseJson(input: String): Result<JsonObject> {
    return runCatching {
        JsonParser.parseString(input).asJsonObject()
    }
}

fun validateEmail(email: String): Result<String> {
    return if (email.contains("@")) {
        Result.success(email.trim())
    } else {
        Result.failure(IllegalArgumentException("Invalid email"))
    }
}

data class SignupData(val name: String, val email: String)

fun processSignup(name: String, email: String): SignupResult {
    return validateEmail(email).fold(
        onSuccess = { validateName(name) },
        onFailure = { SignupResult.Error("Invalid email") }
    )
}

runCatching é um invólucro conveniente em Kotlin que captura qualquer exceção e retorna Result.failure. validateEmail retorna Result.success ou Result.failure dependendo da validação. fold trata ambos os casos de forma compacta: em Success, a próxima função de validação é chamada; em Failure, SignupResult.Error é retornado. Importante: o Result do Kotlin não foi projetado para armazenamento em campos de data class ou transmissão direta através de limites de funções suspend — use classes seladas personalizadas (Success/Error/Loading) para representar estados de UI no Jetpack Compose ou MVVM.

Result em Dart e Flutter

Desde o Dart 3.0, a biblioteca padrão inclui um Result<T> integrado — uma classe selada com dois construtores: T.ok() (sucesso) e Error.error() (erro com Object e StackTrace). Antes do Dart 3.0, os desenvolvedores Flutter usavam Either<L, R> do pacote dartz ou classes seladas personalizadas. O Result integrado no Dart é minimalista: ele não fornece map/flatMap diretamente — essas funções precisam ser implementadas via when ou usando métodos de extensão. Para tratamento funcional sério, o Either do fpdart continua sendo uma solução mais poderosa com suporte para map, flatMap, mapLeft, fold, andThen e operadores bind.

Either do fpdart

Either<L, R> é um tipo tendencioso à esquerda do pacote fpdart, onde Left é o erro e Right é o sucesso. Ao contrário do Result<T> integrado, o Either tipifica o erro no nível do parâmetro de tipo (L), permitindo a diferenciação do tipo de erro em tempo de compilação. O pacote fpdart fornece um conjunto completo de combinadores funcionais: map (Right -> Right), mapLeft (Left -> Left), flatMap (bind — Either aninhado), andThen (encadear sem transformação), fold (sair do Either), getOrElse (valor padrão). Para aplicativos Flutter com uma abordagem funcional, o Either é o padrão de fato.

dart
import 'dart:convert';
import 'package:fpdart/fpdart.dart';

class UserService {
    Either<AppError, User> fetchUser(String id) {
        try {
            final response = await http.get(
                Uri.parse('https://api.example.com/users/$id')
            );
            if (response.statusCode == 200) {
                final user = User.fromJson(
                    json.decode(response.body)
                );
                return Either.of(user);
            }
            return Either.left(
                AppError.serverError(response.statusCode)
            );
        } on SocketException catch (e) {
            return Either.left(AppError.networkError(e.message));
        }
    }
}

// Uso com fold
final result = await service.fetchUser('42');
result.fold(
    (left)  => showError(left.message),
    (right) => showUser(right),
);

Either<L, R> do fpdart é um tipo tendencioso à esquerda: Left — erro, Right — sucesso. fetchUser retorna Either<AppError, User>, onde AppError é uma classe selada com tipos de erro concretos (serverError, networkError). fold trata ambos os casos: o primeiro callback para Left (erro), o segundo para Right (sucesso). No Dart 3.0, o Result integrado também suporta fold, mas não fornece map/flatMap. Para cadeias, o Either do fpdart fornece map, flatMap (bind), mapLeft, andThen — um conjunto completo de combinadores funcionais para composição de erros.

Composição de Result via map e flatMap

A principal vantagem do Result sobre as exceções é a composição. Se você tem várias operações, cada uma das quais pode falhar, pode encadeá-las usando map e flatMap sem um único if aninhado ou try-catch. map transforma o valor de sucesso: Result.success(x) -> Result.success(f(x)). flatMap (também chamado de bind ou andThen) é para casos onde a própria transformação retorna um Result: Result.success(x) -> f(x) -> Result<Y>. Se alguma etapa falhar com Failure, as operações subsequentes não são executadas — a cadeia é interrompida.

kotlin
data class UserRequest(val userId: String, val token: String)

sealed class AuthError {
    data object InvalidToken : AuthError()
    data class UserNotFound(val id: String) : AuthError()
}

typealias Outcome<T> = Either<AuthError, T>

fun validateToken(token: String): Outcome<String> =
    if (token.isNotBlank()) Either.right(token)
    else Either.left(AuthError.InvalidToken)

fun fetchProfile(userId: String): Outcome<Profile> =
    if (userId == "42") Either.right(Profile("Alice"))
    else Either.left(AuthError.UserNotFound(userId))

// Composição via flatMap (andThen no fpdart)
val result = validateToken("abc123")
    .flatMap { fetchProfile("42") }
    .map { it.name }
    .getOrElse { "Guest" }

println(result) // "Alice"

Cadeia: validateToken -> fetchProfile -> map name -> getOrElse “Convidado”. Se validateToken retornar Left (InvalidToken), a cadeia é interrompida e retorna “Convidado”. Se fetchProfile retornar Left (UserNotFound) — também “Convidado”. Se ambas as operações forem bem-sucedidas — o nome do perfil. flatMap permite combinar funções Either, cada uma das quais pode falhar, em uma única cadeia linear. No estilo tradicional de exceções, o mesmo código exigiria dois try-catch aninhados ou verificações de null. getOrElse no final é o ponto de saída da composição, fornecendo um valor padrão para o caso de Failure.

Result vs Exceções: quando escolher cada um

Result e exceções não são abordagens mutuamente exclusivas. Cada um tem seu próprio domínio, e em um aplicativo móvel bem projetado, ambos são usados. A escolha depende se o erro é esperado ou inesperado. Result é para erros esperados que fazem parte da lógica de negócios: e-mail inválido, fundos insuficientes, limite de taxa excedido. Exceções são para erros de sistema inesperados: perda de rede, OutOfMemoryError, NullPointerException (que não deveriam acontecer, mas acontecem).

CritérioResult TypeExceções (Exception/Error)
Tipo de erroEsperados (lógica de negócio)Inesperados (sistema)
DesempenhoCusto baixo (sem desenrolamento de pilha)Custo alto (desenrolamento de pilha, captura de StackTrace)
ComposiçãoVia map/flatMap — cadeias linearesTry-catch aninhados — difícil de ler
CompiladorVerificação exaustiva (switch/when)Apenas exceções verificadas em Java
Fluxo de execuçãoNão interrompido — erro como valorInterrompido até o catch mais próximo
TesteFácil: verificar resultado, afirmar isSuccess/isErrorRequer assertThrows e objetos mock
Quando usarValidação de negócio, cadeias de requisições, formuláriosPerda de rede, erros de E/S, falhas do sistema

Regra prática: se o erro faz parte do fluxo normal de trabalho do aplicativo (usuário digitou um e-mail inválido, permissões insuficientes) — use Result. Se o erro é uma situação excepcional (servidor não respondendo, sem memória) — use exceções. No desenvolvimento móvel, Result nos limites das camadas (UseCase -> ViewModel) e exceções dentro das camadas (API -> Repository) é um padrão comum que combina as vantagens de ambas as abordagens.

Estratégia de migração de exceções para Result

A migração do código existente baseado em exceções para Result deve ser gradual. Comece pelos limites das camadas: envolva chamadas de funções throws em Result { try ... } (Swift) ou runCatching { ... } (Kotlin). Em seguida, substitua o tipo de retorno dos métodos de Repository e UseCase por Result/Either, mantendo a implementação interna em exceções. Na última etapa, migre a ViewModel: em vez de UiState com exceções, use uma classe selada UiState<T> (Loading, Success, Error), onde Error armazena um erro de domínio, não um Throwable. A migração gradual permite testar cada camada separadamente sem uma refatoração global.

Perguntas frequentes

Como o Result Type difere do Optional/Option?

Optional (T?) representa a presença ou ausência de um valor — nil significa “sem dados” mas não explica por quê. Result (Success/Failure) contém não apenas o sucesso, mas também a razão do erro com um tipo concreto. Use Optional quando a ausência é normal (por exemplo, um campo de perfil opcional) e Result quando precisar de informação do erro.

Como converter uma função throws para Result?

Em Swift, use Result { try throwingFunc() } — o construtor Result recebe um fechamento throws. Em Kotlin, use runCatching { throwingFunc() }, que retorna Result<T>. Em Dart, use Result<T>.tryCatch(() => throwingFunc()). Isso permite a integração fácil de código baseado em exceções em cadeias de Result.

Como tratar um erro no Result sem perder informação?

Use mapError (Swift) ou mapLeft (Either no Dart/Kotlin) para transformar o tipo de erro sem alterar o valor de sucesso. Se precisar tratar ambos os casos e retornar um único valor, use fold. Para registrar sem interromper a cadeia, use onFailure (Kotlin) ou um ponto de inspeção de prefixo.

Result pode ser usado em Kotlin com corrotinas?

Sim, mas com cuidado. O Result<T> do Kotlin não é recomendado como tipo de retorno para funções suspend diretamente devido a peculiaridades do compilador K2 e reflexão. Use sua própria classe selada NetworkResult<T> (Success, Error, Loading) para representar estados em corrotinas. Para erros esperados na lógica de negócio, o Either do Arrow é uma alternativa mais poderosa.

O que é fold no contexto de Result?

fold é um método que recebe dois callbacks: onSuccess (para o caso de sucesso) e onFailure (para o caso de erro), e retorna um único valor de qualquer tipo. É o equivalente a uma expressão switch, mas como uma função de ordem superior. fold é o principal ponto de saída das cadeias de Result, onde você converte Success/Failure em UiState, uma string para o usuário ou outro Result.

Resumo

  • Result Type — um contêiner genérico (Success/Failure) para tratamento seguro de erros esperados sem desenrolamento de pilha
  • Em Swift Result<Success, Failure> com Failure: Error fornece verificação exaustiva via switch com erros type-safe
  • Em Kotlin Result<T> envolve sucesso ou Throwable, runCatching é um construtor conveniente a partir de código throws
  • Em Dart Result<T> integrado (Dart 3.0) e Either<L, R> do fpdart para composição avançada com map/flatMap
  • Composição via map (transformar sucesso) e flatMap (encadear funções Result) substitui try-catch aninhados
  • Result vs exceções: Result para erros de negócio esperados, exceções para falhas de sistema inesperadas
  • Use Result nos limites das camadas para tratamento de erros explícito e testável sem interromper o fluxo de execução

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também