安装 rust
参考官网 安装 Rust - Rust 程序设计语言 (rust-lang.org)
Hello Rust
下面来创建和编写第一个 rust 程序,首先需要创建一个 rust 源代码文件,rust 源代码文件以.rs结尾,例如main.rs,而文件名称则以多个单词下划线分割,例如hello_rust.rs
编辑main.rs,输入如下内容
fn main() {
println!("Hello, world!");
}
在当前目录下,使用rustc编译并执行 rust 源代码文件,如下命令
rustc ./main.rs
执行成功后,控制台应该输出
Hello, world!信息
Rust Cargo
Cargo 是 Rust 的构建系统和包管理器。可使用 Cargo 来管理 Rust 项目,比如构建代码、下载依赖库,以及编译这些依赖库,使用官方 rust 安装包安装的 rust 自带 cargo,使用如下命令查看 cargo 版本
cargo --version
使用 cargo 创建新项目
cargo new project_name
进入项目名称对应的目录,其下主要由./src源代码目录以及./Cargo.toml两部分组成,src下包含程序的入口文件main.rs,而Cargo.toml则是当前项目的配置文件,有如下内容
[package]
name = "demo2" # 项目名称
version = "0.1.0" # 项目版本号
edition = "2021" # rust 核心版本号,例如 2015、2018、2021 版等
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] # 当前项目的依赖
配置 cargo 国内镜像源 [配置 cargo 国内镜像源 - Rust Crates 实践指南 - The Guide to Rust Crates (gitcode.host)](mirrors.gitcode.host/zzy/rust-cr… Cargo 国内镜像源 Rust 官方默认的 Cargo 源服务器为 crates.io,其同时也是,Cargo 的“注册表源”与 crates.io 本身相同。 也就是说,Cargo 也有一个在 github 存储库中提供的索引。)
简单来说,就是在$HOME/.cargo目录下,创建config文件,文件内容如下
[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
# 指定镜像
replace-with = '镜像源名' # 如:tuna、sjtu、ustc,或者 rustcc
# 注:以下源配置一个即可,无需全部
# 中国科学技术大学
[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"
# 上海交通大学
[source.sjtu]
registry = "https://mirrors.sjtug.sjtu.edu.cn/git/crates.io-index"
# 清华大学
[source.tuna]
registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git"
# rustcc社区
[source.rustcc]
registry = "https://code.aliyun.com/rustcc/crates.io-index.git"
cargo 命令还有一些其他用法,如下
cargo build
在当前项目目录下,此命令用于构建当前项目并生成可执行文件到
target/debug/project_name,windows 平台下生成的可执行文件名称为target/debug/project_name.exe
cargo run
在当前项目目录下,编译并运行当前项目
cargo check
快速检查代码确保其可以编译,但并不产生可执行文件
cargo build --release
当准备发布项目时,使用此命令来优化编译项目,使得 rust 代码运行更快
猜数游戏
通过一个简单的 rust 程序,快速了解一个 rust 程序的基本结构,如下代码
use std::io;
// Rng 是一个 trait, 它定义了随机数生成器应实现的方法
use rand::Rng;
// Ordering 是一个枚举, 其成员包括 Less(小于) Greater(大于) 和 Equal(等于)这是比较两个值时可能出现的三种结果
use std::cmp::Ordering;
fn main() {
println!("Guess the number!");
// 定义一个不可变变量, 用于保存生成的随机数
let secret_number = rand::thread_rng().gen_range(1..101);
// 循环体
loop {
println!("\nPlease input your guess.");
// mut 定义一个可变变量, 否则直接申明的变量默认为不可变
let mut guess = String::new();
// read_lin 从控制台读取一行信息; expect 处理此过程中的异常情况
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line.");
// rust 中允许新值来【遮蔽】一个变量, 这使得我们可以复用 guess 变量的名字而不是被迫定义新的变量
// trim 去除开头和结尾的空白字符 parse 将字符串解析为数字并且再次通过 expect 处理异常情况
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => {
println!("Please a number!");
continue;
}
};
println!("You guessed: {}", guess);
// 使用 match 表达式匹配 guess.cmp(&secret_number) 的结果
// 不同的结果通过不同的分支来处理
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small"),
Ordering::Greater => println!("Too big"),
Ordering::Equal => {
println!("You win");
break;
}
}
}
}
变量
变量 && 可变性
首先尝试运行如下程序
fn main(){
let x = 1;
x = 2;
}
会抛出错误信息cannot assign twice to immutable variable,意为:不能对不可变变量二次赋值,这说明 rust 中的变量声明默认是不可变的
如果想要声明一个可变变量,则需要通过mut关键字,如下代码
let mut x = 5;
常量
常量使用const关键字而非let关键字申明,常量自始至终不可变
常量必须注明值的类型
常量只能设置为常量表达式,而不能是函数调用的结果或是只能在运行时计算得到的值
一个常量申明的示例代码如下
// 申明 HOURS_UNIT 常量, 标注其类型为 u32
const HOURS_UNIT: u32 = 1000 * 60 * 60;
变量遮蔽
变量遮蔽允许再次声明相同名称的新变量,同时新声明的变量其数据类型也可被改变,如下代码
let x = 1;
let x = "AAA";
数据类型
Rust 是一种静态类型(statically typed)的语言,这意味着它必须在编译期知道所有变量的类型
标量类型
标量 表示单个值,Rust 有 4 个基本的标量类型:整型、浮点型、布尔型和字符
整数类型
rust 中的整数类型分为无符号(unsigned)类型以及有符号(integer)类型,如下表格(rust 默认使用的整型为i32)
| 长度 | 有符号 | 无符号 |
|---|---|---|
| 8 位 | i8 | u8 |
| 16 位 | i16 | u16 |
| 32 位 | i32 | u32 |
| 64 位 | i64 | u64 |
| 128 位 | i128 | u128 |
| arch | isize | usize |
其中,不同长度的整型,它们各自的范围计算方式分别如下
-
有符号
-
无符号
其中,n代表对应数据类型的长度
例如,u8类型可表示0 ~ 255之间的数值,而有符号的i8可表示-128 ~ 127之间的数值,因为i8中额外使用一个二进制位来表示符号
另外,isize和usize类型取决于程序运行的计算机体系结构,在表中表示为arch,若使用 64 位架构系统则为 64 位,若使用 32 位架构系统则为 32 位
整型溢出
当给定的值范围超出对应的数据类型时,例如
let num: u8 = 512;
一个u8最大可表示的范围为0-255,这里对其赋值为512则会产生整型溢出,官方文档的说法如下
比方说有一个
u8,它可以存放从 0 到 255 的值。那么当你将其修改为范围之外的值,比如 256,则会发生 整型溢出(integer overflow),这会导致两种行为的其中一种。当在调试(debug)模式编译时,Rust 会检查整型溢出,若存在这些问题则使程序在编译时 panic。Rust 使用 panic 这个术语来表明程序因错误而退出。第 9 章 “panic!与不可恢复的错误”会详细介绍 panic。在当使用
--release参数进行发布(release)模式构建时,Rust 不检测会导致 panic 的整型溢出。相反当检测到整型溢出时,Rust 会进行一种被称为二进制补码包裹(two’s complement wrapping)的操作。简而言之,大于该类型最大值的数值会被“包裹”成该类型能够支持的对应数字的最小值。比如在u8的情况下,256 变成 0,257 变成 1,依此类推。程序不会 panic,但是该变量的值可能不是你期望的值。依赖整型溢出包裹的行为不是一种正确的做法。要显式处理溢出的可能性,可以使用标准库针对原始数字类型提供的以下一系列方法:
- 使用
wrapping_*方法在所有模式下进行包裹,例如wrapping_add- 如果使用
checked_*方法时发生溢出,则返回None值- 使用
overflowing_*方法返回该值和一个指示是否存在溢出的布尔值- 使用
saturating_*方法使值达到最小值或最大值
浮点类型
浮点数是带有小数点的数字,在 rust 中,针对浮点数,也有两种类型,分别为f32和f64,默认为f64,其精度更高
所有的浮点类型都是有符号的,示例代码如下
let x = 2.0; // 默认为 f64
let y: f32 = 3.0; // 可以指定类型为 f32
布尔类型
与大多数编程语言一样,rust 中也有布尔类型,用于表示两个可能的值true false,布尔类型的大小为1字节,使用示例如下
let t = true;
let f: bool = false;
字符类型
rust 中的字符类型字面量使用''表示,与此相对的字符串字面量则使用""表示
rust 中,字符类型占用大小为4字节,表示的是一个 Unicode 标量值,其范围为 U+0000 ~ U+D7FF 和 U+E000~U+10FFFF,能够表示包括但不限于:中文、日文、韩文的文字,以及 emoji 等字符
复合类型
复合类型指的是将多个值组合到一个类型中,rust 有两种基本的复合类型:元组、数组
元组
元组是将多种类型的多个值组合到一个复合类型中的一种基本方式。元组的长度是固定的:声明后,它们就无法增长或缩小
声明一个元组,如下代码
let tup: (i32, f64, u8) = (500, 6.4, 1);
如果需要从元组中取值,一种是使用模式匹配来解构,如下
let tup: (i32, f64, u8) = (500, 6.4, 1);
let (x, y, z) = tup;
没有任何值的元组 () 是一种特殊的类型,只有一个值,也写成 ()。该类型被称为单元类型(unit type),该值被称为单元值(unit value)。如果表达式不返回任何其他值,就隐式地返回单元值
数组
数组和元组类似,也是将多个值组合在一起,不同的是,数组只能存放一种类型的元素。数组的声明如下所示
// 数组会在栈上开辟空间用于存放数据
let a = [1, 2, 3, 4, 5];
在创建数组时,指定其类型和长度
let arr: [u64; 5] = [1, 2, 3, 4, 5];
初始化默认元素
// 会创建长度为 5, 默认值为 3 的数组, 至于数组类型则会自动推导
let arr = [3; 5];
如果访问数组的索引超出数组的长度,则会产生数组越界,因为在 rust 中,不允许访问超出数组索引之外的内存区域,如下代码
let arr = [0; 5];
let item = arr[5];
上述代码在执行cargo build时,会打印index out of bounds:...的异常信息,但这只针对于较为明显的数组越界能够在编译时被检查出,参考如下代码
let arr = [0; 5];
let indexs = [5, 6, 7];
let item = arr[indexs[0]];
上面这种稍微隐藏得深一点的数组越界错误,就无法在编译期就检测出来,需要到运行时,才会打印相关异常信息