golang 對比
先用go語言做一個對比,比如我們要將兩個字串轉換為數字後返回結果,如果使用golang,則實現方式如下:
func multiply(first_number string, second_number string) (result int, err error) {
var (
first int
second int
)
first, err = strconv.Atoi(first_number)
if err != nil {
return
}
second, err = strconv.Atoi(second_number)
if err != nil {
return
}
result = first * second
return
}
可以看到,這也是go語言最常用的錯誤處理機制,如果捕獲到錯誤,則將錯誤返回,否則返回正常的結果。這樣處理錯誤的結果就是:每一個錯誤都要進行人肉處理,怎麼很多的冗餘性程式碼,如果程式碼量很大的話,會寫很多冗餘的程式碼,很不優雅。
rust 實現
基礎版本
現在,使用Rust進行實現同樣的邏輯:
fn multiply(first_number_str: &str, second_number_str: &str) -> i32 {
let first_number = first_number_str.parse::<i32>().unwrap();
let second_number = second_number_str.parse::<i32>().unwrap();
first_number * second_number
}
以上程式碼相比golang的程式碼,並沒有多少現金的地方。因為程式在解析到程式異常的時候,直接終止程式。這樣在專案開發中是很不友好的,但是,這個只是最粗糙的實現方式,我們使用match進行優化它:
match
use std::num::ParseIntError;
type AliasResult<T> = Result<T, ParseIntError>;
#[allow(dead_code)]
fn multiply1(first_number_str: &str, second_number_str: &str) -> AliasResult<i32> {
match first_number_str.parse::<i32>() {
Err(e) => Err(e),
Ok(first_number) => match second_number_str.parse::<i32>() {
Err(e) => Err(e),
Ok(second_number) => Ok(first_number * second_number)
}
}
}
這個和golang相比,實現不相上下。都是檢測到異常就返回異常,如果止步於此,那麼,rust只能和golang打成平手。但是,rust還提供的函式式的結果處理機制:
使用 map 和 and_then 實現
#[allow(dead_code)]
fn multiply2(first_number_str: &str, second_number_str: &str) -> AliasResult<i32> {
first_number_str.parse::<i32>().and_then(|first_number| {
second_number_str.parse::<i32>().map(|second_number| first_number * second_number)
})
}
可以看到,通過運算元的方式,rust可以通過兩行程式碼就可以處理想要的結果,這裡已經是相對比較好的實現了。但是,相比之前的程式碼,不過不了解函數語言程式設計,可能相對難懂。那麼我們有沒有折中的實現呢
通用折中的實現
#[allow(dead_code)]
fn multiply3(first_number_str: &str, second_number_str: &str) -> AliasResult<i32> {
let first_number = match first_number_str.parse::<i32>() {
Ok(first_number) => first_number,
Err(e) => return Err(e)
};
let second_number = match second_number_str.parse::<i32>() {
Ok(second_number) => second_number,
Err(e) => return Err(e)
};
Ok(first_number * second_number)
}
以上實現,相對來說清晰易讀,但是,有沒有一種方式,能將重複的錯誤處理簡化掉呢,答案是有:
#[allow(dead_code)]
fn multiply4(first_number_str: &str, second_number_str: &str) -> AliasResult<i32> {
let first_number = first_number_str.parse::<i32>()?;
let second_number = second_number_str.parse::<i32>()?;
Ok(first_number * second_number)
}
以上實現,相比之前第一個最粗糙的實現,只是多了兩個?.但是,他依然做了最安全的錯誤處理,同時,相比於golang,程式碼量成倍的減少,並且程式碼的易讀性也相當的高,同時,相比程式碼的執行效率,rust也是完勝golang的。所以,擁抱rust吧~~