初接触到Rust这门语言,主要是看中了它的性能和语法。一开始主要是在力扣平台刷题学习,然后每个语言都会试一试,发现Rust速度甚至在某些地方超过了C/C++,我就对这门语言比较感兴趣。
接下来说说我对这门语言优点的看法:
一、多范式
Rust是一门多范式的语言,能够很好地支持函数式和面向对象。其独特的函数声明方式
fn 函数名(参数列表) -> 返回类型
这种简捷的声明方式就能够让人一眼看出这是一个函数。
在面向对象方面,Rust加入了很多新鲜的概念和思路。比如Trait和Enum类型,其中最吸引我的是Rust中的Enum类型。不管是处理空指针的Opition(Some/None)还是处理错误的Result(Ok/Err),这种处理方式都非常的人性化,非常地好读。而且这种Enum类型对泛型的支持非常好,也真正地能够解决经常出现的内存安全问题:如解引用空指针等。
在面向对象方面还必须要提的是基于Trait的Impl体系,这个体系完全取消了构造函数,转而使用户自定义。在这种情况下,一些之前传统的构造函数难以表达的意思,就很好地在函数名中体现出来。比如一个TimeDuration类,应该会有许许多多的构造函数,例如from_ms,from_us等,但仅用一个构造函数不仅难以实现,更难以让用户感知构造函数到底在做什么。
Trait系统我认为是一个类似于标签的东西。这个类有了某种标签,它就必须定义这种标签的行为。比如Add Trait,非常常用。就相当于告诉编译器,这个东西可以去使用+号。在我们写泛型的函数或者其他类的时候,就可以很轻松地使用这个Trait去限定参数的类型,如果没有Impl Add Trait,编译器就不允许将其作为参数。这样就可以将错误暴露在编译器的检查之下。
二、错误处理
错误处理我认为也是非常好的一个环节。在没有定义错误处理的行为时,一个函数会抛出panic。这个panic就相当于是没有被捕获的错误。一般语言中会用try/catch这种表达式来捕获错误,但其实可读性不仅很差,而且完全没法对错误发生的地方进行预知。
在Rust中,所有可能出现的函数都会被打上Result返回值的标签。这个Result也是一个枚举泛型类型,但其功能实在是太多了,既能作为正确的返回值,也能承载错误信息向上传递。这个设计使得整个Rust中的错误查找和处理变得非常舒适。在Rust中,只要一个函数的返回值是Result,就可以在其中使用?操作符。?操作符的作用主要是将Result<T,E>解包成T(也就是默认为正确值),在错误传递链中具有几乎不可替代的作用。他不仅不会影响整体代码的可读性,更是将可能发生错误的位点清晰地标注在代码之中。
三、强大且规范的标准库
C++总是被人诟病的就是其巨大的标准库。而Rust作为一门新语言,没有C++承载的那么多历史使命,自然也就能够保证其标准库的设计风格始终一致。
Rust的标准库几乎全部按照面向对象的风格进行设计。其中使用较多的主要有Vec/Deque/(Hash/BTree)(Set/Map)等等。这些的声明非常规范。且都符合同一套API,也就是可以通过同一套成员函数进行操作。就算是对于内置类型,也就是primitive types,标准库都有非常多数学和二进制函数的支持,比如count_ones()就可以计算一个数二进制位中所有的1的个数,a.max(b)就可以取两个数中的最大值。这种全部面向对象的操作也为链式调用提供了空间。
如果想在C/C++中求三个数字的最大值,一般都会如下写
res = max(max(a, b), c);
但在Rust中,一般如下写
res = a
.max(b)
.max(c);
这样在长的时候就非常易读。采用这种设计的还有Iterator Trait,也就是基本上所有容器都声明了该Trait。配合Lambda表达式和map函数能够很好地处理链式操作。
四、宏
作为一个偏底层的语言,宏是必不可少的一项。但例如C/C++这种基于简单字符串替换的宏就带来了很多不安全不稳定的因素。Rust推出了过程宏作为解决方案,使得宏可以按照值和表达式来进行捕获,大大提高了宏的能力。在Rust中,宏一般具有特殊的使用标记——感叹号,可以让调用者明确该出调用的是一个宏而不是一个普通的函数。
过程宏分为属性宏派生宏和函数式宏。函数式宏表现得像普通的函数,并且可以具有可变参数,派生宏和属性宏则可以作用在过程和类之前,表现得像python里的装饰器。
// 函数式宏的用法
let v = vec![1, 2, 3];
println!("{}", 3);
dbg!(v);
五、现代的包管理体系cargo
cargo基本上是Rust工具链中不是必要但不可替代的一员。虽然其只是rustc的一层封装,但是在包管理上,Rust基本上是我目前使用过所有语言的第一梯队。C/C++几乎没有包管理可言,大家的工具都不成熟,装库更是只能靠手动make,非常原始。Rust作为新生代语言当然接入了网络库,只需要在Cargo.toml里写配置就可以自动下载自动编译,非常地高效和便捷。还有rustup作为工具链管理,可以非常方便地跨平台编译。