最近在学习Rust语言,感觉这门语言有点意思,因此写一篇文章分享。我不会去介绍Rust的基本语法,什么变量声明,if..else..,循环等等。这些东西,文档介绍的很清楚,何必多此一举。本文主要介绍Rust这门语言的特点,和其它语言的对比。
虽然学习时间不长,但已经体会到Rust语言的特点就是极致的追求“安全”,追求代码的稳健。为了达到这个目标,Rust增加了不少编码“限制”(或者称之为规则),让码农们要循规蹈矩的写代码,从而实现“编译即无bug”的目标。(前提条件,编码的逻辑必须正确:D)
在我看来,系统编程语言可以实现对性能要求极高,同时对硬件要求很低的语言,经常用于平台开发。一般来说,就是C和C++。而这两门语言,要写出“安全”的代码,还是有一定门槛的。而Rust就针对“安全”这个方向,做了很多工作,同时保持高性能。
Rust语言的工程管理工具叫做cargo,它负责工程建立,编译,包的依赖等等。
下面开始介绍Rust的一些特点:
一、工程化支持:
按惯例,我们创建一个helloworld工程。
cargo new hellworld
cargo会自动在当前目录创建一个helloword目录,作为项目目录。
ls该目录,可以看到有一个文件Cargo.toml和src目录,前者Cargo.toml为项目描述和管理文件,起到了项目管理和“Makefile”的作用。
接下来看rust源代码
当创建Rust工程时,rust会自动生成main.rs的原文件,恰好是我们需要的打印hello world。接下来看rust的编译和运行。
执行cargo build进行编译,生成debug版本的可运行程序。如果要生成release版本,只需要加上--release选项。生成的二进制,cargo也会放在指定目录target目录下。
接下来让我们用Rust生成一个库工程 —— 使用--lib参数。
与bin工程类似,cargo在addlib/src目录下自动生成了一个lib.rs文件。
文件中的mod tests是Rust对单元测试的支持。这里我们先不介绍,只要了解这段代码是用于单元测试即可。—— 从这里又可以看到Rust的一个好处:D
下面让我们写个简单的add函数,作为addlib的接口。
然后编译和做单元测试如下:
可以看出,rust对工程化的支持还是很友好的:
1. 免去了编写工程文件的痛苦;
2. 格式化的目录结构
3. 支持单元测试
4. 支持自动生成文档(cargo doc)
二、“强制”的Error Handler
Rust的错误分为两种:Panic和Results。前者表示“永远”不应该发生的事情,后者表示程序逻辑产生的错误。
下面看一个产生panic的情况
编译运行这个rust程序
可以看到箭头所指的位置,程序直接panic了。这种错误是无法在编译阶段捕捉的。
下面看第二种情况,rust对错误检查的“强制”处理。
使用cargo编译,会报告三个warning,提示没有检查返回值
这时,我们要么加上返回值检查,要么使用Rust提供的unwrap方法来消除警告。
使用match语句检查my_function的返回值,按照Rust的语法,要求必须把所有可能的值都检查到,不然就会报错。可见上面的代码写起来还是有些繁琐的。而用unwrap的方式,就是像Rust承诺,这个函数不会返回出错。那么当my_function返回出错时,会怎么样呢?
说话不算数,Rust就死给你看:)
三、对库的版本管理
作为从业多年的码农,大家一定会遇到对第三方库的管理和依赖问题。这里Rust的cargo提供了很友好的方式,通过Cargo.toml文件进行第三方库的管理。
这个示例要利用rand库生成随机数,其版本为0.4.*版本。使用cargo build进行编译
其会自动下载符合定义的库,rand v0.4.6版本。
如果某天要升级到0.5版本,只需要更改toml文件,改为rand = "0.5.*",然后执行cargo update更新库。
是不是很方便呢?
四、保证内存安全
绝大部分crash和安全问题都是由于内存问题导致的,比较常见的几种问题:
1. 引用空指针;
2. 引用未初始化指针;
3. 引用释放后的指针;
4. 溢出或越界;
5. Double Free;
Rust通过严格语法检查,所有权系统和生命周期来保证了以上几种情况不会在Rust下发生,或者极容易定位。
看下面的示例代码
这里有4个常见的内存错误,如代码中的注释。使用cargo build编译,看看rust是如何阻止这些错误的。
这里一共出现了3个编译错误,分别防止了1)未初始化的引用;2)引用已经销毁的变量;3)避免Double Free。Rust中的Box::new是用来从堆上申请内存的,类似于C/C++中的指针,当超过其生命周期后,会自动释放内存。但是当将一个指针的值赋给另外一个指针,即将内存的“所有权”交给了第二个指针。第一个指针就不能再被引用。
代码中一共有4个错误,cargo阻止了三个,剩下的overflow的问题怎么办呢?让我们先把上面的3个问题纠正,让cargo编译出可执行程序并运行。
在运行到overflow的代码时,直接崩溃,并明确的告知这里出现了越界访问。这样的“死亡”方式,要比踩踏了其他内存导致程序不知道挂在哪里的方式,要好上很多。我相信,大家都经历过修复内存越界的Bug,查起来可不是那么简单。与其带着“炸弹”运行,不如直接在现场挂掉暴露问题的好。
本想一篇文章介绍完Rust所有权系统,包含Ownership,Borrowing,和Lifetimes。这三方面保证Rust的内存安全,也就是保证构成健壮的程序。下一篇文章,将介绍Rust所有权系统
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。