编程语言Rust介绍与简单入门

Written By BobAnkh, 2021-04-06(Tue), in Category Language

Language, Rust

摘要

本篇文章主要介绍Rust及其安装和开发环境的配置,加上基本构建过程和简单语法。相对高级的语法诸如所有权、生命周期、Trait、闭包等将留到以后介绍。

0 概述

Rust是一种使每个人都可以构建可靠、高效软件的编程语言。它是一种系统级编程语言,注重高性能可靠性生产力,支持结构化编程、函数式编程、面向对象编程等多种编程范式。

Rust是编译型语言,没有运行时(Runtime)和垃圾回收(Garbage Collector, GC)。Rust使用所有权机制来实现自动内存管理,并以此来保证内存安全。Rust是开源项目,目前托管在Github上,Rust官方软件包管理器为Cargo

高性能:Rust 速度惊人且内存利用率极高。由于没有运行时和垃圾回收,它能够胜任对性能要求特别高的服务,可以在嵌入式设备上运行,还能轻松和其他语言集成。

可靠性:Rust 丰富的类型系统和所有权模型保证了内存安全和线程安全,让您在编译期就能够消除各种各样的错误。

生产力:Rust 拥有出色的文档、友好的编译器和清晰的错误提示信息, 还集成了一流的工具——包管理器和构建工具, 智能地自动补全和类型检验的多编辑器支持, 以及自动格式化代码等等。

全世界已有数百家公司在生产环境中使用 Rust,以达到快速、跨平台、低资源占用的目的。很多著名且受欢迎的软件,例如 FirefoxDropboxCloudflare 都在使用 Rust。

1 特点

1.1 零成本抽象

零成本抽象是Rust实现高性能的核心。

1.2 所有权模型与内存安全

Rust通过强大的类型系统和所有权模型来保证内存安全与线程安全,在编译时消除很多可能存在的错误。

使用Rust语言编程不需要手动分配(malloc)释放(free)内存和资源。Rust通过所有权系统来判断资源分配和释放的时机,在资源不再使用自动调用析构函数并释放资源。所有权系统是Rust与其他语言最不同的一点,这也是Rust学习曲线较为陡峭的原因之一。

1.3 特征(trait)

Rust使用特征(trait)机制来实现抽象和代码复用,而非使用其他编程语言中常见的类(Class)。

特征(trait)的概念接近于其他编程语言的接口概念,trait可以包含方法声明和默认实现,但不能包含成员变量。

1.4 基于返回值的错误处理

Rust语言使用返回值来进行错误处理,Rust中可能产生错误或空的函数的返回值一般为Result<T, E>Option<T>类型,开发者必须在代码中显式的处理每一种情况,以此保证错误得到处理。

1.5 迭代器与闭包

Rust支持多种类型的迭代器、迭代器适配器和消费器。

Rust支持非装箱(Unboxed)闭包。

1.6 async/await与Future

Async/await是Rust提供的异步编程方式。

Rust支持异步函数(async function)、异步闭包(async closure)、异步代码块(async block)和在async标记的块中使用.await后置关键词。

Rust只提供了async-await语法和Future特征等必要功能,没有提供默认的事件循环机制/异步运行时(asynchronous run-time)。

1.7 Unsafe

强大的类型系统和所有权模型保证了Rust的安全性,但也限制了Rust实现部分功能的能力,比如外部函数接口(FFI)等。对此,Rust提供了unsafe关键词,在unsafe标记的块中,部分操作不会提供安全检查。

1.8 宏

Rust支持声明宏、过程宏、编译器插件等机制,显著提升了Rust的表达能力和易用性。

2 安装Rust

推荐采用官网上的方式进行安装rustup

windows直接下载官网提供的rustup-init.exe后运行并根据提示安装。

Unix系统可以直接使用curl以如下命令安装:curl https://sh.rustup.rs -sSf | sh,或在官方Forge中下载对应的脚本自行安装。

安装完成后,可以运行如下命令进行检测:rustc -V。该命令将会输出rustc的版本。同样可以运行如下命令来检测包管理工具Cargo是否正确安装:cargo -V。该命令将会输出cargo的版本。

如果仅仅只是想体验这门语言,也可以直接使用官方的在线 playground 进行体验。

3 配置Rust开发环境

推荐使用Visual Studio Code进行开发,这方面的插件和工具适配的比较好。推荐安装如下插件:

4 构建Rust的基本流程

4.1 创建并初始化新项目

常规而言,推荐采用Cargo以命令行方式构建Rust项目。

在一个你希望构建Rust项目的路径下,以命令行方式运行如下指令:cargo new <project>,此处<project>替换为你的项目名称,如:cargo new hello-world

这样会在当前目录下新建一个文件夹名为hello-world并完成其内部的一些初始化工作。

同样,也可以自己手动新建一个文件夹,在文件夹内运行cargo init命令来完成初始化过程。

初始化过程会构建如下结构:

.
├── Cargo.toml
├── .git
│   └── ...
├── .gitignore
└── src
    └── main.rs

src文件夹下存放的即是该Rust项目的源码,可以看到此时已经生成了一个main.rs文件。此外,Cargo会将该项目置于git管理下,同时会在.gitignore中加入忽略/target目录(当然如果本项目已经存在.git文件夹,即已经被git所管理,Cargo只会修改.gitignore文件)。该目录是默认情况下,Rust编译之后产物所在的文件夹。Cargo.toml是该项目的配置文件,其内包含的内容如下:

