Swift 中 Result 類型全解析:從基礎到進階

在現代 iOS 開發中,Swift 的 Result 類型是處理同步與異步錯誤的一大利器。相比傳統的 throws / do-catch 語法,它更清晰、結構化,也更易于組合式編程。
本文將帶你從 Result 的基礎定義出發,逐步深入其在實際項目中的多種應用,包括:

  • Result 類型結構和基本用法
  • 使用 Result(catching:) 簡化拋出函數調用
  • 使用 get() 還原拋出異常
  • map / flatMap / mapError 組合式編程
  • async/await 的結合
  • 高階封裝與擴展函數

什么是 Result 類型?

Swift 在 5.0 引入了標準庫的 Result 枚舉,用于統一表示一個操作是成功還是失敗:

enum Result<Success, Failure: Error> {case success(Success)case failure(Failure)
}

這種設計使得我們可以顯式處理成功和失敗的情況,并能將其作為值傳遞。


基礎用法

例如我們封裝一個網絡請求:

enum NetworkError: Error {case invalidURLcase requestFailed
}func fetchData(from urlString: String) -> Result<Data, NetworkError> {guard let url = URL(string: urlString) else {return .failure(.invalidURL)}// 假設這里成功返回let data = Data()return .success(data)
}

調用者可以顯式處理成功與失敗:

switch fetchData(from: "https://example.com") {
case .success(let data):print("獲取到數據:\(data.count) bytes")
case .failure(let error):print("失敗:\(error)")
}

Result(catching:):讓 throws 函數更優雅

傳統寫法:

do {let value = try someThrowingFunction()print(value)
} catch {print(error)
}

改寫為:

let result = Result { try someThrowingFunction() }

這等價于:

let result: Result<ReturnType, Error>
do {let value = try someThrowingFunction()result = .success(value)
} catch {result = .failure(error)
}

這是 Swift 標準庫為 Result 提供的一個便捷初始化方式:

public init(catching body: () throws -> Success)

配合 switch.get() 解包:

do {let value = try result.get()print(value)
} catch {print(error)
}

這段代碼的重點在于:result 是一個 Result<Success, Failure> 類型,你通過 .get() 方法來從中提取值。

.get() 是 Result 提供的一個方法:

func get() throws -> Success

? 如果是 .success(value),就返回 value
? 如果是 .failure(error),就會 throw error

也就是說,它把 Result 恢復成一個“會拋出錯誤”的函數調用。

它等價于這樣寫:

switch result {
case .success(let value):print(value)
case .failure(let error):print(error)
}

或者:

if case let .success(value) = result {print(value)
} else if case let .failure(error) = result {print(error)
}

.get() 允許你在使用 Result 的同時,兼容 throws 風格的控制流。這在以下場景特別有用:

  1. 在函數中繼續“throw”出去:
func loadUser() throws -> User {let result = Result { try JSONDecoder().decode(User.self, from: data) }return try result.get() // 自動把錯誤 throw 出去
}

不想寫 switch,只想“順著拋出去”,那 .get() 就特別方便。

  1. 在某些 try-catch 中臨時轉回 throws 風格:
do {let user = try result.get()print("用戶名:", user.name)
} catch {print("解包失敗:", error)
}

這段代碼可讀性也很好,就像你調用了一個 throws 函數一樣。


函數組合:map / flatMap / mapError

map

僅在 .success 情況下轉換結果:

let result: Result<Int, Error> = .success(2)
let squared = result.map { $0 * $0 } // .success(4)

flatMap

鏈接另一個 Result

func square(_ input: Int) -> Result<Int, Error> {return .success(input * input)
}let chained = result.flatMap(square) // .success(4)

mapError

將錯誤轉換成另一種類型:

let errorMapped = result.mapError { error inreturn MyCustomError(reason: error.localizedDescription)
}

與 async/await 協作

Swift 的 async/await 與 throws 通常這樣使用:

func loadRemoteData() async throws -> Data

這意味著你在調用它時必須用 try await,并包裹在 do-catch 中:

do {let data = try await loadRemoteData()
} catch {// 處理錯誤
}

