1 关键字
只列出C++中没有的,相同含义的不列出:
关键字 | 含义 |
---|---|
as | 强制类型转换, 消除特定包含项的 trait 的歧义, 或者对 use 和 extern crate 语句中的项重命名 |
crate | 链接( link) 一个外部 crate 或一个代表宏定义的 crate 的宏变量 |
dyn | 动态分发 trait 对象 |
fn | 定义一个函数或 函数指针类型 (function pointer type) |
for | 遍历一个迭代器或实现一个 trait 或者指定一个更高级的生命周期 |
impl | 实现自有或 trait 功能 |
in | for 循环语法的一部分 |
let | 绑定一个变量,默认不可修改,可以多次使用并改变类型 |
loop | 无条件循环 |
match | 模式匹配 |
mod | 定义一个模块 |
move | 使闭包获取其所捕获项的所有权 |
mut | 表示引用、 裸指针或模式可以修改,但不能改变类型 |
pub | 表示结构体字段、 impl 块或模块的公有可见性 |
ref | 通过引用绑定 |
Self | 实现 trait 的类型的类型别名 |
self | 表示方法本身或当前模块 |
static | 表示全局变量或在整个程序执行期间保持其生命周期 |
super | 表示当前模块的父模块 |
trait | 定义一个 trait |
type | 定义一个类型别名或关联类型 |
unsafe | 表示不安全的代码、 函数、 trait 或实现 |
use | 引入外部空间的符号 |
where | 表示一个约束类型的从句 |
常量: | const MAX_POINTS: u32 = 100_000; |
2 数据类型
在 Rust 中, 每一个值都属于某一个 数据类型( data type) , 这告诉 Rust 它被指定为何种数据, 以便明确数据处理方式。 我们将看到两类数据类型子集: 标量( scalar) 和复合( compound)。
Rust 有四种基本的标量类型: 整型、 浮点型、 布尔类型(bool)和字符类型(char)。
2.1 整型
长度 | 有符号 | 无符号 |
---|---|---|
8-bit | i8 | u8 |
16-bit | i16 | u16 |
32-bit | i32(默认类型) | u32 |
64-bit | i64 | u64 |
128-bit | i128 | u128 |
arch(依赖系统架构,64或32) | isize | usize |
Rust的浮点数类型是 f32 和 f64 , 分别占 32 位和 64 位。 默认类型是 f64 , 因为在现代 CPU中, 它与 f32 速度几乎一样, 不过精度更高。
整型的运算存在类型范围溢出的问题,rust提供了带前缀的运算函数对运算结果进行处理:
Checked 前缀返回一个结果的 Option 值: 如果运算结果可以被结果类型正确表示就返回 Some(v),否则返回 None
// 10 和 20 的结果可以用 u8 表示
println!("{}", (10_u8).checked_add(20).unwrap());
//100 和 200 的和不能用 u8 表示
println!("{}", (100_u8).checked_add(200).unwrap());
println!("{}", (100_u8).checked_add(200).expect("result is too big"));
Wrapping 前缀返回正确的值对结果类型能表示的范围的余数
println!("{}", (100_u16).wrapping_mul(200));
println!("{}", (500_u16).wrapping_mul(500));//超出范围,因此我们得到 250000 对 2^16 取模
Saturating 前缀会返回被“截断”到这个类型能表示的最大或最小值
println!("{}", 32760_i16.saturating_add(10));//i16最大为32767,add超过范围
Overflowing 前缀返回一个 tuple(reulst, overflowed), result为wrapping计算的结果,overflowed表示是否溢出
//对u16来说移动 17 位太多了,2^17 对 2^16 取余等于 10
let (result, flag) = 5_u16.overflowing_shl(17);
println!("result:{}, overflowed:{}", result, flag);
2.2 char
Rust 的字符类型char代表一个单独的 Unicode字符(UTF-8),是一个 32 位的值。
Rust 永远不会进行 char 和其他任何类型之间的隐式转换:可以使用
as
将char转换为i32的整数,小于i32的类型会被截断- 整数转换为char,可以使用
as
(只能用于u8类型)或std::char
提供的转换函数 ```rust //使用as转换为32位整数 assert_eq!(‘‘ as i32, 42); //整数转换为char assert_eq!(42_u8 as char, ‘‘);
assert_eq!(‘8’.to_digit(10), Some(8));//字符8转换为10进制2 assert_eq!(std::char::from_digit(2, 10), Some(‘2’));//2转换位字符2
<a name="E7QKs"></a>
## 2.3 复合类型
<a name="rF7aq"></a>
### 元组
元组是一个将多个其他类型的值组合进一个复合类型的主要方式。** 元组长度固定:** 一旦声明, 其长度不会增大或缩小。
```rust
let tup: (i32, f64, u8) = (500, 6.4, 1); //定义元组
//元组解构
let (x, y, z) = tup; //使用新变量保存解构后值
let first = tup.0; //指定获取元组的哪个值,不能使用i遍历
数组
Rust 中, 数组中的值位于中括号内的逗号分隔的列表中:
let a = [1, 2, 3, 4, 5];
//或
let a: [i32; 5] = [1, 2, 3, 4, 5];//显示指定类型和个数
//或
let a = [1; 5] //指定个数和默认值
vector
一个 Vec 是一个长度可变的类型 T 的数组,它的元素都存储在堆上。创建vector的方法如下:
- Vec::new()空的vector,并push元素
- 用
vec!
宏来创建vector(相当于自动完成上面的操作) - 使用迭代器collect()
//创建vector方法1 let mut pal = Vec::new(); pal.push("step"); pal.push("on"); pal.push("no"); pal.push("pets"); assert_eq!(pal, vec!["step", "on", "no", "pets"]); //创建vector方法2 let mut primes = vec![2, 3, 5, 7]; assert_eq!(primes.iter().product::<i32>(), 210); //创建vector方法3 let v: Vec<i32> = (0..5).collect();//需要指定v类型 assert_eq!(v, [0, 1, 2, 3, 4]);
2.4 String类型
String类型不是基本类型,是标准库提供的扩展类型,有几种方式创建 String:
.to_string()
方法把 &str 转换为一个 String,这会拷贝字符串:let error_message = "too many pets".to_string();
format!()
宏类返回一个新的 String, 而且它不会再最后自动加上换行符:assert_eq!(format!("{}°{:02}′{:02}′′N", 24, 5, 23), "24°05′23′′N".to_string());
字符串的数组、切片、vector 都有两个方法
.concat()
和.join(sep)
,把多个字符串组合成一个:let bits = vec!["veni", "vidi", "vici"]; assert_eq!(bits.concat(), "venividivici"); assert_eq!(bits.join(", "), "veni, vidi, vici");
Rust提供一些类似字符串的类型用于不同的场景:
- 对于 Unicode 文本坚持使用 String 和 &str。
- 当处理文件名时,使用 std::path::PathBuf 和 &Path 来代替。
- 当处理完全不是 UTF8 编码的二进制数据时,使用 Vec 和 &[u8]。
- 当处理环境变量名称或者命令行参数这些由操作系统提供的内容时,使用 OsString 和 &OsStr。
当和以空字符结尾的 C 库交互时,使用 std::ffi::CString 和 &CStr。
2.5 struct结构体
结构体定义和C++基本一致,不同的是成员末尾使用逗号:
struct Rectangle { width: u32, height: u32, }
可以使用
impl
为结构体添加成员函数(C++是可以直接写在结构体中,rust没有类的概念,所以需要写在结构体外):impl Rectangle { fn area(&self) -> u32 { self.width * self.height } fn can_hold(&self, other: &Rectangle) -> bool { self.width > other.width && self.height > other.height } } //调用函数: fn main() { let rect1 = Rectangle { width: 30, height: 50, }; println!("rect1 is {}", rect1.area()); }
而对于下面这种成员函数,参数中没有
self
,意味着这属于类不属于对象(C++的概念),所以调用时需要使用结构体名::函数名
: ```rust impl Rectangle { fn square(size: u32) -> Rectangle {Rectangle { width: size, height: size, }
} }
fn main() { let sq = Rectangle::square(3); }
<a name="6rEOY"></a>
# 3 函数
Rust 代码中的函数和变量名使用 snake case 规范风格。 在 snake case 中, 所有字母都是小写并使用下划线分隔单词。<br />Rust 中的函数定义**以 fn 开始并在函数名后跟一对圆括号**。 大括号告诉编译器哪里是函数体的开始和结尾。Rust 不关心函数定义于何处, 只要定义了就行。
<a name="xjEvb"></a>
## 3.1 函数参数
```rust
fn another_function(x: i32, y: i32) {//参数和类型
println!("The value of x is: {}", x);
println!("The value of y is: {}", y);
}
3.2 返回值
函数可以向调用它的代码返回值。 我们并不对返回值命名, 但要在箭头( -> ) 后声明它的类型。使用 return 关键字指定返回值。
fn another_function(x: i32) -> i32 {//返回值类型
println!("The value of x is: {}", x);
return 100;
}
4 控制流
4.1 if语句
所有的 if 表达式都以 if 关键字开头, 其后跟一个条件(不需要括号):
fn main() {
let number = 3;
//if语句,表达式结果必须为bool类型
if number % 4 == 0 {
println!("number is divisible by 4");
} else if number % 3 == 0 {
println!("number is divisible by 3");
} else if number % 2 == 0 {
println!("number is divisible by 2");
} else {
println!("number is not divisible by 4, 3, or 2");
}
}
注意:
- Rust 并不会尝试自动地将非布尔值转换为布尔值。 必须总是显式地使用布尔值作为 if的条件。
- enum类型不能直接在if里判断(因为enum类没有实现==运算符),需要借助
**match**
或**if let**
进行判断4.2 循环
Rust 有三种循环: loop 、 while 和 for 。loop
loop可以执行一个循环,但是没有终止条件,需要在内部使用break返回,同时break可以携带返回值,将loop的最终结果赋给一个变量。let mut counter = 0; let result = loop {//loop返回值赋给了result变量 counter += 1; if counter == 10 { break counter * 2; //注意,break可以携带返回值 } };
while
fn main() { let mut number = 3; while number != 0 { println!("{}!", number); number = number - 1; } }
for
fn main() { let a = [10, 20, 30, 40, 50]; for element in a.iter() { //直接生成一个index的迭代器遍历,不直接使用index,防止越界 println!("the value is: {}", element); } println!(); for element in a.iter().rev() { //rev用来反转index序列,从后往前输出 println!("the value is: {}", element); } println!(); for i in 1..3 { //直接用index,不用迭代器 println!("the value is: {}", a[i]); } }