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吧~~
本作品采用《CC 协议》,转载必须注明作者和本文链接