你可以把這段異步 throws 的代碼包裝為 Result<Success, Failure> 類型:

let result = await Result {try await loadRemoteData()
}

這是一種將“異步拋錯邏輯”變成一個明確的結果值的方式,使得你可以把邏輯結構化、組合、傳遞。

實用場景:

在 ViewModel 中捕獲異步錯誤

目的
? 避免在 SwiftUI 的 @MainActor、@Published 環境中嵌套 do-catch
? 讓錯誤與成功的結果都作為值進行統一處理

示例:

@MainActor
func fetchData() async {let result = await Result {try await apiService.loadUser()}switch result {case .success(let user):self.user = usercase .failure(let error):self.errorMessage = error.localizedDescription}
}

這樣可以:
? 更易測試:你可以直接構造 .success / .failure 進行單元測試
? 更可組合:可以繼續 .map() 或 .mapError() 處理后續邏輯

搭配 .mapError 統一錯誤類型

很多時候我們不希望把錯誤直接暴露給 UI,而是將其轉為通用的 AppError:

let result = await Result {try await apiService.loadUser()
}
.mapError { error inreturn AppError.network(error.localizedDescription)
}

這讓你的 ViewModel 不用關心底層的 error 是 URLError 還是 DecodingError,而是統一成業務可識別的類型。

避免多個 await 重復 do-catch 塊

當你在一個異步流程中連續調用多個 async throws 函數時,傳統寫法會很冗長:

do {let user = try await fetchUser()let profile = try await fetchProfile(for: user.id)let avatar = try await fetchAvatar(for: profile.avatarID)
} catch {// 各種錯誤可能混在一起
}

如果改用 Result 方式,每步都可以安全處理錯誤并組合:

let result = await Result {try await fetchUser()
}
.flatMap { user inawait Result { try await fetchProfile(for: user.id) }
}
.flatMap { profile inawait Result { try await fetchAvatar(for: profile.avatarID) }
}

再用 .get() 解包或 .switch 判斷結果。

好處:
? 每一步都能捕獲錯誤
? 易于插入中間處理(日志、緩存、fallback)
? 錯誤處理更細粒度、可組合


async throws 函數封裝
func taskResult<T>(_ work: @escaping () async throws -> T) async -> Result<T, Error> {await Result {try await work()}
}

然后統一調用:

let result = await taskResult {try await fetchData()
}

實用擴展函數

onSuccess / onFailure

extension Result {func onSuccess(_ handler: (Success) -> Void) -> Self {if case let .success(value) = self {handler(value)}return self}func onFailure(_ handler: (Failure) -> Void) -> Self {if case let .failure(error) = self {handler(error)}return self}
}

用法:

result.onSuccess { print("成功結果:\($0)") }.onFailure { print("錯誤:\($0)") }

recover:提供默認值或兜底方案

extension Result {func recover(_ value: @autoclosure () -> Success) -> Success {switch self {case .success(let success): return successcase .failure: return value()}}
}

實戰案例:網絡請求封裝

func loadUser() async -> Result<User, NetworkError> {await Result {let (data, _) = try await URLSession.shared.data(from: URL(string: "https://api.example.com/user")!)return try JSONDecoder().decode(User.self, from: data)}.mapError { error in.requestFailed}
}

總結對比表

特性throws/do-catchResult
返回結構無返回值顯式 success/failure
鏈式調用不方便支持 map / flatMap
多階段處理需嵌套可組合
可讀性
可測試性異常難模擬易構造成功/失敗場景

參考資源

  • Apple 官方文檔 - Result

結語

Swift 的 Result 是將函數式編程與錯誤處理融合的優秀設計。
它不僅簡化了異常流程,還增強了組合能力。我們應在業務邏輯、網絡請求、異步流程、狀態傳遞中更多地使用 Result,寫出更簡潔、更安全的代碼。

讓我們從現在開始,用 Result 統一錯誤處理思路,讓代碼更具表達力!

最后,希望能夠幫助到有需要的朋友,如果覺得有幫助,還望點個贊,添加個關注,筆者也會不斷地努力,寫出更多更好用的文章。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/87031.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/87031.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/87031.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Github 2025-06-28 Rust開源項目日報 Top10