[package]
name = "hello-world"
version = "0.1.0"
authors = ["author <[email protected]>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

这里记录了项目的名称、版本、作者、使用的 Rust 版本和依赖。

main.rs中初始化的内容为:

fn main() {
   println!("Hello, world!");
}

这里是一个main函数,是直接调用main.rs时的程序入口,fn是定义函数的关键字,其内使用了一个宏(Macro)println!,用于打印内容并换行。关于宏的内容属于Rust的高级特征,在本文中暂且略去不表。此处是用于输出"Hello, world!"到终端。

4.2 编译运行项目

我们可以使用cargo run命令来编译该文件并运行:

$ cargo run
   Compiling hello-world v0.1.0 (/home/shenyx/hello-world)
    Finished dev [unoptimized + debuginfo] target(s) in 1.04s
     Running `target/debug/hello-world`
Hello, world!

也可以使用cargo build --release将代码编译为优化后的二进制可执行文件(只编译而并不会运行),可以进入到target/build/release目录下(如果不加--release参数则也可在target/build/debug目录下找到),运行可执行文件:

$ ./hello-world
Hello, world!

4.3 实用工具

  1. rustfmt

    在开发过程中,可以使用rustfmt根据社区代码风格自动格式化代码。安装rustfmt

    shell rustup component add rustfmt

    为了格式化整个Cargo项目:

    shell cargo fmt

  2. rustfix

    在开发过程中,可以使用rustfix工具,以命令cargo fix使用其来自动采纳编译器警告中给出的修改建议。

  3. clippy

    clippy 工具是一系列 lint 的集合,用于捕捉常见错误和改进 Rust 代码。安装clippy

    shell rustup component add clippy

    对于任何Cargo项目可以运行其lint:

    shell cargo clippy

事实上,cargo还有很多使用,例如cargo test可以运行代码内定义的测试等,更多细节建议参考官方指导书 The Cargo Book,此处不做过多赘述

5. 简单语法介绍

Rust语言中的注释为使用//,后跟注释内容,多行注释需在每行前均加上//

Rust中还有文档注释,文档注释使用三斜杠 /// 而不是两斜杆以支持 Markdown 注解来格式化文本。

5.1 定义变量

Rust采用let name: type = value;的形式定义变量,例如let a: i32 = 1;就定义了一个i32类型(32位有符号整型)的变量a,并赋值为1。

Rust的变量默认是不可变的,如果想要修改变量的值,需要在定义时加上mut,即例如:

let mut b: u32 = 1;
println!("b = {}", b);
b = 3;
println!("b = {}", b);

就定义了一个u32类型(32位无符号整型)的变量b,并赋值为1,然后将其修改为3。

常量使用const来定义,如const MAX_POWER: u32 = 10000;就定义了一个值为10000的u32类型的常量MAX_POWER

另外,Rust中,变量名一般采用snake_case,常量名一般采用SCREAMING_SNAKE_CASE

5.2 基本变量类型

最主要的、常用的基本变量类型为:

5.3 函数

参数传递等方面的问题涉及到所有权的概念,在这里不做细表

使用fn关键字来定义函数,Rust中函数名一般采用snake_case。Rust并不关心函数定义在使用它的地方的前面还是后面,只要定义了即可。

定义如下函数other_func

fn other_func(a: i32, b: u32) -> i32 {
    println!("This is another function!");
    println!("a = {}, b = {}", a, b);
    let result = a + 1;
    result    // 这是Rust推荐写法,较为简洁,也可以写成return result;
}

上述函数需要i32类型的参数a和u32类型的参数b,并将a+1的值返回,返回值是i32类型的。使用 return 关键字和指定值,可从函数中提前返回;但大部分函数隐式的返回最后的表达式(最后无分号的result即是一个表达式)。

可以在main函数中如此调用该函数:

let a: i32 = -1;
let b: u32 = 1;
let result: i32 = other_func(a, b);
println!("result = {}", result);

运行这些代码,我们可以看到其输出了result = 0在命令行中。

5.4 控制流

let x = 1;
if x == 1 {
    println!("Yes, x = 1!");
}
let x = 1;
if x == 2 {
    println!("Yes, x = 2!");
} else {
    println!("No, x != 2!");
}
let x = 3;
if x == 1 {
    println!("Yes, x = 1!")
} else if x == 2 {
    println!("Yes, x = 2!")
} else if x == 3 {
    println!("Yes, x = 3!")
} else {
    println!("No, this is Wrong")
}
let y = 10;
let condition = true;
let x = if condition { 
    y + 1 
} else { 
    6 
};
println!("x = {}", x);
let mut counter: u32 = 0;
loop {
    println!("Count: {}", counter);
    if counter == 9 {
        println!("Exit Loop!");
        break;
    }
    //counter = counter + 1;
    counter += 1;
}
let y = loop {
    counter += 1;
    if counter == 20 {
        // return value when break
        break counter * 2;
    }
};
println!("y = {}", y);
let mut i = 0;
while i != 10 {
    i += 1;
}
println!("i = {}", i);
let arr: [u32; 5] = [1, 2, 3, 4, 5];
for element in arr.iter() {
    println!("element = {}", element);
}
for element in &arr {
    println!("element = {}", element);
}

6 结语

事实上,Rust的编译器是相对较为严格的,而且提示和警告也比较清晰,不少时候出现编译问题直接按照编译器提示修改即可;而复杂程序过了编译器之后,出现运行时错误的概率也会大大降低~~虽然与编译器斗智斗勇也是一个必然过程~~

以上就是对于Rust这门编程语言的基本介绍和简单语法入门,更多内容留待后续再言,或可直接阅读官方教程 The Book 来进行更加深入的学习(Rust的官方教程真的写的很好)。

Other Articles

Previous Article: 在Ubuntu下挂载swap分区

Next Article: 利用docker-compose快速部署——以Cloudreve为例