前言 本文面向有编程经验的Rust学习者,使用 Cargo 作为包管理器。
参考链接
[0]Rust Course
[1]rfcs
[2]Rust权威指南(第二版)
[3]Rust算法教程
[4]rust-lang-learn
[5]rust-lang-blog
Rust中的变量 Hello world!
1 2 3 fn main () { println! ("Hello, world!" ); }
变量命名 在命名方面,和其它语言没有区别,不过当给变量命名时,需要遵循 Rust 命名规范 ,也不能使用 Rust关键字 。
变量绑定 上面这条语句将 "hello world" 字符串绑定给了变量a,"hello world" 的所有权也就属于a。在Rust中任何内存对象都是有主人的,而且一般情况下完全属于它的主人,绑定就是把这个对象绑定给一个变量,让这个变量成为它的主人。
所有权转移后,该对象之前的主人就会丧失对该对象的所有权。
1 2 3 4 5 6 7 8 9 10 11 12 13 fn main () { let a = String ::from ("hello world" ); println! ("a 的值: {}" , a); let b = a; println! ("b 的值: {}" , b); }
变量可变性 Rust 的变量在默认情况下是不可变的 。
1 2 3 4 5 6 fn main () { let a = 10 ; println! ("a is a number: {}" , a); println! ("a is a number: {}" , a); }
我们可以通过 mut 关键字让变量变为可变的 。
1 2 3 4 5 6 fn main () { let mut a = 10 ; println! ("a is a number: {}" , a); a = 20 ; println! ("a is a number: {}" , a); }
忽略未使用的变量 如果你创建了一个变量却不在任何地方使用它,Rust 通常会给你一个警告,因为这可能会是个 BUG。但是有时创建一个不会被使用的变量是有用的,比如你正在设计原型或刚刚开始一个项目。这时你希望告诉 Rust 不要警告未使用的变量,为此可以用下划线作为变量名的开头 :
1 2 3 4 fn main () { let _x = 5 ; let y = 10 ; }
变量解构 let 表达式不仅仅用于变量的绑定,还能进行复杂变量的解构:从一个相对复杂的变量中,匹配出该变量的一部分内容:
1 2 3 4 5 6 7 8 fn main () { let (a, mut b): (bool ,bool ) = (true , false ); println! ("a = {:?}, b = {:?}" , a, b); b = true ; assert_eq! (a, b); }
解构式赋值 在 Rust 1.59 版本后,我们可以在赋值语句的左式中使用元组、切片和结构体模式了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 struct Struct { e: i32 } fn main () { let (a, b, c, d, e); (a, b) = (1 , 2 ); [c, .., d, _] = [1 , 2 , 3 , 4 , 5 ]; Struct { e, .. } = Struct { e: 5 }; println! ("a = {}, b = {}, c = {}, d = {}, e = {}" , a, b, c, d, e); }
变量与常量 变量的值不能更改可能让你想起其他另一个很多语言都有的编程概念:常量 (constant )。与不可变变量一样,常量也是绑定到一个常量名且不允许更改的值,但是常量和变量之间存在一些差异:
常量不允许使用 mut。常量不仅仅默认不可变,而且自始至终不可变 ,因为常量在编译完成后,已经确定它的值。
常量使用 const 关键字而不是 let 关键字来声明,并且值的类型必须 标注。
下面是一个常量声明的例子,其常量名为 MAX_POINTS,值设置为 100,000。(Rust 常量的命名约定是全部字母都使用大写,并使用下划线分隔单词,另外对数字字面量可插入下划线以提高可读性):
1 const MAX_POINTS: u32 = 100_000 ;
常量可以在任意作用域内声明,包括全局作用域,在声明的作用域内,常量在程序运行的整个过程中都有效。对于需要在多处代码共享一个不可变的值时非常有用,例如游戏中允许玩家赚取的最大点数或光速。
变量遮蔽 Rust 允许声明相同的变量名,在后面声明的变量会遮蔽掉前面声明的:
1 2 3 4 5 6 7 8 9 10 11 12 13 fn main () { let x = 5 ; let x = x + 1 ; { let x = x * 2 ; println! ("The value of x in the inner scope is: {}" , x); } println! ("The value of x is: {}" , x); }
变量遮蔽的用处在于,如果你在某个作用域内无需再使用之前的变量(在被遮蔽后,无法再访问到之前的同名变量)。
基本类型 Rust 每个值都有其确切的数据类型,总的来说可以分为两类:基本类型和复合类型。 基本类型意味着它们往往是一个最小化原子类型,无法解构为其它类型(一般意义上来说),由以下组成:
数值类型:有符号整数 (i8, i16, i32, i64, isize)、 无符号整数 (u8, u16, u32, u64, usize) 、浮点数 (f32, f64)、以及有理数、复数
字符串:字符串字面量和字符串切片 &str
布尔类型:true 和 false
字符类型:表示单个 Unicode 字符,存储为 4 个字节
单元类型:即 () ,其唯一的值也是 ()
数值类型 整数类型 整数 是没有小数部分的数字。之前使用过的 i32 类型,表示有符号的 32 位整数( i 是英文单词 integer 的首字母,与之相反的是 u,代表无符号 unsigned 类型)。下表显示了 Rust 中的内置的整数类型:
长度
有符号类型
无符号类型
范围
8 位
i8
u8
-128 ~ 127 / 0 ~ 255
16 位
i16
u16
-32,768 ~ 32,767 / 0 ~ 65,535
32 位
i32
u32
-2³¹ ~ 2³¹-1 / 0 ~ 2³²-1
64 位
i64
u64
-2⁶³ ~ 2⁶³-1 / 0 ~ 2⁶⁴-1
128 位
i128
u128
-2¹²⁷ ~ 2¹²⁷-1 / 0 ~ 2¹²⁸-1
视架构而定(32位/64位)
isize
usize
同 i32/u32 或 i64/u64
有符号类型 (iN):
范围:-2^(N-1) ~ 2^(N-1) - 1
无符号类型 (uN):
整型字面量可以用下表的形式书写:
数字字面量
示例
十进制
98_222
十六进制
0xff
八进制
0o77
二进制
0b1111_0000
字节 (仅限于 u8)
b'A'
Rust 整型默认使用 i32,例如 let i = 1,那 i 就是 i32 类型,因此你可以首选它,同时该类型也往往是性能最好的。isize 和 usize 的主要应用场景是用作集合的索引。
整型溢出
假设有一个 u8 ,它可以存放从 0 到 255 的值。那么当你将其修改为范围之外的值,比如 256,则会发生整型溢出 。关于这一行为 Rust 有一些有趣的规则:当在 debug 模式编译时,Rust 会检查整型溢出,若存在这些问题,则使程序在编译时 panic (崩溃,Rust 使用这个术语来表明程序因错误而退出)。
在当使用 --release 参数进行 release 模式构建时,Rust 不 检测溢出。相反,当检测到整型溢出时,Rust 会按照补码循环溢出(two’s complement wrapping )的规则处理。简而言之,大于该类型最大值的数值会被补码转换成该类型能够支持的对应数字的最小值。比如在 u8 的情况下,256 变成 0,257 变成 1,依此类推。程序不会 panic ,但是该变量的值可能不是你期望的值。依赖这种默认行为的代码都应该被认为是错误的代码。
要显式处理可能的溢出,可以使用标准库针对原始数字类型提供的这些方法:
使用 wrapping_* 方法在所有模式下都按照补码循环溢出规则处理,例如 wrapping_add
如果使用 checked_* 方法时发生溢出,则返回 None 值
使用 overflowing_* 方法返回该值和一个指示是否存在溢出的布尔值
使用 saturating_* 方法,可以限定计算后的结果不超过目标类型的最大值或低于最小值,例如:
1 2 assert_eq! (100u8 .saturating_add (1 ), 101 );assert_eq! (u8 ::MAX.saturating_add (127 ), u8 ::MAX);
下面是一个演示wrapping_*方法的示例:
1 2 3 4 5 fn main () { let a : u8 = 255 ; let b = a.wrapping_add (20 ); println! ("{}" , b); }
浮点类型 浮点类型数字 是带有小数点的数字,在 Rust 中浮点类型数字也有两种基本类型: f32 和 f64,分别为 32 位和 64 位大小。默认浮点类型是 f64,在现代的 CPU 中它的速度与 f32 几乎相同,但精度更高。
下面是一个演示浮点数的示例:
1 2 3 4 5 fn main () { let x = 2.0 ; let y : f32 = 3.0 ; }
浮点数根据 IEEE-754 标准实现。f32 类型是单精度浮点型,f64 为双精度。
浮点陷阱
浮点数由于底层格式的特殊性,导致了如果在使用浮点数时不够谨慎,就可能造成危险,有两个原因:
浮点数往往是你想要数字的近似表达 浮点数类型是基于二进制实现的,但是我们想要计算的数字往往是基于十进制,例如 0.1 在二进制上并不存在精确的表达形式,但是在十进制上就存在。这种不匹配性导致一定的歧义性,更多的,虽然浮点数能代表真实的数值,但是由于底层格式问题,它往往受限于定长的浮点数精度,如果你想要表达完全精准的真实数字,只有使用无限精度的浮点数才行
浮点数在某些特性上是反直觉的 例如大家都会觉得浮点数可以进行比较,对吧?是的,它们确实可以使用 >,>= 等进行比较,但是在某些场景下,这种直觉上的比较特性反而会害了你。因为 f32 , f64 上的比较运算实现的是 std::cmp::PartialEq 特征(类似其他语言的接口),但是并没有实现 std::cmp::Eq 特征,但是后者在其它数值类型上都有定义,说了这么多,可能大家还是云里雾里,用一个例子来举例:
Rust 的 HashMap 数据结构,是一个 KV 类型的 Hash Map 实现,它对于 K 没有特定类型的限制,但是要求能用作 K 的类型必须实现了 std::cmp::Eq 特征,因此这意味着你无法使用浮点数作为 HashMap 的 Key,来存储键值对,但是作为对比,Rust 的整数类型、字符串类型、布尔类型都实现了该特征,因此可以作为 HashMap 的 Key。
为了避免上面说的两个陷阱,你需要遵守以下准则:
避免在浮点数上测试相等性
当结果在数学上可能存在未定义时,需要格外的小心。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 fn main () { let abc : (f32 , f32 , f32 ) = (0.1 , 0.2 , 0.3 ); let xyz : (f64 , f64 , f64 ) = (0.1 , 0.2 , 0.3 ); println! ("abc (f32)" ); println! (" 0.1 + 0.2: {:x}" , (abc.0 + abc.1 ).to_bits ()); println! (" 0.3: {:x}" , (abc.2 ).to_bits ()); println! (); println! ("xyz (f64)" ); println! (" 0.1 + 0.2: {:x}" , (xyz.0 + xyz.1 ).to_bits ()); println! (" 0.3: {:x}" , (xyz.2 ).to_bits ()); println! (); assert! (abc.0 + abc.1 == abc.2 ); assert! (xyz.0 + xyz.1 == xyz.2 ); }
对 f32 类型做加法时,0.1 + 0.2 的结果是 3e99999a,0.3 也是 3e99999a,因此 f32 下的 0.1 + 0.2 == 0.3 通过测试,但是到了 f64 类型时,结果就不一样了,因为 f64 二进制精度高很多,导致了 0.1 + 0.2 并不严格等于 0.3,它们可能在小数点 N 位后存在误差。0.1 + 0.2 以 4 结尾,但是 0.3 以3结尾,这个区别导致 f64 下的测试失败了,并且抛出了异常。
NaN
对于数学上未定义的结果,例如对负数取平方根 -42.1.sqrt() ,会产生一个特殊的结果:Rust 的浮点数类型使用 NaN (not a number) 来处理这些情况。
**所有跟 NaN 交互的操作,都会返回一个 NaN**,而且 NaN 不能用来比较,下面的代码会崩溃:
1 2 3 4 fn main () { let x = (-42.0_f32 ).sqrt (); assert_eq! (x, x); }
出于防御性编程的考虑,可以使用 is_nan() 等方法,可以用来判断一个数值是否是 NaN :
1 2 3 4 5 6 fn main () { let x = (-42.0_f32 ).sqrt (); if x.is_nan () { println! ("未定义的数学行为" ) } }
数字运算 Rust 支持所有数字类型的基本数学运算:加法、减法、乘法、除法和取模运算。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 fn main () { let a = 10 ; let b : i32 = 20 ; let c = 30i32 ; println! ("加法: {} + {} = {}" , a, b, a + b); println! ("减法: {} - {} = {}" , b, a, b - a); println! ("乘法: {} * {} = {}" , a, c, a * c); println! ("除法: {} / {} = {}" , c, a, c / a); println! ("取模: {} % {} = {}" , b, a, b % a); let million : i64 = 1_000_000 ; println! ("百万的平方: {}" , million.pow (2 )); let x = 10.0 ; let y = 3.5f32 ; println! ("浮点除法: {:.2}" , x / 2.0 ); println! ("f32: {:.3}" , y * 2.0 ); }
位运算 Rust 的位运算基本上和其他语言一样
运算符
说明
& 位与
相同位置均为1时则为1,否则为0
| 位或
相同位置只要有1时则为1,否则为0
^ 异或
相同位置不相同则为1,相同则为0
! 位非
把位中的0和1相互取反,即0置为1,1置为0
<< 左移
所有位向左移动指定位数,右位补0(会改变符号位)
>> 右移
所有位向右移动指定位数,行为取决于类型 : • 有符号类型(i8/i16/i32等):算术右移,保持符号位 • 无符号类型(u8/u16/u32等):逻辑右移,补0
重要说明:
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 fn main () { let a = 0b1100u8 ; let b = 0b1010u8 ; println! ("a = {:08b} ({})" , a, a); println! ("b = {:08b} ({})" , b, b); println! (); println! ("a & b = {:08b} ({})" , a & b, a & b); println! ("a | b = {:08b} ({})" , a | b, a | b); println! ("a ^ b = {:08b} ({})" , a ^ b, a ^ b); println! ("!a = {:08b} ({})" , !a, !a); }
关于移位运算:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 fn main () { let neg : i8 = -120 ; println! ("========== 移位操作对比 ==========\n" ); println! ("测试值: {} = {:08b}\n" , neg, neg as u8 ); println! ("--- 右移2位 ---" ); println! ("算术右移: {} >> 2 = {:3} = {:08b} (补1)" , neg, neg >> 2 , (neg >> 2 ) as u8 ); println! ("逻辑右移: {} >>> 2 = {:3} = {:08b} (补0)" , neg, (neg as u8 ) >> 2 , (neg as u8 ) >> 2 ); println! ("\n--- 左移2位 ---" ); println! ("算术左移: {} << 2 = {:3} = {:08b}" , neg, neg << 2 , (neg << 2 ) as u8 ); println! ("逻辑左移: {} <<< 2 = {:3} = {:08b}" , neg, (neg as u8 ) << 2 , (neg as u8 ) << 2 ); }
序列(Range) Rust 提供了一个非常简洁的方式,用来生成连续的数值,例如 1..5,生成从 1 到 4 的连续数字,不包含 5 ;1..=5,生成从 1 到 5 的连续数字,包含 5,它的用途很简单,常常用于循环中:
1 2 3 for i in 1 ..=5 { println! ("{}" ,i); }
序列只允许用于数字或字符类型,原因是它们可以连续,同时编译器在编译期可以检查该序列是否为空,字符和数字值是 Rust 中仅有的可以用于判断是否为空的类型。如下是一个使用字符类型序列的例子:
1 2 3 for i in 'a' ..='z' { println! ("{}" ,i); }
字符类型 Rust 的字符不仅仅是 ASCII,所有的 Unicode 值都可以作为 Rust 字符,包括单个的中文、日文、韩文、emoji 表情符号等等,都是合法的字符类型。Unicode 值的范围从 U+0000 ~ U+D7FF 和 U+E000 ~ U+10FFFF。由于 Unicode 都是 4 个字节编码,因此Rust的字符类型也是占用 4 个字节。
1 2 3 4 5 6 fn main () { let c = 'z' ; let z = 'ℤ' ; let g = '国' ; let heart_eyed_cat = '😻'; }
布尔类型 Rust 中的布尔类型有两个可能的值:true 和 false,布尔值占用内存的大小为 1 个字节:
1 2 3 4 5 6 7 8 9 fn main () { let t = true ; let f : bool = false ; if f { println! ("这是段毫无意义的代码" ); } }
单元类型 单元类型就是 (),唯一的值也是 () 。
1 2 3 4 5 6 7 8 9 10 11 fn normal () -> () { }fn main () { }println! ("hello" );fn diverge () -> ! { panic! ("crash!" ); }
特性
说明
定义
() 既是类型也是唯一的值
大小
0 字节,不占内存
main 返回
返回 (),不是”无返回值”
用途
占位符、表示”不关心返回值”
区别
-> () 有返回值,-> ! 才是永不返回(发散函数,即没有返回值)
语句和表达式 Rust 的函数体是由一系列语句组成,最后由一个表达式来返回值,例如:
1 2 3 4 5 fn add_with_extra (x: i32 , y: i32 ) -> i32 { let x = x + 1 ; let y = y + 5 ; x + y }
语句会执行一些操作但是不会返回一个值,而表达式会在求值后返回一个值,因此在上述函数体的三行代码中,前两行是语句,最后一行是表达式。
对于 Rust 语言而言,这种基于语句(statement)和表达式(expression)的方式是非常重要的,你需要能明确的区分这两个概念 ,但是对于很多其它语言而言,这两个往往无需区分。基于表达式是函数式语言的重要特征,表达式总要返回值 。
语句 1 2 3 let a = 8 ;let b : Vec <f64 > = Vec ::new ();let (a, c) = ("hi" , false );
以上都是语句,它们完成了一个具体的操作,但是并没有返回值,因此是语句。
由于 let 是语句,因此不能将 let 语句赋值给其它值,如下形式是错误的:
表达式 表达式会进行求值,然后返回一个值。例如 5 + 6,在求值后,返回值 11,因此它就是一条表达式。
表达式可以成为语句的一部分,例如 let y = 6 中,6 就是一个表达式,它在求值后返回一个值 6。调用一个函数是表达式,因为会返回一个值,调用宏也是表达式,用花括号包裹最终返回一个值的语句块也是表达式,总之,能返回值,它就是表达式:
1 2 3 4 5 6 7 8 fn main () { let y = { let x = 3 ; x + 1 }; println! ("The value of y is: {}" , y); }
上面使用一个语句块表达式将值赋给 y 变量,语句块长这样:
该语句块是表达式的原因是:它的最后一行是表达式,返回了 x + 1 的值,注意 x + 1 不能以分号结尾,否则就会从表达式变成语句, 表达式不能包含分号 。这一点非常重要,一旦你在表达式后加上分号,它就会变成一条语句,再也不会 返回一个值,请牢记!
最后,表达式如果不返回任何值,会隐式地返回一个 () 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 fn main () { assert_eq! (ret_unit_type (), ()) } fn ret_unit_type () { let x = 1 ; let y = if x % 2 == 1 { "odd" } else { "even" }; let z = if x % 2 == 1 { "odd" } else { "even" }; }
函数 以 add 函数为例,声明函数的关键字 fn,函数名 add(),参数 i: i32 和 j: i32,参数类型和返回值类型都是 i32。
1 2 3 fn add (i: i32 , j: i32 ) -> i32 { i + j }
函数要点
函数名和变量名使用蛇形命名法(snake_case),例如 fn add_two() {}
函数的位置可以随便放,Rust 不关心我们在哪里定义了函数,只要有定义即可
每个函数参数都需要标注类型
函数参数 Rust 是静态类型语言,因此需要你为每一个函数参数都标识出它的具体类型,例如:
1 2 3 4 5 6 7 8 fn main () { another_function (5 , 6.1 ); } fn another_function (x: i32 , y: f32 ) { println! ("The value of x is: {}" , x); println! ("The value of y is: {}" , y); }
another_function 函数有两个参数,其中 x 是 i32 类型,y 是 f32 类型,然后在该函数内部,打印出这两个值。这里去掉 x 或者 y 的任何一个的类型,都会报错。
函数返回 在 Rust 中函数就是表达式,因此我们可以把函数的返回值直接赋给调用者。
函数的返回值就是函数体最后一条表达式的返回值,当然我们也可以使用 return 提前返回,下面的函数使用最后一条表达式来返回一个值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 fn plus_number (x:i32 ) -> i32 { if x == 5 { return 10 ; } else { x + 10 } } fn main () { let x = plus_number (6 ); println! ("The value of x is: {}" , x); }
返回单元类型
单元类型 (),是一个零长度的元组。它没啥作用,但是可以用来表达一个函数没有返回值:
函数没有返回值,那么返回一个 ()
通过 ; 结尾的语句返回一个 ()
1 2 3 4 5 6 7 fn plus_number (x:i32 ) { x + 5 ; } fn plus_number (x:i32 ) -> () { x + 5 ; }
永不返回的发散函数 !
当用 ! 作函数返回类型的时候,表示该函数永不返回( diverging functions ),特别的,这种语法往往用做会导致程序崩溃的函数:
1 2 3 fn dead_end () -> ! { panic! ("dead_end" ); }
下面的函数创建了一个无限循环,该循环永不跳出,因此函数也永不返回:
1 2 3 4 5 fn forever () -> ! { loop { }; }
所有权和借用 所有权 Rust 通过所有权来管理内存 ,这种检查只发生在编译期,因此对于程序运行期,不会有性能上的损失。
Rust 中每一个值都被一个变量所拥有,该变量被称为值的所有者
一个值同时只能被一个变量所拥有,或者说一个值只能拥有一个所有者
当所有者(变量)离开作用域范围时,这个值将被丢弃(drop)
变量作用域 作用域是一个变量在程序中有效的范围,假如有这样一个变量:
变量 s 绑定到了一个字符串字面值,该字符串字面值是硬编码到程序代码中的。s 变量从声明的点开始直到当前作用域的结束都是有效的:
1 2 3 4 5 { let s = "hello" ; }
简而言之,s 从创建开始就有效,然后有效期持续到它离开作用域为止,可以看出,就作用域来说,Rust 语言跟其他编程语言没有区别。
所有权转移 先来看一段代码:
这段代码并没有发生所有权的转移,原因很简单: 代码首先将 5 绑定到变量 x,接着拷贝 x 的值赋给 y,最终 x 和 y 都等于 5,因为整数是 Rust 基本数据类型,是固定大小的简单值,因此这两个值都是通过自动拷贝 的方式来赋值的,都被存在栈中,完全无需在堆上分配内存。整个过程中的赋值都是通过值拷贝的方式完成(发生在栈中),因此并不需要所有权转移。
移动(move)
然后再来看一段代码:
1 2 3 4 5 6 fn main () { let s1 = String ::from ("hello" ); let s2 = s1; println! ("{}, world!" , s1); }
String 类型是一个复杂类型,由存储在栈中的堆指针 、字符串长度 、字符串容量 共同组成。String 类型指向了一个堆上的空间,这里存储着它的真实数据。当 s1 被赋予 s2 后,Rust 认为 s1 不再有效,因此也无需在 s1 离开作用域后 drop 任何东西,这就是把所有权从 s1 转移给了 s2,s1 在被赋予 s2 后就马上失效了 。
1 2 3 4 5 6 7 8 9 pub struct String { vec: Vec <u8 >, } pub struct Vec <T> { ptr: *mut T, len: usize , cap: usize , }
深拷贝(clone)
首先,Rust 永远也不会自动创建数据的 “深拷贝” 。因此,任何自动 的复制都不是深拷贝,可以被认为对运行时性能影响较小。
如果我们确实 需要深度复制 String 中堆上的数据,而不仅仅是栈上的数据,可以使用一个叫做 clone 的方法。
1 2 3 4 let s1 = String ::from ("hello" );let s2 = s1.clone ();println! ("s1 = {}, s2 = {}" , s1, s2);
这段代码能够正常运行,说明 s2 确实完整的复制了 s1 的数据。
如果代码性能无关紧要,例如初始化程序时或者在某段时间只会执行寥寥数次时,你可以使用 clone 来简化编程。但是对于执行较为频繁的代码(热点路径),使用 clone 会极大的降低程序性能,需要小心使用!
浅拷贝(copy)
关键点 :
Copy 类型在赋值时会按位复制 (栈上复制)
实现 Copy 的类型不能 拥有堆上的数据
Copy 是隐式 的,Clone 是显式 的
Rust 有一个叫做 Copy 的特征,可以用在类似整型这样在栈中存储的类型。如果一个类型拥有 Copy 特征,一个旧的变量在被赋值给其他变量后仍然可用,也就是赋值的过程即是拷贝的过程。
如下是一些 Copy 的类型:
所有整数类型,比如 u32
布尔类型,bool,它的值是 true 和 false
所有浮点数类型,比如 f64
字符类型,char
元组,当且仅当其包含的类型也都是 Copy 的时候。比如,(i32, i32) 是 Copy 的,但 (i32, String) 就不是
不可变引用 &T ,但是注意:可变引用 &mut T 是不可以 Copy的。
一个类型可以是 Copy 的,当且仅当 :
✅ 所有字段都实现了 Copy
✅ 不拥有任何堆上的资源
✅ 没有实现 Drop trait
✅ 是固定大小的(Sized)
函数传值和返回 将值传递给函数,一样会发生 移动 或者 复制,就跟 let 语句一样,下面的代码展示了所有权、作用域的规则:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 fn main () { let s = String ::from ("hello" ); takes_ownership (s); let x = 5 ; makes_copy (x); } fn takes_ownership (some_string: String ) { println! ("{}" , some_string); } fn makes_copy (some_integer: i32 ) { println! ("{}" , some_integer); }
同样的,函数返回值也有所有权,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 fn main () { let s1 = gives_ownership (); let s2 = String ::from ("hello" ); let s3 = takes_and_gives_back (s2); } fn gives_ownership () -> String { let some_string = String ::from ("hello" ); some_string } fn takes_and_gives_back (a_string: String ) -> String { a_string }
所有权很强大,避免了内存的不安全性,但是也带来了一个新麻烦: 总是把一个值传来传去来使用它 。 传入一个函数,很可能还要从该函数传出去,结果就是语言表达变得非常啰嗦,幸运的是,Rust 提供了新功能解决这个问题。
引用和借用 引用与解引用 常规引用是一个指针类型,指向了对象存储的内存地址。在下面代码中,我们创建一个 i32 值的引用 y,然后使用解引用运算符来解出 y 所使用的值:
1 2 3 4 5 6 7 fn main () { let x = 5 ; let y = &x; assert_eq! (5 , x); assert_eq! (5 , *y); }
变量 x 存放了一个 i32 值 5。y 是 x 的一个引用。可以断言 x 等于 5。然而,如果希望对 y 的值做出断言,必须使用 *y 来解出引用所指向的值(也就是解引用 )。一旦解引用了 y,就可以访问 y 所指向的整型值并可以与 5 做比较。
不可变引用 下面的代码,我们用 s1 的引用作为参数传递给 calculate_length 函数,而不是把 s1 的所有权转移给该函数:
1 2 3 4 5 6 7 8 9 10 11 fn main () { let s1 = String ::from ("hello" ); let len = calculate_length (&s1); println! ("The length of '{}' is {}." , s1, len); } fn calculate_length (s: &String ) -> usize { s.len () }
这里,& 符号即是引用,它们允许你使用值,但是不获取所有权。通过 &s1 语法,我们创建了一个指向 s1 的引用 ,但是并不拥有它。因为并不拥有这个值,当引用离开作用域后,其指向的值也不会被丢弃。
可变引用 1 2 3 4 5 6 7 8 9 fn main () { let mut s = String ::from ("hello" ); change (&mut s); } fn change (some_string: &mut String ) { some_string.push_str (", world" ); }
首先,声明 s 是可变类型,其次创建一个可变的引用 &mut s 和接受可变引用参数 some_string: &mut String 的函数。可变引用同时只能存在一个,可变引用并不是随心所欲、想用就用的,它有一个很大的限制: 同一作用域,特定数据只能有一个可变引用 :
1 2 3 4 5 6 let mut s = String ::from ("hello" );let r1 = &mut s;let r2 = &mut s;println! ("{}, {}" , r1, r2);
1 2 3 4 5 6 7 8 9 10 11 error[E0499]: cannot borrow `s` as mutable more than once at a time 同一时间无法对 `s` 进行两次可变借用 --> src/main.rs:5 :14 | 4 | let r1 = &mut s; | ------ first mutable borrow occurs here 首个可变引用在这里借用 5 | let r2 = &mut s; | ^^^^^^ second mutable borrow occurs here 第二个可变引用在这里借用 6 |7 | println! ("{}, {}" , r1, r2); | -- first borrow later used here 第一个借用在这里使用
在数据被可变借用期间,原所有者完全失去对数据的访问权 ,既不能读也不能写。
1 2 3 4 5 6 let mut s = String ::from ("hello" );let r1 = &mut s;s.push_str (", world!" ); println! ("{}" , r1);
1 2 3 4 5 6 7 8 9 10 error[E0499]: cannot borrow `s` as mutable more than once at a time --> src/main.rs:5 :5 | 4 | let r1 = &mut s; | ------ first mutable borrow occurs here 5 | s.push_str (", world!" ); | ^ second mutable borrow occurs here 6 | 7 | println! ("{}" , r1); | -- first borrow later used here
复合类型 字符串 Rust 中的字符串主要有两种核心类型:String 和 &str。
两种核心字符串类型 &str(字符串切片)1 2 3 4 5 6 let s1 : &str = "Hello, Rust!" ;let string = String ::from ("Hello" );let s2 : &str = &string[0 ..3 ];
特点:
不可变引用(borrowed)
固定大小的视图
通常作为函数参数使用
String(可增长字符串)1 2 3 4 5 6 7 8 9 let mut s = String ::new ();let s = String ::from ("Hello" );let s = "Hello" .to_string ();let s = String ::with_capacity (100 );
特点:
堆上分配,可增长
拥有所有权(owned)
可修改
内存布局对比 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 ┌─────────────────────────────────────────────────────────────────────┐ │ String 内存布局 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ 栈上 (Stack) 堆上 (Heap) │ │ ┌────────────────┐ ┌───┬───┬───┬───┬───┐ │ │ │ ptr ──────────────────────────►│ H │ e │ l │ l │ o │ │ │ ├────────────────┤ └───┴───┴───┴───┴───┘ │ │ │ len = 5 │ │ │ ├────────────────┤ │ │ │ capacity = 8 │ │ │ └────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────────────────────────┐ │ &str 内存布局 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ 栈上 (Stack) 只读内存/堆 │ │ ┌────────────────┐ ┌───┬───┬───┬───┬───┐ │ │ │ ptr ──────────────────────────►│ H │ e │ l │ l │ o │ │ │ ├────────────────┤ └───┴───┴───┴───┴───┘ │ │ │ len = 5 │ (胖指针: ptr + len) │ │ └────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘
String 常用操作 创建与修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 fn main () { let mut s = String ::from ("Hello" ); s.push_str (", World" ); s.push ('!' ); s.insert (0 , '🎉'); s.insert_str (1 , " " ); let new_s = s.replace ("World" , "Rust" ); s.pop (); s.remove (0 ); s.truncate (5 ); s.clear (); }
字符串连接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 fn main () { let s1 = String ::from ("Hello" ); let s2 = String ::from ("World" ); let s3 = s1 + " " + &s2; let s1 = String ::from ("Hello" ); let s4 = format! ("{} {} {}" , s1, s2, "Rust" ); let mut s5 = String ::from ("Hello" ); s5.push_str (" World" ); let s6 = ["Hello" , " " , "World" ].concat (); let s7 = ["Hello" , "World" ].join (" " ); }
UTF-8 编码特性 Rust 字符串强制使用 UTF-8 编码,这带来一些特殊行为:
字符与字节的区别
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 fn main () { let s = "你好Rust" ; println! ("字节长度: {}" , s.len ()); println! ("字符数量: {}" , s.chars ().count ()); for b in s.bytes () { print! ("{} " , b); } for c in s.chars () { print! ("{} " , c); } }
索引限制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 fn main () { let s = String ::from ("你好" ); let slice = &s[0 ..3 ]; let first_char = s.chars ().nth (0 ); }
类型转换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 fn main () { let s : String = "hello" .to_string (); let s : String = String ::from ("hello" ); let s : String = "hello" .to_owned (); let s : String = "hello" .into (); let string = String ::from ("hello" ); let slice : &str = &string; let slice : &str = string.as_str (); let slice : &str = &string[..]; let s = String ::from ("hello" ); let bytes : Vec <u8 > = s.into_bytes (); let s = String ::from_utf8 (bytes).unwrap (); let s = "hello" ; let bytes : &[u8 ] = s.as_bytes (); let s = std::str ::from_utf8 (bytes).unwrap (); let n = 42 ; let s = n.to_string (); let s = format! ("{}" , n); let n : i32 = "42" .parse ().unwrap (); let n = "42" .parse::<i32 >().unwrap (); }
Deref 强制转换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 fn print_str (s: &str ) { println! ("{}" , s); } fn main () { let owned = String ::from ("Hello" ); let borrowed = "World" ; print_str (&owned); print_str (borrowed); }
性能与实践 容量预分配
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 fn main () { let mut s = String ::new (); for i in 0 ..1000 { s.push_str (&i.to_string ()); } let mut s = String ::with_capacity (4000 ); for i in 0 ..1000 { s.push_str (&i.to_string ()); } println! ("Capacity: {}" , s.capacity ()); }
避免不必要的克隆
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 fn process (s: String ) { }let s = String ::from ("hello" );process (s.clone ()); fn process_ref (s: &str ) { }process_ref (&s);use std::borrow::Cow;fn maybe_modify (s: &str ) -> Cow<str > { if s.contains ("old" ) { Cow::Owned (s.replace ("old" , "new" )) } else { Cow::Borrowed (s) } }
函数参数建议
1 2 3 4 5 6 7 8 9 10 11 12 13 fn greet (name: &str ) { println! ("Hello, {}" , name); } greet ("World" );greet (&String ::from ("Rust" ));fn take_ownership (s: String ) { }
常见陷阱总结
陷阱
问题
解决方案
直接索引
s[0] 编译错误
使用 s.chars().nth(0)
无效切片边界
&s[0..2] panic(中文)
确保在字符边界切分
+ 运算符
左侧所有权被移动
使用 format! 或 clone()
比较长度
len() 返回字节数
使用 chars().count()
空字符串检查
多种方式
优先用 is_empty()
切片 切片(Slice)是 Rust 中非常核心的概念,它是对连续内存序列 的一个引用视图 ,不拥有数据所有权。
切片基本概念 什么是切片
1 2 3 4 5 6 7 8 fn main () { let arr = [1 , 2 , 3 , 4 , 5 ]; let slice : &[i32 ] = &arr[1 ..4 ]; println! ("{:?}" , slice); }
切片的本质
1 2 3 4 5 6 7 8 9 10 11 12 ┌─────────────────────────────────────────────────────────────────────┐ │ 切片是"胖指针" │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ 普通指针 (8 bytes) 切片/胖指针 (16 bytes) │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ ptr │ │ ptr │ ──► 指向数据起始位置 │ │ └──────────────┘ ├──────────────┤ │ │ │ len │ ──► 元素数量 │ │ └──────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘
切片的内存布局 1 2 3 4 5 6 7 8 9 fn main () { let arr : [i32 ; 5 ] = [10 , 20 , 30 , 40 , 50 ]; let slice : &[i32 ] = &arr[1 ..4 ]; println! ("数组地址: {:p}" , &arr); println! ("切片指向: {:p}" , slice.as_ptr ()); println! ("切片长度: {}" , slice.len ()); println! ("切片大小: {} bytes" , std::mem::size_of_val (&slice)); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 内存示意图: arr (栈上连续内存): ┌──────┬──────┬──────┬──────┬──────┐ │ 10 │ 20 │ 30 │ 40 │ 50 │ └──────┴──────┴──────┴──────┴──────┘ [0] [1] [2] [3] [4] ▲ │ │ │ └─────────────┘ slice 覆盖范围 [1..4] slice (胖指针): ┌─────────────────┐ │ ptr ──────────────► 指向 arr[1] 的地址 ├─────────────────┤ │ len = 3 │ └─────────────────┘
切片语法详解 Range 语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 fn main () { let arr = [0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ]; let a = &arr[2 ..5 ]; let b = &arr[2 ..=5 ]; let c = &arr[..4 ]; let d = &arr[6 ..]; let e = &arr[..]; let full = &arr[0 ..arr.len ()]; println! ("a: {:?}" , a); println! ("b: {:?}" , b); println! ("c: {:?}" , c); println! ("d: {:?}" , d); println! ("e: {:?}" , e); }
范围类型对照表
语法
类型
含义
示例结果
start..end
Range
[start, end)
[1..4] → 索引1,2,3
start..=end
RangeInclusive
[start, end]
[1..=4] → 索引1,2,3,4
start..
RangeFrom
[start, 末尾]
[2..] → 从索引2到末尾
..end
RangeTo
[0, end)
[..3] → 索引0,1,2
..=end
RangeToInclusive
[0, end]
[..=3] → 索引0,1,2,3
..
RangeFull
全部
[..] → 所有元素
常见切片类型 数组切片 &[T]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 fn main () { let arr : [i32 ; 5 ] = [1 , 2 , 3 , 4 , 5 ]; let slice : &[i32 ] = &arr[..]; let vec : Vec <i32 > = vec! [1 , 2 , 3 , 4 , 5 ]; let slice : &[i32 ] = &vec[1 ..4 ]; let sub_slice : &[i32 ] = &slice[0 ..2 ]; println! ("{:?}" , sub_slice); }
字符串切片 &str
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 fn main () { let s1 : &str = "Hello, World!" ; let string = String ::from ("Hello, Rust!" ); let s2 : &str = &string[0 ..5 ]; let chinese = "你好世界" ; let s3 = &chinese[0 ..3 ]; println! ("{}" , s3); }
可变切片 &mut [T]
1 2 3 4 5 6 7 8 9 10 11 12 13 fn main () { let mut arr = [1 , 2 , 3 , 4 , 5 ]; let slice : &mut [i32 ] = &mut arr[1 ..4 ]; slice[0 ] = 20 ; slice[1 ] = 30 ; slice[2 ] = 40 ; println! ("{:?}" , arr); }
切片与所有权类型的关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ┌─────────────────────────────────────────────────────────────────────┐ │ 所有权类型 vs 借用类型 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ Owned │ │ Borrowed │ │ │ │ (拥有数据) │ ──Deref──► │ (借用视图) │ │ │ └─────────────┘ └─────────────┘ │ │ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ String │ ──────────────► │ &str │ │ │ └─────────────┘ └─────────────┘ │ │ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ Vec<T> │ ──────────────► │ &[T] │ │ │ └─────────────┘ └─────────────┘ │ │ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ [T; N] │ ──────────────► │ &[T] │ │ │ │ (数组) │ │ │ │ │ └─────────────┘ └─────────────┘ │ │ │ └─────────────────────────────────────────────────────────────────────┘
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 fn main () { let vec : Vec <i32 > = vec! [1 , 2 , 3 ]; let slice : &[i32 ] = &vec; let slice : &[i32 ] = vec.as_slice (); let slice : &[i32 ] = &vec[..]; let arr : [i32 ; 3 ] = [1 , 2 , 3 ]; let slice : &[i32 ] = &arr; let slice : &[i32 ] = &arr[..]; let string : String = String ::from ("hello" ); let slice : &str = &string; let slice : &str = string.as_str (); }
切片常用方法 基础方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 fn main () { let slice : &[i32 ] = &[10 , 20 , 30 , 40 , 50 ]; println! ("长度: {}" , slice.len ()); println! ("是否为空: {}" , slice.is_empty ()); println! ("第一个: {:?}" , slice.first ()); println! ("最后一个: {:?}" , slice.last ()); println! ("索引2: {:?}" , slice.get (2 )); println! ("索引10: {:?}" , slice.get (10 )); println! ("slice[2] = {}" , slice[2 ]); }
迭代与遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 fn main () { let slice = &[1 , 2 , 3 , 4 , 5 ]; for item in slice.iter () { print! ("{} " , item); } println! (); for (index, item) in slice.iter ().enumerate () { println! ("[{}] = {}" , index, item); } let mut arr = [1 , 2 , 3 , 4 , 5 ]; let slice = &mut arr[..]; for item in slice.iter_mut () { *item *= 2 ; } println! ("{:?}" , arr); }
查找与搜索
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 fn main () { let slice = &[3 , 1 , 4 , 1 , 5 , 9 , 2 , 6 ]; println! ("包含 4: {}" , slice.contains (&4 )); println! ("4 的位置: {:?}" , slice.iter ().position (|&x| x == 4 )); println! ("以 [3,1] 开头: {}" , slice.starts_with (&[3 , 1 ])); println! ("以 [2,6] 结尾: {}" , slice.ends_with (&[2 , 6 ])); let sorted = &[1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 ]; println! ("二分查找 5: {:?}" , sorted.binary_search (&5 )); println! ("二分查找 10: {:?}" , sorted.binary_search (&10 )); }
分割与连接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 fn main () { let slice = &[1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ]; let (left, right) = slice.split_at (4 ); println! ("左: {:?}, 右: {:?}" , left, right); let parts : Vec <&[i32 ]> = slice.split (|&x| x % 3 == 0 ).collect (); println! ("{:?}" , parts); for chunk in slice.chunks (3 ) { println! ("块: {:?}" , chunk); } for window in slice.windows (3 ) { println! ("窗口: {:?}" , window); } }
排序与反转(需要可变切片)
1 2 3 4 5 6 7 8 9 10 11 12 fn main () { let mut arr = [3 , 1 , 4 , 1 , 5 , 9 , 2 , 6 ]; let slice = &mut arr[..]; slice.sort (); println! ("排序后: {:?}" , slice); slice.reverse (); println! ("反转后: {:?}" , slice); }
复制与填充
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 fn main () { let src = [1 , 2 , 3 ]; let mut dst = [0 ; 5 ]; dst[..3 ].copy_from_slice (&src); println! ("{:?}" , dst); dst.fill (7 ); println! ("{:?}" , dst); dst.fill_with (|| 42 ); println! ("{:?}" , dst); let mut arr = [1 , 2 , 3 , 4 , 5 ]; arr.swap (0 , 4 ); println! ("{:?}" , arr); }
函数参数中的切片 切片作为参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 fn sum (numbers: &[i32 ]) -> i32 { numbers.iter ().sum () } fn sum_vec (numbers: &Vec <i32 >) -> i32 { numbers.iter ().sum () } fn main () { let arr = [1 , 2 , 3 , 4 , 5 ]; let vec = vec! [1 , 2 , 3 , 4 , 5 ]; println! ("数组求和: {}" , sum (&arr)); println! ("Vec求和: {}" , sum (&vec)); println! ("部分求和: {}" , sum (&arr[1 ..4 ])); println! ("部分求和: {}" , sum (&vec[..3 ])); }
可变切片参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 fn double_values (slice: &mut [i32 ]) { for item in slice.iter_mut () { *item *= 2 ; } } fn main () { let mut arr = [1 , 2 , 3 , 4 , 5 ]; double_values (&mut arr[1 ..4 ]); println! ("{:?}" , arr); double_values (&mut arr); println! ("{:?}" , arr); }
返回切片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 fn first_half (slice: &[i32 ]) -> &[i32 ] { let mid = slice.len () / 2 ; &slice[..mid] } fn first_half_mut (slice: &mut [i32 ]) -> &mut [i32 ] { let mid = slice.len () / 2 ; &mut slice[..mid] } fn main () { let arr = [1 , 2 , 3 , 4 , 5 , 6 ]; let half = first_half (&arr); println! ("{:?}" , half); }
元组 元组是 Rust 中一种重要的复合类型,可以将多个不同类型 的值组合成一个单一的复合值。
元组基本概念 什么是元组
1 2 3 4 5 6 fn main () { let tuple : (i32 , f64 , char , &str ) = (42 , 3.14 , 'R' , "Rust" ); println! ("{:?}" , tuple); }
元组的特点
1 2 3 4 5 6 7 8 9 10 11 ┌─────────────────────────────────────────────────────────────────────┐ │ 元组特性 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ ✓ 固定长度 - 一旦声明,长度不可改变 │ │ ✓ 异构类型 - 每个元素可以是不同类型 │ │ ✓ 有序集合 - 元素按位置索引访问 │ │ ✓ 值类型 - 存储在栈上(如果所有元素都是栈类型) │ │ ✓ 可解构 - 支持模式匹配解构 │ │ │ └─────────────────────────────────────────────────────────────────────┘
元组的创建与类型 基本创建方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 fn main () { let t1 : (i32 , f64 , bool ) = (100 , 2.5 , true ); let t2 = (100 , 2.5 , true ); let t3 : (i32 ,) = (42 ,); let not_tuple = (42 ); let unit : () = (); let nested : ((i32 , i32 ), (f64 , f64 )) = ((1 , 2 ), (3.0 , 4.0 )); println! ("t1: {:?}" , t1); println! ("t3: {:?}" , t3); println! ("nested: {:?}" , nested); }
元组类型签名
1 2 3 4 5 6 7 8 9 10 fn main () { let a : (i32 , i32 ) = (1 , 2 ); let b : (i32 , i64 ) = (1 , 2 ); let c : (i64 , i32 ) = (1 , 2 ); }
内存布局 1 2 3 4 5 6 fn main () { let tuple : (u8 , u32 , u8 ) = (1 , 2 , 3 ); println! ("元组大小: {} bytes" , std::mem::size_of_val (&tuple)); println! ("元组对齐: {} bytes" , std::mem::align_of_val (&tuple)); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ┌─────────────────────────────────────────────────────────────────────┐ │ (u8, u32, u8) 实际内存布局 │ ├─────────────────────────────────────────────────────────────────────┤ │ │ │ 声明顺序: (u8, u32, u8) │ │ 理论大小: 1 + 3(padding) + 4 + 1 + 3(padding) = 12 bytes │ │ │ │ ═══════════════════════════════════════════════════════════ │ │ │ │ 实际布局(Rust 编译器重排优化后): │ │ │ │ ┌────────────────────────────┬──────┬──────┬──────────────┐ │ │ │ u32 │ u8 │ u8 │ padding │ │ │ │ 4 bytes │ 1B │ 1B │ 2 bytes │ │ │ └────────────────────────────┴──────┴──────┴──────────────┘ │ │ offset: 0 4 5 6 8 │ │ │ │ 实际大小: 4 + 1 + 1 + 2(padding) = 8 bytes │ │ 对齐要求: 4 bytes (最大成员 u32 的对齐) │ │ │ └─────────────────────────────────────────────────────────────────────┘
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #[repr(C)] struct TupleC (u8 , u32 , u8 );struct TupleRust (u8 , u32 , u8 );fn main () { println! ("repr(C) 大小: {} bytes" , std::mem::size_of::<TupleC>()); println! ("repr(Rust) 大小: {} bytes" , std::mem::size_of::<TupleRust>()); }
访问元组元素 索引访问(点号语法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 fn main () { let tuple = (500 , 6.4 , "hello" , true ); let first = tuple.0 ; let second = tuple.1 ; let third = tuple.2 ; let fourth = tuple.3 ; println! ("第一个元素: {}" , first); println! ("第二个元素: {}" , second); println! ("第三个元素: {}" , third); println! ("第四个元素: {}" , fourth); }
解构(Destructuring)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 fn main () { let tuple = (1 , 2.0 , "three" ); let (x, y, z) = tuple; println! ("x={}, y={}, z={}" , x, y, z); let (a, _, c) = tuple; println! ("a={}, c={}" , a, c); let (first, ..) = tuple; println! ("first={}" , first); let tuple5 = (1 , 2 , 3 , 4 , 5 ); let (head, .., tail) = tuple5; println! ("head={}, tail={}" , head, tail); let nested = ((1 , 2 ), (3 , 4 )); let ((a, b), (c, d)) = nested; println! ("a={}, b={}, c={}, d={}" , a, b, c, d); }
可变元组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 fn main () { let mut tuple = (1 , 2 , 3 ); tuple.0 = 100 ; tuple.1 = 200 ; println! ("{:?}" , tuple); let (ref mut a, ref mut b, _) = tuple; *a = 1000 ; *b = 2000 ; println! ("{:?}" , tuple); }
单元类型 () 我们之间讲到的单元类型也是一种特殊的元组。
单元类型的概念
1 2 3 4 5 6 7 8 9 10 fn main () { let unit : () = (); println! ("() 的大小: {} bytes" , std::mem::size_of::<()>()); assert_eq! (unit, ()); }
单元类型的用途
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 fn no_return () { println! ("这个函数没有显式返回值" ); } fn explicit_unit () -> () { println! ("显式声明返回 ()" ); } fn main () { let result = no_return (); println! ("result: {:?}" , result); let x = { let y = 5 ; }; println! ("x: {:?}" , x); let mut a = 0 ; let b = (a = 5 ); println! ("b: {:?}" , b); } struct Container <T> { value: T, } fn main () { let empty : Container<()> = Container { value: () }; } fn might_fail () -> Result <(), String > { Ok (()) }
元组与函数 作为函数参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 fn print_point (point: (i32 , i32 )) { println! ("Point: ({}, {})" , point.0 , point.1 ); } fn print_point_destructured ((x, y): (i32 , i32 )) { println! ("Point: ({}, {})" , x, y); } fn main () { let p = (10 , 20 ); print_point (p); print_point_destructured (p); }
作为返回值(多返回值)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 fn min_max (numbers: &[i32 ]) -> Option <(i32 , i32 )> { if numbers.is_empty () { return None ; } let mut min = numbers[0 ]; let mut max = numbers[0 ]; for &num in numbers.iter ().skip (1 ) { if num < min { min = num; } if num > max { max = num; } } Some ((min, max)) } fn divide_with_remainder (dividend: i32 , divisor: i32 ) -> (i32 , i32 ) { let quotient = dividend / divisor; let remainder = dividend % divisor; (quotient, remainder) } fn main () { if let Some ((min, max)) = min_max (&[3 , 1 , 4 , 1 , 5 , 9 , 2 , 6 ]) { println! ("最小值: {}, 最大值: {}" , min, max); } let (quotient, remainder) = divide_with_remainder (17 , 5 ); println! ("17 / 5 = {} 余 {}" , quotient, remainder); }
交换值的惯用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 fn main () { let mut a = 1 ; let mut b = 2 ; println! ("交换前: a={}, b={}" , a, b); (a, b) = (b, a); println! ("交换后: a={}, b={}" , a, b); std::mem::swap (&mut a, &mut b); println! ("再次交换: a={}, b={}" , a, b); }
结构体 结构体是 Rust 中创建自定义数据类型的基本方式,将多个相关值组合成一个整体。
结构体形式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 struct User { username: String , email: String , age: u32 , active: bool , } struct Color (u8 , u8 , u8 );struct Point (f64 , f64 );struct UserId (u64 ); struct Marker ;fn main () { let user = User { username: String ::from ("alice" ), email: String ::from ("alice@example.com" ), age: 25 , active: true , }; let red = Color (255 , 0 , 0 ); let origin = Point (0.0 , 0.0 ); let id = UserId (12345 ); let marker = Marker; println! ("用户名: {}" , user.username); println! ("红色值: {}" , red.0 ); println! ("用户ID: {}" , id.0 ); println! ("Marker大小: {} bytes" , std::mem::size_of::<Marker>()); }
1 2 3 4 5 6 7 8 ┌────────────────┬─────────────────┬─────────────────┬──────────────────┐ │ 特性 │ 常规结构体 │ 元组结构体 │ 单元结构体 │ ├────────────────┼─────────────────┼─────────────────┼──────────────────┤ │ 字段名称 │ 有 │ 无 │ 无 │ │ 访问方式 │ .name │ .0 .1 .2 │ N/A │ │ 内存大小 │ 字段和+对齐 │ 字段和+对齐 │ 0 │ │ 主要用途 │ 数据建模 │ NewType/简单 │ 标记/trait │ └────────────────┴─────────────────┴─────────────────┴──────────────────┘
创建与初始化 基本创建与字段简写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 struct Point { x: i32 , y: i32 , } fn main () { let p1 = Point { x: 10 , y: 20 }; let p2 = Point { y: 50 , x: 30 }; let x = 100 ; let y = 200 ; let p3 = Point { x, y }; println! ("p1: ({}, {})" , p1.x, p1.y); println! ("p3: ({}, {})" , p3.x, p3.y); }
结构体更新语法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 struct Config { host: String , port: u16 , timeout: u32 , debug: bool , } fn main () { let config1 = Config { host: String ::from ("localhost" ), port: 8080 , timeout: 30 , debug: true , }; let config2 = Config { port: 3000 , ..config1 }; println! ("{}" , config1.port); println! ("{}" , config1.debug); println! ("config2 port: {}" , config2.port); }
字段访问与修改 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 struct Rectangle { width: u32 , height: u32 , } fn main () { let rect = Rectangle { width: 30 , height: 50 }; println! ("宽: {}, 高: {}" , rect.width, rect.height); let area = rect.width * rect.height; println! ("面积: {}" , area); let mut rect2 = Rectangle { width: 10 , height: 20 }; rect2.width = 100 ; rect2.height = 200 ; println! ("修改后: {}x{}" , rect2.width, rect2.height); let rect_ref = ▭ println! ("通过引用: {}" , rect_ref.width); modify_rect (&mut rect2); println! ("函数修改后: {}x{}" , rect2.width, rect2.height); } fn modify_rect (r: &mut Rectangle) { r.width *= 2 ; r.height *= 2 ; }
所有权与借用 字段的移动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 struct Container { data: String , count: u32 , } fn main () { let c = Container { data: String ::from ("hello" ), count: 1 , }; let data = c.data; println! ("{}" , c.count); println! ("{}" , data); }
部分借用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 struct Data { field1: String , field2: String , } fn main () { let mut data = Data { field1: String ::from ("one" ), field2: String ::from ("two" ), }; let r1 = &data.field1; let r2 = &mut data.field2; r2.push_str (" modified" ); println! ("{}, {}" , r1, r2); }
内存布局 默认布局
1 2 3 4 5 6 7 8 9 10 struct Example { a: u8 , b: u32 , c: u8 , } fn main () { println! ("大小: {} bytes" , std::mem::size_of::<Example>()); println! ("对齐: {} bytes" , std::mem::align_of::<Example>()); }
1 2 3 4 5 6 7 Rust 编译器可能重排字段以优化内存: ┌────────────────────────────┬──────┬──────┬──────────────┐ │ b (u32) │ a │ c │ padding │ │ 4 bytes │ 1B │ 1B │ 2 bytes │ └────────────────────────────┴──────┴──────┴──────────────┘ 总大小: 8 bytes(优化后)
repr 属性控制布局
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 struct Example { a: u8 , b: u32 , c: u8 , } #[repr(C)] struct CLayout { a: u8 , b: u32 , c: u8 , } #[repr(packed)] struct Packed { a: u8 , b: u32 , c: u8 , } fn main () { println! ("默认: {} bytes" , std::mem::size_of::<Example>()); println! ("repr(C): {} bytes" , std::mem::size_of::<CLayout>()); println! ("packed: {} bytes" , std::mem::size_of::<Packed>()); }
打印结构体 1 2 3 4 5 6 7 8 9 10 11 12 13 #[derive(Debug)] struct Point { x: i32 , y: i32 , } fn main () { let p = Point { x: 10 , y: 20 }; println! ("{:?}" , p); println! ("{:#?}" , p); }
枚举 枚举是 Rust 中定义”多选一”类型的方式,一个值在某一时刻只能是其中一个变体。
基本枚举定义 简单枚举 (无关联数据)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 enum Direction { Up, Down, Left, Right, } enum Status { Pending, Running, Completed, Failed, } fn main () { let dir = Direction::Up; let status = Status::Running; match dir { Direction::Up => println! ("向上" ), Direction::Down => println! ("向下" ), Direction::Left => println! ("向左" ), Direction::Right => println! ("向右" ), } if let Status ::Running = status { println! ("正在运行中..." ); } }
带关联数据的枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 enum Message { Quit, Move { x: i32 , y: i32 }, Write (String ), ChangeColor (u8 , u8 , u8 ), } fn main () { let m1 = Message::Quit; let m2 = Message::Move { x: 10 , y: 20 }; let m3 = Message::Write (String ::from ("hello" )); let m4 = Message::ChangeColor (255 , 128 , 0 ); match m2 { Message::Quit => println! ("退出" ), Message::Move { x, y } => println! ("移动到 ({}, {})" , x, y), Message::Write (text) => println! ("消息: {}" , text), Message::ChangeColor (r, g, b) => println! ("颜色: RGB({},{},{})" , r, g, b), } }
三种变体形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 enum Example { Unit, Tuple (i32 , String ), Struct { id: u32 , name: String }, } fn main () { let a = Example::Unit; let b = Example::Tuple (42 , String ::from ("hello" )); let c = Example::Struct { id: 1 , name: String ::from ("test" ) }; match b { Example::Unit => println! ("单元变体" ), Example::Tuple (num, text) => println! ("元组: {}, {}" , num, text), Example::Struct { id, name } => println! ("结构体: {} - {}" , id, name), } }
1 2 3 4 5 6 7 ┌─────────────────┬────────────────────┬─────────────────────────────┐ │ 变体类型 │ 语法 │ 示例 │ ├─────────────────┼────────────────────┼─────────────────────────────┤ │ 单元变体 │ Name │ Quit, None, Empty │ │ 元组变体 │ Name(T1, T2) │ Some(42), Move(10, 20) │ │ 结构体变体 │ Name { f: T } │ Point { x: 1, y: 2 } │ └─────────────────┴────────────────────┴─────────────────────────────┘
枚举的判别值 默认判别值
1 2 3 4 5 6 7 8 9 10 11 12 enum Number { Zero, One, Two, } fn main () { println! ("Zero = {}" , Number::Zero as i32 ); println! ("One = {}" , Number::One as i32 ); println! ("Two = {}" , Number::Two as i32 ); }
自定义判别值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 enum HttpStatus { Ok = 200 , NotFound = 404 , InternalError = 500 , } enum Color { Red = 0xFF0000 , Green = 0x00FF00 , Blue = 0x0000FF , } #[repr(u8)] enum Flag { A = 1 , B = 2 , C = 4 , } fn main () { println! ("OK = {}" , HttpStatus::Ok as i32 ); println! ("NotFound = {}" , HttpStatus::NotFound as i32 ); println! ("Red = 0x{:06X}" , Color::Red as i32 ); println! ("Flag 大小: {} byte" , std::mem::size_of::<Flag>()); }
部分指定 (自动递增)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 enum Mixed { A, B = 10 , C, D, E = 100 , F, } fn main () { println! ("A={}, B={}, C={}, D={}, E={}, F={}" , Mixed::A as i32 , Mixed::B as i32 , Mixed::C as i32 , Mixed::D as i32 , Mixed::E as i32 , Mixed::F as i32 ); }
核心枚举 Option 和 Result 是 Rust 中最重要的两个枚举,用于处理空值 和错误 ,替代了其他语言中的 null 和异常机制。
Option<T> - 可选值 定义
1 2 3 4 5 enum Option <T> { Some (T), None , }
为什么需要 Option?
1 2 3 4 5 6 7 8 9 10 11 12 fn main () { let maybe_number : Option <i32 > = Some (42 ); let no_number : Option <i32 > = None ; }
创建 Option
1 2 3 4 5 6 7 8 9 10 11 12 13 14 fn main () { let a : Option <i32 > = Some (5 ); let b = Some ("hello" ); let c = Some (3.14 ); let d : Option <i32 > = None ; let e : Option <String > = None ; println! ("a = {:?}" , a); println! ("d = {:?}" , d); }
Option 不能直接使用内部值
1 2 3 4 5 6 7 8 9 10 11 fn main () { let x : Option <i32 > = Some (5 ); let y : i32 = 10 ; let sum = x.unwrap () + y; println! ("sum = {}" , sum); }
基本方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 fn main () { let some_val : Option <i32 > = Some (10 ); let none_val : Option <i32 > = None ; println! ("some_val.is_some() = {}" , some_val.is_some ()); println! ("some_val.is_none() = {}" , some_val.is_none ()); println! ("none_val.is_some() = {}" , none_val.is_some ()); println! ("none_val.is_none() = {}" , none_val.is_none ()); let v1 = some_val.unwrap (); println! ("unwrap = {}" , v1); let v2 = some_val.expect ("错误信息" ); println! ("expect = {}" , v2); println! ("some.unwrap_or(0) = {}" , some_val.unwrap_or (0 )); println! ("none.unwrap_or(0) = {}" , none_val.unwrap_or (0 )); let s : Option <String > = None ; println! ("default = '{}'" , s.unwrap_or_default ()); let n : Option <i32 > = None ; println! ("default = {}" , n.unwrap_or_default ()); }
常见使用场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 fn main () { let numbers = vec! [1 , 2 , 3 , 4 , 5 ]; let first : Option <&i32 > = numbers.first (); let tenth : Option <&i32 > = numbers.get (10 ); println! ("first = {:?}" , first); println! ("tenth = {:?}" , tenth); let good : Result <i32 , _> = "42" .parse (); let bad : Result <i32 , _> = "abc" .parse (); let good_opt : Option <i32 > = "42" .parse ().ok (); let bad_opt : Option <i32 > = "abc" .parse ().ok (); println! ("good_opt = {:?}" , good_opt); println! ("bad_opt = {:?}" , bad_opt); use std::collections::HashMap; let mut map = HashMap::new (); map.insert ("a" , 1 ); let val : Option <&i32 > = map.get ("a" ); let none : Option <&i32 > = map.get ("b" ); }
Result<T, E> - 错误处理 定义
1 2 3 4 5 enum Result <T, E> { Ok (T), Err (E), }
为什么需要 Result?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 fn divide (a: i32 , b: i32 ) -> Result <i32 , String > { if b == 0 { Err (String ::from ("除数不能为零" )) } else { Ok (a / b) } } fn main () { let result = divide (10 , 2 ); }
创建 Result
1 2 3 4 5 6 7 8 9 10 11 12 13 fn main () { let success : Result <i32 , String > = Ok (42 ); let ok_str : Result <&str , i32 > = Ok ("成功" ); let failure : Result <i32 , String > = Err (String ::from ("失败了" )); let err_num : Result <&str , i32 > = Err (404 ); println! ("success = {:?}" , success); println! ("failure = {:?}" , failure); }
基本方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 fn main () { let ok_val : Result <i32 , &str > = Ok (10 ); let err_val : Result <i32 , &str > = Err ("错误" ); println! ("ok_val.is_ok() = {}" , ok_val.is_ok ()); println! ("ok_val.is_err() = {}" , ok_val.is_err ()); println! ("err_val.is_ok() = {}" , err_val.is_ok ()); println! ("err_val.is_err() = {}" , err_val.is_err ()); let v1 = ok_val.unwrap (); println! ("unwrap = {}" , v1); let v2 = ok_val.expect ("失败了" ); println! ("expect = {}" , v2); let e = err_val.unwrap_err (); println! ("unwrap_err = {}" , e); println! ("ok.unwrap_or(0) = {}" , ok_val.unwrap_or (0 )); println! ("err.unwrap_or(0) = {}" , err_val.unwrap_or (0 )); let opt1 : Option <i32 > = ok_val.ok (); let opt2 : Option <i32 > = err_val.ok (); let opt3 : Option <&str > = err_val.err (); let opt4 : Option <&str > = ok_val.err (); println! ("ok_val.ok() = {:?}" , opt1); println! ("err_val.ok() = {:?}" , opt2); }
常见使用场景
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 use std::fs::File;use std::io::Read;fn main () { let file_result : Result <File, std::io::Error> = File::open ("hello.txt" ); let file = file_result.unwrap_or_else (|error| { panic! ("打开文件失败: {:?}" , error); }); let num : Result <i32 , _> = "42" .parse (); let bad : Result <i32 , _> = "abc" .parse (); println! ("num = {:?}" , num); println! ("bad = {:?}" , bad); fn validate_age (age: i32 ) -> Result <i32 , String > { if age < 0 { Err (String ::from ("年龄不能为负" )) } else if age > 150 { Err (String ::from ("年龄不合理" )) } else { Ok (age) } } println! ("{:?}" , validate_age (25 )); println! ("{:?}" , validate_age (-5 )); println! ("{:?}" , validate_age (200 )); }
相互转换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 fn main () { let opt : Option <i32 > = Some (5 ); let none : Option <i32 > = None ; let res1 : Result <i32 , &str > = opt.ok_or ("无值" ); let res2 : Result <i32 , &str > = none.ok_or ("无值" ); println! ("opt.ok_or() = {:?}" , res1); println! ("none.ok_or() = {:?}" , res2); let ok : Result <i32 , &str > = Ok (10 ); let err : Result <i32 , &str > = Err ("错误" ); let opt1 : Option <i32 > = ok.ok (); let opt2 : Option <i32 > = err.ok (); println! ("ok.ok() = {:?}" , opt1); println! ("err.ok() = {:?}" , opt2); let opt3 : Option <&str > = ok.err (); let opt4 : Option <&str > = err.err (); println! ("ok.err() = {:?}" , opt3); println! ("err.err() = {:?}" , opt4); }
内存布局 基本原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 enum Simple { A, B, C, } enum WithData { Empty, Number (i32 ), Text (String ), } fn main () { println! ("Simple 大小: {} bytes" , std::mem::size_of::<Simple>()); println! ("WithData 大小: {} bytes" , std::mem::size_of::<WithData>()); println! ("String 大小: {} bytes" , std::mem::size_of::<String >()); }
1 2 3 4 5 6 7 8 9 10 11 枚举内存布局 = 判别值(tag)+ 数据(payload) WithData 布局示意: ┌──────────┬─────────────────────────────────────────┐ │ tag │ payload │ │ (8B) │ (最大变体的大小) │ ├──────────┼─────────────────────────────────────────┤ │ 0 │ (Empty: 无数据,但空间仍保留) │ │ 1 │ Number: i32 (4B) + padding │ │ 2 │ Text: String (24B) │ └──────────┴─────────────────────────────────────────┘
数组 数组是固定长度、存储在栈上的同类型元素集合,长度是类型的一部分。
基本定义与创建 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 fn main () { let arr1 : [i32 ; 5 ] = [1 , 2 , 3 , 4 , 5 ]; let arr2 = [1 , 2 , 3 , 4 , 5 ]; let arr3 = [1.0 , 2.0 , 3.0 ]; let arr4 = ["hello" , "world" ]; let zeros = [0 ; 10 ]; let ones = [1u8 ; 100 ]; let spaces = [' ' ; 20 ]; let empty : [i32 ; 0 ] = []; println! ("arr1: {:?}" , arr1); println! ("zeros: {:?}" , zeros); println! ("empty 长度: {}" , empty.len ()); }
长度是类型的一部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 fn main () { let a : [i32 ; 3 ] = [1 , 2 , 3 ]; let b : [i32 ; 5 ] = [1 , 2 , 3 , 4 , 5 ]; println! ("[i32; 3] 大小: {} bytes" , std::mem::size_of::<[i32 ; 3 ]>()); println! ("[i32; 5] 大小: {} bytes" , std::mem::size_of::<[i32 ; 5 ]>()); fn print_three (arr: [i32 ; 3 ]) { println! ("{:?}" , arr); } print_three (a); }
1 2 3 4 5 6 7 8 9 ┌─────────────────────────────────────────────────────────┐ │ 数组类型 = 元素类型 + 长度 │ │ │ │ [i32; 3] → 3 个 i32,共 12 bytes │ │ [i32; 5] → 5 个 i32,共 20 bytes │ │ [u8; 100] → 100 个 u8,共 100 bytes │ │ │ │ 这三个是完全不同的类型! │ └─────────────────────────────────────────────────────────┘
元素访问 索引访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 fn main () { let arr = [10 , 20 , 30 , 40 , 50 ]; let first = arr[0 ]; let third = arr[2 ]; let last = arr[4 ]; println! ("第一个: {}, 第三个: {}, 最后一个: {}" , first, third, last); let mut arr2 = [1 , 2 , 3 ]; arr2[0 ] = 100 ; arr2[2 ] = 300 ; println! ("修改后: {:?}" , arr2); }
越界检查 (运行时 panic)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 fn main () { let arr = [1 , 2 , 3 , 4 , 5 ]; let index = 10 ; let x = arr[index]; match arr.get (2 ) { Some (value) => println! ("索引 2 的值: {}" , value), None => println! ("索引越界" ), } match arr.get (10 ) { Some (value) => println! ("值: {}" , value), None => println! ("索引 10 越界" ), } if let Some (v) = arr.get (3 ) { println! ("值: {}" , v); } }
遍历数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 fn main () { let arr = [10 , 20 , 30 , 40 , 50 ]; println! ("遍历引用:" ); for item in &arr { println! (" {}" , item); } println! ("遍历值:" ); for item in arr { println! (" {}" , item); } println! ("带索引:" ); for (index, value) in arr.iter ().enumerate () { println! (" arr[{}] = {}" , index, value); } println! ("索引遍历:" ); for i in 0 ..arr.len () { println! (" arr[{}] = {}" , i, arr[i]); } let mut arr2 = [1 , 2 , 3 ]; for item in &mut arr2 { *item *= 2 ; } println! ("翻倍后: {:?}" , arr2); }
常用方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 fn main () { let arr = [3 , 1 , 4 , 1 , 5 , 9 , 2 , 6 ]; println! ("长度: {}" , arr.len ()); println! ("是否为空: {}" , arr.is_empty ()); println! ("第一个: {:?}" , arr.first ()); println! ("最后一个: {:?}" , arr.last ()); println! ("get(2): {:?}" , arr.get (2 )); println! ("get(100): {:?}" , arr.get (100 )); let slice = &arr[2 ..5 ]; println! ("切片 [2..5]: {:?}" , slice); println! ("包含 5: {}" , arr.contains (&5 )); println! ("包含 7: {}" , arr.contains (&7 )); let sum : i32 = arr.iter ().sum (); println! ("总和: {}" , sum); let max = arr.iter ().max (); println! ("最大值: {:?}" , max); let pos = arr.iter ().position (|&x| x == 5 ); println! ("5 的位置: {:?}" , pos); }
排序与修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 fn main () { let mut arr = [3 , 1 , 4 , 1 , 5 , 9 , 2 , 6 ]; arr.sort (); println! ("升序: {:?}" , arr); arr.sort_by (|a, b| b.cmp (a)); println! ("降序: {:?}" , arr); arr.reverse (); println! ("反转: {:?}" , arr); let mut arr2 = [0 ; 5 ]; arr2.fill (42 ); println! ("填充: {:?}" , arr2); let mut arr3 = [1 , 2 , 3 , 4 , 5 ]; arr3.swap (0 , 4 ); println! ("交换后: {:?}" , arr3); let mut arr4 = [1 , 2 , 3 , 4 , 5 ]; arr4.rotate_left (2 ); println! ("左旋 2: {:?}" , arr4); }
数组与切片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 fn main () { let arr = [1 , 2 , 3 , 4 , 5 ]; let slice : &[i32 ] = &arr; let part : &[i32 ] = &arr[1 ..4 ]; println! ("部分切片: {:?}" , part); fn sum_slice (data: &[i32 ]) -> i32 { data.iter ().sum () } let arr3 = [1 , 2 , 3 ]; let arr5 = [1 , 2 , 3 , 4 , 5 ]; println! ("sum arr3: {}" , sum_slice (&arr3)); println! ("sum arr5: {}" , sum_slice (&arr5)); println! ("sum part: {}" , sum_slice (&arr5[2 ..])); }
1 2 3 4 5 6 7 8 9 ┌─────────────────────────────────────────────────────────┐ │ 数组 [T; N] vs 切片 &[T] │ ├─────────────────────────────────────────────────────────┤ │ 长度编译时固定 长度运行时确定 │ │ 存储在栈上 是引用(胖指针) │ │ 大小 = N * sizeof(T) 大小 = 2 * 指针 │ │ 类型包含长度 类型不包含长度 │ │ [i32; 3] ≠ [i32; 5] &[i32] 通用 │ └─────────────────────────────────────────────────────────┘
多维数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 fn main () { let matrix : [[i32 ; 4 ]; 3 ] = [ [1 , 2 , 3 , 4 ], [5 , 6 , 7 , 8 ], [9 , 10 , 11 , 12 ], ]; println! ("matrix[1][2] = {}" , matrix[1 ][2 ]); for row in &matrix { for col in row { print! ("{:3} " , col); } println! (); } let zeros : [[i32 ; 4 ]; 3 ] = [[0 ; 4 ]; 3 ]; println! ("零矩阵: {:?}" , zeros); let cube : [[[i32 ; 2 ]; 3 ]; 4 ] = [[[0 ; 2 ]; 3 ]; 4 ]; println! ("三维数组大小: {} bytes" , std::mem::size_of_val (&cube)); }
内存布局 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 fn main () { let arr = [1i32 , 2 , 3 , 4 , 5 ]; println! ("数组地址: {:p}" , &arr); println! ("arr[0] 地址: {:p}" , &arr[0 ]); println! ("arr[1] 地址: {:p}" , &arr[1 ]); println! ("arr[2] 地址: {:p}" , &arr[2 ]); println! ("i32 大小: {} bytes" , std::mem::size_of::<i32 >()); println! ("数组大小: {} bytes" , std::mem::size_of_val (&arr)); }
1 2 3 4 5 6 7 8 9 10 内存布局(栈上连续存储): arr: [1i32, 2, 3, 4, 5] 地址: 0x1000 0x1004 0x1008 0x100C 0x1010 ┌────────┬────────┬────────┬────────┬────────┐ │ 1 │ 2 │ 3 │ 4 │ 5 │ │ (i32) │ (i32) │ (i32) │ (i32) │ (i32) │ └────────┴────────┴────────┴────────┴────────┘ │◄──────────────── 20 bytes ───────────────►│
流程控制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ┌─────────────────────────────────────────────────────────────────────────┐ │ Rust 流程控制 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ 条件判断 循环 模式匹配 │ │ ├── if/else ├── loop ├── match │ │ ├── if let ├── while ├── if let / while let │ │ ├── let else ├── while let ├── @ 绑定 │ │ └── matches! └── for └── 模式语法 │ │ │ │ 跳转控制 错误处理 表达式 │ │ ├── break ├── ? 运算符 └── 块表达式 │ │ ├── continue ├── panic! │ │ ├── return └── unwrap/expect │ │ └── 'label 标签 │ │ │ └─────────────────────────────────────────────────────────────────────────┘
条件判断 if/else 表达式 Rust 中 if 是表达式而非语句,可以返回值。条件必须是 bool 类型,不会自动转换。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 let number = 42 ;if number > 0 { println! ("正数" ); } else if number < 0 { println! ("负数" ); } else { println! ("零" ); } let description = if number % 2 == 0 { "偶数" } else { "奇数" };let abs_value = if number >= 0 { number } else { -number };let category = if number > 100 { "大" } else { if number > 10 { "中" } else { "小" } };
if let 表达式 当只关心一种匹配情况时,if let 比 match 更简洁。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 let some_value : Option <i32 > = Some (42 );if let Some (x) = some_value { println! ("值为: {}" , x); } else { println! ("无值" ); } match some_value { Some (x) => println! ("值为: {}" , x), _ => println! ("无值" ), } let config : Result <i32 , &str > = Ok (100 );if let Ok (value) = config { println! ("配置值: {}" , value); } else if let Err (e) = config { println! ("配置错误: {}" , e); } struct Point { x: i32 , y: i32 }let point = Some (Point { x: 10 , y: 20 });if let Some (Point { x, y }) = point { println! ("坐标: ({}, {})" , x, y); }
let else 表达式 Rust 1.65 引入,用于匹配失败时必须发散(提前退出)的场景。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 fn get_username (id: Option <u32 >) -> String { let Some (user_id) = id else { return String ::from ("anonymous" ); }; format! ("user_{}" , user_id) } fn find_first_even (numbers: &[i32 ]) -> Option <i32 > { for &n in numbers { let 0 = n % 2 else { continue ; }; return Some (n); } None } fn process_data (data: Option <Result <String , Error>>) -> String { let Some (result) = data else { return String ::new (); }; let Ok (content) = result else { return String ::from ("error" ); }; content }
matches! 宏 返回 bool 的模式匹配,适合在条件判断中使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 let c = 'f' ;let is_lowercase = matches!(c, 'a' ..='z' );let is_letter = matches!(c, 'a' ..='z' | 'A' ..='Z' );let is_hex = matches!(c, '0' ..='9' | 'a' ..='f' | 'A' ..='F' );let opt = Some (42 );let is_positive = matches!(opt, Some (x) if x > 0 );let is_large = matches!(opt, Some (x) if x > 100 );enum Status { Active, Inactive, Pending (u32 ) }let status = Status::Pending (5 );let is_pending = matches!(status, Status::Pending (_));let is_urgent = matches!(status, Status::Pending (x) if x < 10 );let numbers = vec! [Some (1 ), None , Some (3 ), None , Some (5 )];let valid : Vec <_> = numbers.iter () .filter (|x| matches!(x, Some (_))) .collect ();
循环控制 loop 无限循环 loop 创建无限循环,必须通过 break 退出,可以返回值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 let mut count = 0 ;loop { count += 1 ; if count == 10 { break ; } } let mut counter = 0 ;let result = loop { counter += 1 ; if counter == 10 { break counter * 2 ; } }; let mut attempts = 0 ;let connection = loop { attempts += 1 ; match try_connect () { Ok (conn) => break conn, Err (e) if attempts < 3 => { println! ("重试 {}/3..." , attempts); continue ; } Err (e) => panic! ("连接失败: {}" , e), } }; enum State { Start, Running, Finished }let mut state = State::Start;loop { state = match state { State::Start => { println! ("启动" ); State::Running } State::Running => { println! ("运行中" ); State::Finished } State::Finished => { println! ("完成" ); break ; } }; }
while 条件循环 条件为真时持续执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 let mut n = 0 ;while n < 5 { println! ("{}" , n); n += 1 ; } let mut input = String ::new ();while input.trim () != "quit" { input.clear (); std::io::stdin ().read_line (&mut input).unwrap (); println! ("你输入了: {}" , input.trim ()); } let mut stack = vec! [1 , 2 , 3 , 4 , 5 ];while !stack.is_empty () { let top = stack.pop ().unwrap (); println! ("弹出: {}" , top); }
while let 模式匹配循环 结合模式匹配的条件循环,常用于迭代器和 Option/Result。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 let mut optional = Some (0 );while let Some (i) = optional { if i > 5 { optional = None ; } else { println! ("{}" , i); optional = Some (i + 1 ); } } let mut stack = vec! [1 , 2 , 3 ];while let Some (top) = stack.pop () { println! ("处理: {}" , top); } use std::sync::mpsc;let (tx, rx) = mpsc::channel ();while let Ok (msg) = rx.recv () { println! ("收到: {}" , msg); } let mut pairs = vec! [(1 , 'a' ), (2 , 'b' ), (3 , 'c' )];while let Some ((num, ch)) = pairs.pop () { println! ("{}: {}" , num, ch); }
for 迭代循环 Rust 最常用的循环,遍历任何实现了 IntoIterator 的类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 for i in 0 ..5 { println! ("{}" , i); } for i in 0 ..=5 { println! ("{}" , i); } for i in (0 ..5 ).rev () { println! ("{}" , i); } let v = vec! [1 , 2 , 3 ];for item in &v { println! ("{}" , item); } for item in &mut v { *item += 1 ; } for item in v { println! ("{}" , item); } let colors = vec! ["红" , "绿" , "蓝" ];for (index, color) in colors.iter ().enumerate () { println! ("{}: {}" , index, color); } use std::collections::HashMap;let mut scores = HashMap::new ();scores.insert ("Alice" , 100 ); scores.insert ("Bob" , 85 ); for (name, score) in &scores { println! ("{}: {}" , name, score); } for i in (0 ..10 ).step_by (2 ) { println! ("{}" , i); } let names = vec! ["Alice" , "Bob" ];let ages = vec! [25 , 30 ];for (name, age) in names.iter ().zip (ages.iter ()) { println! ("{} is {} years old" , name, age); }
跳转控制 break 退出循环 退出当前循环,可携带返回值,可指定标签跳出多层循环。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 for i in 0 ..10 { if i == 5 { break ; } println! ("{}" , i); } let result = loop { let value = compute (); if value > 100 { break value; } }; 'outer : for i in 0 ..5 { for j in 0 ..5 { if i * j > 10 { println! ("在 ({}, {}) 处退出" , i, j); break 'outer ; } } } let result = 'search : loop { for row in 0 ..10 { for col in 0 ..10 { if matrix[row][col] == target { break 'search (row, col); } } } break 'search (-1 , -1 ); };
continue 跳过迭代 跳过当前迭代的剩余部分,进入下一次迭代。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 for i in 0 ..10 { if i % 2 == 0 { continue ; } println! ("{}" , i); } 'outer : for i in 0 ..5 { for j in 0 ..5 { if j == 2 { continue 'outer ; } println! ("({}, {})" , i, j); } } let items = vec! [Some (1 ), None , Some (2 ), None , Some (3 )];for item in items { let Some (value) = item else { continue ; }; println! ("处理: {}" , value); }
return 函数返回 从函数中提前返回,可携带返回值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 fn divide (a: f64 , b: f64 ) -> Option <f64 > { if b == 0.0 { return None ; } Some (a / b) } fn find_in_matrix (matrix: &[Vec <i32 >], target: i32 ) -> Option <(usize , usize )> { for (i, row) in matrix.iter ().enumerate () { for (j, &value) in row.iter ().enumerate () { if value == target { return Some ((i, j)); } } } None } fn square (x: i32 ) -> i32 { x * x } fn process (numbers: Vec <i32 >) -> Vec <i32 > { numbers.into_iter () .filter_map (|n| { if n < 0 { return None ; } Some (n * 2 ) }) .collect () }
标签块表达式 从任意代码块中返回值,不仅限于循环。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 let result = 'block : { if condition_a { break 'block "A" ; } if condition_b { break 'block "B" ; } "default" }; fn categorize (value: i32 ) -> &'static str { 'check : { if value < 0 { break 'check "negative" ; } if value == 0 { break 'check "zero" ; } if value < 10 { break 'check "small" ; } if value < 100 { break 'check "medium" ; } "large" } } let validated = 'validation : { let Some (name) = get_name () else { break 'validation false }; let Some (age) = get_age () else { break 'validation false }; if age < 18 { break 'validation false }; true };
模式匹配 match 表达式 Rust 最强大的控制流结构,必须穷尽所有可能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 let number = 13 ;match number { 1 => println! ("一" ), 2 => println! ("二" ), 3 => println! ("三" ), _ => println! ("其他" ), } match number { 1 | 2 | 3 => println! ("小数字" ), 4 ..=10 => println! ("中等数字" ), _ => println! ("大数字" ), } match number { n if n < 0 => println! ("负数: {}" , n), n if n == 0 => println! ("零" ), n if n % 2 == 0 => println! ("正偶数: {}" , n), n => println! ("正奇数: {}" , n), } enum Message { Quit, Move { x: i32 , y: i32 }, Write (String ), ChangeColor (u8 , u8 , u8 ), } let msg = Message::ChangeColor (255 , 128 , 0 );match msg { Message::Quit => println! ("退出" ), Message::Move { x, y } => println! ("移动到 ({}, {})" , x, y), Message::Write (text) => println! ("写入: {}" , text), Message::ChangeColor (r, g, b) => println! ("颜色: RGB({}, {}, {})" , r, g, b), } let description = match number { 0 => "零" , 1 ..=9 => "个位数" , 10 ..=99 => "两位数" , _ => "更大的数" , };
@ 绑定 在匹配的同时将值绑定到变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 let num = 5 ;match num { n @ 1 ..=5 => println! ("1-5 范围内: {}" , n), n @ 6 ..=10 => println! ("6-10 范围内: {}" , n), n => println! ("范围外: {}" , n), } match num { n @ (1 | 3 | 5 | 7 | 9 ) => println! ("奇数: {}" , n), n @ (2 | 4 | 6 | 8 | 10 ) => println! ("偶数: {}" , n), n => println! ("其他: {}" , n), } enum Message { Hello { id: i32 }, } let msg = Message::Hello { id: 5 };match msg { Message::Hello { id: id_var @ 3 ..=7 } => { println! ("id 在 3-7 范围内: {}" , id_var); } Message::Hello { id: 10 ..=12 } => { println! ("id 在 10-12 范围内" ); } Message::Hello { id } => { println! ("其他 id: {}" , id); } } match Some (42 ) { Some (n @ 1 ..=100 ) if n % 2 == 0 => println! ("1-100 的偶数: {}" , n), Some (n) => println! ("其他: {}" , n), None => println! ("无值" ), }
模式语法详解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 let point = (3 , 5 );match point { (0 , 0 ) => println! ("原点" ), (x, 0 ) => println! ("x轴上, x = {}" , x), (0 , y) => println! ("y轴上, y = {}" , y), (x, y) => println! ("点 ({}, {})" , x, y), } struct Point { x: i32 , y: i32 }let p = Point { x: 0 , y: 7 };match p { Point { x: 0 , y } => println! ("y轴上, y = {}" , y), Point { x, y: 0 } => println! ("x轴上, x = {}" , x), Point { x, y } => println! ("点 ({}, {})" , x, y), } struct Point3D { x: i32 , y: i32 , z: i32 }let p = Point3D { x: 1 , y: 2 , z: 3 };let Point3D { x, .. } = p; let arr = [1 , 2 , 3 , 4 , 5 ];match &arr[..] { [] => println! ("空" ), [single] => println! ("单元素: {}" , single), [first, second] => println! ("两个元素: {}, {}" , first, second), [first, .., last] => println! ("首: {}, 尾: {}" , first, last), } let numbers = [1 , 2 , 3 , 4 , 5 ];match numbers { [first, rest @ ..] => { println! ("首: {}, 剩余: {:?}" , first, rest); } } let mut pair = (String ::from ("hello" ), 42 );match pair { (ref s, ref mut n) => { println! ("字符串: {}" , s); *n += 1 ; } } enum Color { Rgb (u8 , u8 , u8 ), Hsv (u8 , u8 , u8 ), } enum Message { ChangeColor (Color), Quit, } let msg = Message::ChangeColor (Color::Rgb (255 , 128 , 0 ));match msg { Message::ChangeColor (Color::Rgb (r, g, b)) => { println! ("RGB: ({}, {}, {})" , r, g, b); } Message::ChangeColor (Color::Hsv (h, s, v)) => { println! ("HSV: ({}, {}, {})" , h, s, v); } Message::Quit => println! ("退出" ), }
错误处理 ? 运算符 简化错误传播,遇到 Err 或 None 时提前返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 use std::fs::File;use std::io::{self , Read};fn read_file (path: &str ) -> Result <String , io::Error> { let mut file = File::open (path)?; let mut content = String ::new (); file.read_to_string (&mut content)?; Ok (content) } fn read_file_compact (path: &str ) -> Result <String , io::Error> { let mut content = String ::new (); File::open (path)?.read_to_string (&mut content)?; Ok (content) } fn first_char (s: &str ) -> Option <char > { s.lines ().next ()?.chars ().next () } fn main () -> Result <(), Box <dyn std::error::Error>> { let content = read_file ("config.txt" )?; println! ("{}" , content); Ok (()) } fn process () -> Result <(), MyError> { let content = std::fs::read_to_string ("file.txt" )?; let data : Data = serde_json::from_str (&content)?; Ok (()) }
panic! 宏 不可恢复错误,终止程序(或在 panic=abort 模式下直接中止)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 fn divide (a: i32 , b: i32 ) -> i32 { if b == 0 { panic! ("除数不能为零!" ); } a / b } fn get_item (index: usize , items: &[&str ]) { if index >= items.len () { panic! ("索引 {} 越界,数组长度为 {}" , index, items.len ()); } } fn process_type (t: &str ) -> i32 { match t { "a" => 1 , "b" => 2 , "c" => 3 , _ => unreachable! ("未知类型: {}" , t), } } fn complex_algorithm () { todo!("待实现复杂算法" ); } fn placeholder () { unimplemented! ("此功能尚未实现" ); }
unwrap 和 expect 快速提取值的方法,失败时 panic。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 let value : Option <i32 > = Some (42 );let v = value.unwrap (); let none : Option <i32 > = None ;let config = std::fs::read_to_string ("config.txt" ) .expect ("无法读取配置文件" ); let num : i32 = "42" .parse ().expect ("硬编码的字符串应该能解析" );let value = some_option.unwrap_or (0 ); let value = some_option.unwrap_or_else (|| compute_default ()); let value = some_option.unwrap_or_default (); let ok_value = result.unwrap_or (default);let ok_value = result.unwrap_or_else (|e| handle_error (e));let err_value = result.unwrap_err ();
块表达式 Rust 中几乎所有代码块都是表达式,最后一行无分号即为返回值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 let result = { let a = 1 ; let b = 2 ; a + b }; let unit = { let a = 1 ; let b = 2 ; a + b; }; let message = { let mut temp = String ::new (); temp.push_str ("Hello, " ); temp.push_str ("World!" ); temp }; let connection = { let config = load_config (); let pool = create_pool (&config); let conn = pool.get_connection (); conn.configure (&config); conn }; let description = match status_code { 200 => "成功" , 404 => { log_not_found (); "未找到" } 500 ..=599 => { let msg = format! ("服务器错误: {}" , status_code); log_error (&msg); "服务器错误" } _ => "未知状态" , }; let ptr_value = unsafe { let ptr = some_raw_ptr; *ptr };
方法 Method 核心概念 方法 是定义在结构体、枚举或 trait 对象上下文中的函数,第一个参数始终是 self,代表调用该方法的实例。
1 2 3 4 5 6 ┌─────────────────────────────────────────────────────┐ │ 方法 vs 函数 │ ├─────────────────────────────────────────────────────┤ │ 函数 (fn) │ 独立存在,不依附于任何类型 │ │ 方法 (method) │ 定义在 impl 块中,绑定到特定类型 │ └─────────────────────────────────────────────────────┘
基本语法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 struct Rectangle { width: u32 , height: u32 , } impl Rectangle { fn area (&self ) -> u32 { self .width * self .height } fn new (width: u32 , height: u32 ) -> Self { Rectangle { width, height } } } fn main () { let rect = Rectangle::new (30 , 50 ); println! ("面积: {}" , rect.area ()); }
self 的三种形式
形式
含义
所有权
使用场景
&self
不可变借用
借用
只读访问(最常用)
&mut self
可变借用
借用
需要修改实例
self
获取所有权
转移
转换类型或消费实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 impl Rectangle { fn area (&self ) -> u32 { self .width * self .height } fn scale (&mut self , factor: u32 ) { self .width *= factor; self .height *= factor; } fn to_square (self ) -> Rectangle { let side = self .width.max (self .height); Rectangle { width: side, height: side } } }
自动引用和解引用 Rust 会自动添加 &、&mut 或 *,使方法调用更简洁:
1 2 3 4 5 let rect = Rectangle::new (10 , 20 );rect.area (); (&rect).area ();
多个 impl 块 一个类型可以有多个 impl 块(常用于 trait 实现分离):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 impl Rectangle { fn area (&self ) -> u32 { } } impl Rectangle { fn perimeter (&self ) -> u32 { } } impl std ::fmt::Display for Rectangle { fn fmt (&self , f: &mut std::fmt::Formatter) -> std::fmt::Result { write! (f, "{}x{}" , self .width, self .height) } }
枚举上的方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 enum Message { Quit, Move { x: i32 , y: i32 }, Write (String ), } impl Message { fn call (&self ) { match self { Message::Quit => println! ("退出" ), Message::Move { x, y } => println! ("移动到 ({}, {})" , x, y), Message::Write (s) => println! ("消息: {}" , s), } } }
方法链式调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 impl Rectangle { fn set_width (mut self , width: u32 ) -> Self { self .width = width; self } fn set_height (mut self , height: u32 ) -> Self { self .height = height; self } } let rect = Rectangle::new (0 , 0 ) .set_width (100 ) .set_height (50 );
泛型 Generics 核心概念 泛型 是一种参数化类型的机制,让代码可以处理多种数据类型,而无需重复编写。
1 2 3 4 5 6 7 8 ┌─────────────────────────────────────────────────────────────┐ │ 泛型的价值 │ ├─────────────────────────────────────────────────────────────┤ │ ✗ 没有泛型:fn max_i32(a: i32, b: i32) -> i32 │ │ fn max_f64(a: f64, b: f64) -> f64 重复代码! │ ├─────────────────────────────────────────────────────────────┤ │ ✓ 使用泛型:fn max<T>(a: T, b: T) -> T 一份代码! │ └─────────────────────────────────────────────────────────────┘
函数泛型 1 2 3 4 5 6 7 8 9 10 fn largest <T: PartialOrd >(a: T, b: T) -> T { if a > b { a } else { b } } fn main () { println! ("{}" , largest (5 , 10 )); println! ("{}" , largest (3.14 , 2.71 )); println! ("{}" , largest ('a' , 'z' )); }
结构体泛型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 struct Point <T> { x: T, y: T, } struct Pair <T, U> { first: T, second: U, } fn main () { let int_point = Point { x: 5 , y: 10 }; let float_point = Point { x: 1.0 , y: 4.0 }; let mixed = Pair { first: 5 , second: "hello" }; }
枚举泛型 标准库中最经典的两个泛型枚举:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 enum Option <T> { Some (T), None , } enum Result <T, E> { Ok (T), Err (E), } fn main () { let number : Option <i32 > = Some (42 ); let text : Option <&str > = None ; let success : Result <i32 , String > = Ok (200 ); let failure : Result <i32 , String > = Err ("错误" .to_string ()); }
方法泛型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 struct Point <T> { x: T, y: T, } impl <T> Point<T> { fn new (x: T, y: T) -> Self { Point { x, y } } fn x (&self ) -> &T { &self .x } } impl Point <f64 > { fn distance_from_origin (&self ) -> f64 { (self .x.powi (2 ) + self .y.powi (2 )).sqrt () } } impl <T> Point<T> { fn mixup <U>(self , other: Point<U>) -> Point<T, U> { Point { x: self .x, y: other.y, } } }
Trait Bounds(特征约束) 基本语法 1 2 3 4 5 6 7 8 9 10 11 12 13 fn print_info <T: std::fmt::Display>(item: T) { println! ("{}" , item); } fn complex_function <T, U>(t: T, u: U) -> i32 where T: Display + Clone , U: Debug + PartialOrd , { }
常见约束模式 1 2 3 4 5 6 7 8 9 ┌──────────────────┬────────────────────────────────────────┐ │ 约束形式 │ 含义 │ ├──────────────────┼────────────────────────────────────────┤ │ T: Clone │ T 必须实现 Clone │ │ T: Clone + Debug │ T 必须同时实现 Clone 和 Debug │ │ T: 'static │ T 不包含非静态引用 │ │ T: ?Sized │ T 可以是动态大小类型 │ │ T: Default │ T 可以有默认值 │ └──────────────────┴────────────────────────────────────────┘
实际示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 use std::fmt::{Display, Debug };fn largest <T: PartialOrd + Copy >(list: &[T]) -> T { let mut largest = list[0 ]; for &item in list { if item > largest { largest = item; } } largest } fn notify <T>(item: &T) where T: Display + Clone { println! ("通知: {}" , item); }
impl Trait 语法糖 1 2 3 4 5 6 7 8 9 fn print (item: impl Display ) { println! ("{}" , item); } fn counter () -> impl Iterator <Item = i32 > { (0 ..10 ).filter (|x| x % 2 == 0 ) }
const 泛型(常量泛型) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct Array <T, const N: usize > { data: [T; N], } impl <T, const N: usize > Array<T, N> { fn len (&self ) -> usize { N } } fn main () { let arr : Array<i32 , 5 > = Array { data: [1 , 2 , 3 , 4 , 5 ] }; println! ("长度: {}" , arr.len ()); }
默认类型参数 1 2 3 4 5 6 7 8 9 10 11 12 13 trait Add <Rhs = Self > { type Output ; fn add (self , rhs: Rhs) -> Self ::Output; } impl Add for Point { type Output = Point; fn add (self , other: Point) -> Point { } }
单态化(零成本抽象) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ┌─────────────────────────────────────────────────────────────┐ │ 编译时单态化 │ ├─────────────────────────────────────────────────────────────┤ │ 源代码: │ │ fn id<T>(x: T) -> T { x } │ │ id(5); // 调用1 │ │ id(3.14); // 调用2 │ ├─────────────────────────────────────────────────────────────┤ │ 编译后生成: │ │ fn id_i32(x: i32) -> i32 { x } │ │ fn id_f64(x: f64) -> f64 { x } │ ├─────────────────────────────────────────────────────────────┤ │ ✓ 运行时零开销 │ │ ✗ 可能增加二进制体积 │ └─────────────────────────────────────────────────────────────┘
常见使用模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 impl <T> Vec <T> { pub fn new () -> Vec <T> { } } fn convert <T: Into <String >>(value: T) -> String { value.into () } impl <T: Display> ToString for T { } trait Iterator { type Item ; fn next (&mut self ) -> Option <Self ::Item>; } trait Container <T> { fn contains (&self , item: &T) -> bool ; }
特征 Trait 核心概念 Trait 定义了类型应该具备的行为 (方法集合),类似于其他语言的接口(Interface),但更强大。
1 2 3 4 5 6 7 8 ┌─────────────────────────────────────────────────────────────┐ │ Trait 的本质 │ ├─────────────────────────────────────────────────────────────┤ │ • 定义共享行为的抽象 │ │ • 实现多态(编译时/运行时) │ │ • 约束泛型参数 │ │ • 扩展现有类型的能力 │ └─────────────────────────────────────────────────────────────┘
基本语法 定义 Trait 1 2 3 4 trait Summary { fn summarize (&self ) -> String ; }
实现 Trait 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 struct Article { title: String , author: String , content: String , } struct Tweet { username: String , content: String , } impl Summary for Article { fn summarize (&self ) -> String { format! ("{} by {}" , self .title, self .author) } } impl Summary for Tweet { fn summarize (&self ) -> String { format! ("@{}: {}" , self .username, self .content) } } fn main () { let article = Article { title: String ::from ("Rust入门" ), author: String ::from ("张三" ), content: String ::from ("..." ), }; println! ("{}" , article.summarize ()); }
默认实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 trait Summary { fn summarize (&self ) -> String { String ::from ("(阅读更多...)" ) } fn summarize_author (&self ) -> String ; fn full_summary (&self ) -> String { format! ("作者: {} - {}" , self .summarize_author (), self .summarize ()) } } impl Summary for Article { fn summarize_author (&self ) -> String { self .author.clone () } } impl Summary for Tweet { fn summarize (&self ) -> String { format! ("@{}: {}" , self .username, self .content) } fn summarize_author (&self ) -> String { format! ("@{}" , self .username) } }
Trait 作为参数 三种等价写法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 fn notify (item: &impl Summary ) { println! ("速报: {}" , item.summarize ()); } fn notify <T: Summary>(item: &T) { println! ("速报: {}" , item.summarize ()); } fn notify <T>(item: &T) where T: Summary { println! ("速报: {}" , item.summarize ()); }
多重约束 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 use std::fmt::{Display, Debug };fn print_info (item: &(impl Summary + Display)) { println! ("{}" , item.summarize ()); println! ("{}" , item); } fn print_info <T: Summary + Display>(item: &T) { }fn complex <T, U>(t: &T, u: &U) -> String where T: Summary + Clone , U: Display + Debug , { format! ("{} - {:?}" , t.summarize (), u) }
Trait 作为返回值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 fn create_summary () -> impl Summary { Tweet { username: String ::from ("test" ), content: String ::from ("hello" ), } } fn create_summary (switch: bool ) -> impl Summary { if switch { Article { } } else { Tweet { } } }
常用标准库 Trait 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ┌─────────────────┬────────────────────────────────────────────┐ │ Trait │ 用途 │ ├─────────────────┼────────────────────────────────────────────┤ │ Clone │ 显式深拷贝 (.clone()) │ │ Copy │ 隐式按位复制(栈上简单类型) │ │ Debug │ 调试格式化 ({:?}) │ │ Display │ 用户友好格式化 ({}) │ │ Default │ 默认值 │ │ PartialEq / Eq │ 相等比较 (== !=) │ │ PartialOrd / Ord│ 排序比较 (< > <= >=) │ │ Hash │ 哈希计算 │ │ Drop │ 析构函数 │ │ From / Into │ 类型转换 │ │ AsRef / AsMut │ 引用转换 │ │ Deref / DerefMut│ 解引用运算符重载 │ │ Iterator │ 迭代器 │ │ Fn/FnMut/FnOnce │ 闭包 │ │ Send / Sync │ 线程安全标记 │ └─────────────────┴────────────────────────────────────────────┘
派生宏 (derive) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #[derive(Debug, Clone, PartialEq, Eq, Hash, Default)] struct Point { x: i32 , y: i32 , } fn main () { let p1 = Point { x: 1 , y: 2 }; let p2 = p1.clone (); println! ("{:?}" , p1); println! ("{}" , p1 == p2); let p3 = Point::default (); }
可派生的 Trait 1 2 3 4 5 6 7 8 9 10 11 12 #[derive( Debug, // 调试输出 Clone, // 克隆 Copy, // 复制(需要 Clone) PartialEq, // 部分相等 Eq, // 完全相等(需要 PartialEq) PartialOrd, // 部分排序 Ord, // 完全排序(需要 PartialOrd + Eq) Hash, // 哈希(需要 Eq) Default, // 默认值 )] struct Data { }
关联类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 trait Iterator { type Item ; fn next (&mut self ) -> Option <Self ::Item>; } struct Counter { count: u32 , } impl Iterator for Counter { type Item = u32 ; fn next (&mut self ) -> Option <Self ::Item> { self .count += 1 ; if self .count <= 5 { Some (self .count) } else { None } } }
关联类型 vs 泛型参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 trait Iterator { type Item ; fn next (&mut self ) -> Option <Self ::Item>; } trait From <T> { fn from (value: T) -> Self ; } impl From <&str > for String { }impl From <char > for String { }impl From <Vec <u8 >> for String { }
孤儿规则 (Orphan Rule) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 impl Display for MyStruct { }impl MyTrait for Vec <i32 > { }impl Display for Vec <i32 > { } struct Wrapper (Vec <String >);impl Display for Wrapper { fn fmt (&self , f: &mut std::fmt::Formatter) -> std::fmt::Result { write! (f, "[{}]" , self .0 .join (", " )) } }
Supertraits(特征继承) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 use std::fmt::Display;trait OutlinePrint : Display { fn outline_print (&self ) { let output = self .to_string (); let len = output.len (); println! ("{}" , "*" .repeat (len + 4 )); println! ("* {} *" , output); println! ("{}" , "*" .repeat (len + 4 )); } } #[derive(Debug)] struct Point { x: i32 , y: i32 }impl Display for Point { fn fmt (&self , f: &mut std::fmt::Formatter) -> std::fmt::Result { write! (f, "({}, {})" , self .x, self .y) } } impl OutlinePrint for Point {}
完全限定语法 完全限定语法 用于明确指定调用哪个类型或 trait 的方法/函数,解决名称冲突问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 trait Pilot { fn fly (&self ); } trait Wizard { fn fly (&self ); } struct Human ;impl Pilot for Human { fn fly (&self ) { println! ("飞机起飞" ); } } impl Wizard for Human { fn fly (&self ) { println! ("魔法飞行" ); } } impl Human { fn fly (&self ) { println! ("挥动双臂" ); } } fn main () { let person = Human; person.fly (); Pilot::fly (&person); Wizard::fly (&person); <Human as Pilot>::fly (&person); }
关联常量 1 2 3 4 5 6 7 8 9 10 11 12 13 trait Greet { const GREETING: &'static str = "Hello" ; fn greet (&self ) { println! ("{}" , Self ::GREETING); } } struct Japanese ;impl Greet for Japanese { const GREETING: &'static str = "こんにちは" ; }
条件实现 (Blanket Implementation) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 use std::fmt::Display;impl <T: Display> ToString for T { fn to_string (&self ) -> String { format! ("{}" , self ) } } struct Pair <T> { x: T, y: T, } impl <T> Pair<T> { fn new (x: T, y: T) -> Self { Pair { x, y } } } impl <T: Display + PartialOrd > Pair<T> { fn cmp_display (&self ) { if self .x >= self .y { println! ("最大: {}" , self .x); } else { println! ("最大: {}" , self .y); } } }
集合类型 1 2 3 4 5 6 7 8 9 10 11 12 13 ┌─────────────────────────────────────────────────────────────────────────┐ │ Rust 集合类型 │ ├─────────────────────────────────────────────────────────────────────────┤ │ │ │ 序列(Sequences) 映射(Maps) 集合(Sets) │ │ ├── Vec<T> ├── HashMap<K,V> ├── HashSet<T> │ │ ├── VecDeque<T> └── BTreeMap<K,V> └── BTreeSet<T> │ │ └── LinkedList<T> │ │ │ │ 其他 │ │ └── BinaryHeap<T> │ │ │ └─────────────────────────────────────────────────────────────────────────┘
序列 vec<T> VecDeque<T> LinkedList<T> 映射 HashMap<K,V> BTreeMap<K,V> 集合 HashSet<T> BTreeSet<T> 其他 BinaryHeap<T> 生命周期 包和模块 格式化输出 注释与文档 函数式编程 闭包 迭代器 深入类型 智能指针 循环引用 自引用 多线程 多进程 Unsafe 编程 宏编程 异步编程 no_std 编程 自动化测试 Cargo 使用
好了,你现在已经了解 Rust 的基本使用了,请用它来写一个操作系统吧!
版本更新变化