根據Github Trendings的統計,今日(2025-06-28統計)共有10個項目上榜。根據開發語言中項目的數量,匯總情況如下: 開發語言項目數量Rust項目10Rust實現的非官方Bitwarden兼容服務器 創建周期:2317 天開發語言:Rust協議類型:GNU Affero General Public License v3.0Star數量…

python 寫一個判斷文本中是否有手機號的函數,并提取出文本中的手機號

我們需要判斷文本中是否有手機號&#xff0c;并提取出手機號。 中國大陸的手機號規則&#xff1a; 1. 通常為11位數字。 2. 目前手機號段分配如下&#xff1a; - 移動號段&#xff1a;134(0-8)、135、136、137、138、139、147、148、150、151、152、157、158、159、172、178、1…

作物生長模型Oryza V3實戰12:drate程序詳解

drate(v2).exe,可以通過觀察移植日、穗部分化、開花和成熟的物候日期(即日和年),DRATE(v2)用于校準四個階段的發展速率:幼苗期(DVRJ,oCday-1)、光周期敏感期(DVRI,oCday-1)、穗部發育期(DVRP,oCday-1)和生殖期(DVRR,oCday-1)。 一 準備輸入文件 1、準備.crp,.…

利用視覺-語言模型搭建機器人靈巧操作的支架

25年6月來自斯坦福和德國卡爾斯魯厄理工的論文“Scaffolding Dexterous Manipulation with Vision-Language Models”。 靈巧機械手對于執行復雜的操作任務至關重要&#xff0c;但由于演示收集和高維控制的挑戰&#xff0c;其訓練仍然困難重重。雖然強化學習 (RL) 可以通過在模…

面試拷打-20250701

memcopy和memmov 詳細解釋 示例1&#xff1a;不重疊的內存區域 正常復制。 示例2&#xff1a;重疊的內存區域 原始數據&#xff1a;src2是一個包含字符串"HelloWorld"的字符數組。使用memcpy&#xff1a; memcpy(src2 2, src2, 5);試圖將src2中的前5個字符復制…

什么是 BigKey?

Redis BigKey 深度解析&#xff1a;識別、危害與優化方案 什么是 BigKey&#xff1f; 在 Redis 中&#xff0c;BigKey 是指存儲大量數據的單個鍵&#xff0c;這些鍵通常具有異常大的內存占用或包含大量元素。BigKey 不是由數據類型定義&#xff0c;而是由其資源消耗決定的。 …

量化選股策略 聚寬

# 量化選股策略完整分析與優化建議 ## 策略整體架構分析 這個量化交易策略主要由以下幾個核心部分組成&#xff1a; 1. **初始化設置**&#xff1a;配置基準指數、交易參數和全局變量 2. **選股邏輯**&#xff1a;通過財務指標篩選優質股票 3. **股票過濾**&#xff1a;排除…

Python 數據分析:numpy,抽提,布爾索引2。

目錄 1 示例代碼2 歡迎糾錯3 論文寫作/Python 學習智能體------以下關于 Markdown 編輯器新的改變功能快捷鍵合理的創建標題&#xff0c;有助于目錄的生成如何改變文本的樣式插入鏈接與圖片如何插入一段漂亮的代碼片生成一個適合你的列表創建一個表格設定內容居中、居左、居右S…

解決leetcode第3597題分割字符串

3597. 分割字符串 難度&#xff1a;中等 問題描述&#xff1a; 給你一個字符串 s&#xff0c;按照以下步驟將其分割為 互不相同的段 &#xff1a; 從下標 0 開始構建一個段。 逐字符擴展當前段&#xff0c;直到該段之前未曾出現過。 只要當前段是唯一的&#xff0c;就將其…

電源芯片之DCDC初探索ING

1. 概述 DC-DC轉換器的意思是直流變直流&#xff08;不同的直流電源值得轉換&#xff09;&#xff0c;是一種在直流電路中將一個電壓值的電能變為另一個電壓值的電能裝置。 DC-DC轉換器一般由控制芯片、電感線圈、二極管、三極管、電容器構成。 2. 基本拓撲結構 2.1 非隔離…

