對比程式語言的四種錯誤處理方法,哪種才是最優方案?

2023-05-09 06:00:47

作者:Andrea Bergia

譯者:豌豆花下貓@Python貓

英文:Error handling patterns

轉載請保留作者及譯者資訊

錯誤處理是程式設計的一個基本要素。除非你寫的是「hello world」,否則就必須處理程式碼中的錯誤。在本文中,我將討論各種程式語言在處理錯誤時使用的最常見的四種方法,並分析它們的優缺點。

關注不同設計方案的語法、程式碼可讀性、演變過程、執行效率,將有助於我們寫出更為優雅和健壯的程式碼。

返回錯誤程式碼

這是最古老的策略之一——如果一個函數可能會出錯,它可以簡單地返回一個錯誤程式碼——通常是負數或者null。例如,C 語言中經常使用:

FILE* fp = fopen("file.txt" , "w");
if (!fp) {
  // 發生了錯誤
}

這種方法非常簡單,既易於實現,也易於理解。它的執行效率也非常高,因為它只需要進行標準的函數呼叫,並返回一個值,不需要有執行時支援或分配記憶體。但是,它也有一些缺點:

  • 使用者很容易忘記處理常式的錯誤。例如,在 C 中,printf 可能會出錯,但我幾乎沒有見過程式檢查它的返回值!
  • 如果程式碼必須處理多個不同的錯誤(開啟檔案,寫入檔案,從另一個檔案讀取等),那麼傳遞錯誤到呼叫堆疊會很麻煩。
  • 除非你的程式語言支援多個返回值,否則如果必須返回一個有效值或一個錯誤,就很麻煩。這導致 C 和 C++ 中的許多函數必須通過指標來傳遞儲存了「成功」返回值的地址空間,再由函數填充,類似於:
my_struct *success_result;
int error_code = my_function(&success_result);
if (!error_code) {
  // can use success_result
}

眾所周知,Go 選擇了這種方法來處理錯誤,而且,由於它允許一個函數返回多個值,因此這種模式變得更加人性化,並且非常常見:

user, err = FindUser(username)
if err != nil {
    return err
}

Go 採用的方式簡單而有效,會將錯誤傳遞到呼叫方。但是,我覺得它會造成很多重複,而且影響到了實際的業務邏輯。不過,我寫的 Go 還不夠多,不知道這種印象以後會不會改觀!