JavaEE:分布式session

一、使用Redis存儲分布式session&#xff1a; 1.SpringBoot整合Redis&#xff0c;見如下地址&#xff1a; JavaEE&#xff1a;SpringBoot整合Redis_a526001650a-CSDN博客 2.代碼實現分布式session存儲(此處以token為例)&#xff1a; Autowired private RedisTemplate<St…

OpenCV CUDA模塊設備層-----“大于閾值設為零” 的圖像處理函數 thresh_to_zero_inv_func()

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 OpenCV 的 CUDA 模塊&#xff08;cudev&#xff09; 中的一個仿函數生成器&#xff0c;用于創建一個 “大于閾值設為零” 的圖像處理函數對象。 …

FastGPT與MCP:解鎖AI新時代的技術密碼

一、AI 浪潮中的新星&#xff1a;FastGPT 與 MCP 登場 在當今科技飛速發展的時代&#xff0c;人工智能&#xff08;AI&#xff09;已成為推動各行業變革的核心力量。從智能語音助手到復雜的圖像識別系統&#xff0c;AI 的應用無處不在&#xff0c;而其中的關鍵技術 —— 語言模…

browser-tools-mcp + excel-mcp-server + cursor 實現讀取網頁信息自動寫入Excel

browser-tools-mcp excel-mcp-server cursor 實現讀取網頁信息自動寫入Excel 文章目錄 browser-tools-mcp excel-mcp-server cursor 實現讀取網頁信息自動寫入Excel一、安裝node.js和npm1、安裝nvm2、安裝最新版本的node.js 二、安裝browser-tools-mcp1、安裝 BrowserTools…

Linux安裝JDK和Maven

Linux安裝JDK和Maven 安裝JDK1.8 oracle官網 https://www.oracle.com 下載包地址&#xff1a;https://www.oracle.com/java/technologies/downloads/archive/ 步驟1&#xff1a;官網下載壓縮包 點擊想要下載的版本&#xff0c;需要登錄Oracle的賬號&#xff0c;沒有的話需要…

MySQL主從復制與數據庫集群深度解析

一、主從復制核心架構與復制模式 MySQL主從復制是構建分布式數據庫的基礎技術&#xff0c;通過日志同步機制實現數據冗余與讀寫分離。其核心架構分為三層&#xff1a; 日志記錄層&#xff1a;主庫將數據變更寫入二進制日志&#xff08;Binlog&#xff09;網絡傳輸層&#xff…

安裝emsdk 4.0.10報Connection reset by peer解決

出錯如下: 使用瀏覽器下載所需文件 https://storage.googleapis.com/webassembly/emscripten-releases-builds/deps/node-v22.16.0-darwin-x64.tar.gz 移動到到emsdk/downloads下 修改emsdk.py download_even_if_exists=True 設置環境變量

win11,visual studio 2022,配置dcmtk,opencv

一、配置dcmtk 1 文件下載---地址&#xff0c;Software Development based on DCMTK - dicom.offis.de 源文件下載&#xff0c;選擇.zip下載&#xff0c;.tar.gz為Linux和macOS下面常見的壓縮包 支持庫下載 解決 DCMTK 在 Windows 上編譯時所需的依賴庫問題 libiconv GNU有…

2025 最新 Appium Inspector 環境搭建教程

1 環境搭建背景 版本升級&#xff1a;Appium 2.0 版本替代 1.x&#xff0c;原 Appium Desktop 因安全漏洞和功能廢棄不再適用。需求痛點&#xff1a;Android Studio 僅支持 debug 程序元素定位&#xff0c;需通過 Appium Inspector 實現通用 APK 元素定位。 2 環境搭建步驟 …

Vue 安裝使用教程

一、Vue 簡介 Vue&#xff08;讀作 /vju?/&#xff0c;類似于“view”&#xff09;是一款用于構建用戶界面的漸進式 JavaScript 框架。它易于上手&#xff0c;輕量高效&#xff0c;適合快速構建前端界面&#xff0c;廣泛應用于各類 Web 項目中。 二、Vue 安裝方式 2.1 直接通…