本文最后更新于 2025-04-24T15:11:31+08:00
基本输入输出
输入并输出
1 2 3 4 5 6 7 use std::io;fn main () { let mut input = String ::new (); io::stdin ().read_line (&mut input).unwrap (); println! ("input is: {}" , input); }
stdin()
函数获取标准输入的句柄
read_line(buf: &mut String) -> Result<usize>
方法读取一行输入,并将其存储在传入的 String
变量中。成功时,返回读取的字节总数
println!
宏用于打印输出,类似于 C 语言中的 printf
函数。它会自动添加换行符
unwrap() -> T
方法解包 Result
类型,返回 Ok
中的值或引发 panic
在使用 use
语句时一般引入到所用函数或模块的上一级,可以避免命名冲突
读取多行输入
1 2 3 4 5 6 7 8 use std::io;fn main () { let lines = io::stdin ().lines (); for line in lines { println! ("got a line: {}" , line.unwrap ()); } }
lines()
方法返回一个迭代器,迭代器产生的元素末尾不含换行符
格式化输出
format!
宏用于格式化字符串,返回一个 String
print!
宏格式化文本并打印到标准输出
println!
宏格式化文本并打印到标准输出,末尾自动添加换行符
eprint!
宏格式化文本并打印到标准错误输出
eprintln!
宏格式化文本并打印到标准错误输出,末尾自动添加换行符
以上宏的格式化语法全部相同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 println! ("Hello, {}!" , "world" );println! ("{1} {0}" , "world" , "hello" );println! ("{0} {} {1} {}" , "hello" , "world" ); println! ("{greeting} {name}" , greeting="hello" , name="world" );let greeting = "hello" ;let name = "world" ;println! ("{greeting} {name}" );
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 println! ("Hello, {:5}!" , "rust" ); println! ("Hello, {1:0$}!" , 5 , "rust" );println! ("Hello, {:width$}!" , "rust" , width=5 );println! ("Hello, {:<6}!" , "rust" ); println! ("Hello, {:^6}!" , "rust" ); println! ("Hello, {:>6}!" , "rust" ); println! ("{:-^12}" , "数字" );println! ("Hello, {:+}!" , 42 ); println! ("Hello, {:#x}!" , 42 ); println! ("Hello, {:#X}!" , 42 ); println! ("Hello, {:#o}!" , 42 ); println! ("Hello, {:#b}!" , 42 ); println! ("Hello, {:#e}!" , 42.0 ); println! ("Hello, {:#E}!" , 42.0 ); println! ("Hello, {:05}!" , 42 ); println! ("Hello, {:.2}!" , 42.12345 ); println! ("Hello, {:.1$}!" , 42.12345 , 2 ); println! ("{{ Hello }}" );#[derive(Debug)] struct Point { x: i32 , y: i32 , }let p = Point { x: 1 , y: 2 };println! ("{:?}" , p); println! ("{:#?}" , p);
Display
trait
要在 println!
中以 {}
的形式输出自定义类型,需要实现 Display
trait。在 fmt
方法中用 write!
宏格式化输出。
1 2 3 4 5 impl fmt ::Display for Point { fn fmt (&self , f: &mut fmt::Formatter) -> fmt::Result { write! (f, "({}, {})" , self .x, self .y) } }
基本类型
有符号整数:i8
、i16
、i32
、i64
、i128
和 isize
(指针宽度)
无符号整数: u8
、u16
、u32
、u64
、u128
和 usize
(指针宽度)
浮点数: f32
、f64
char:单个 Unicode 字符,如 'a','α' 和 ' 锈'
bool:true
或 false
单元类型:()
,空元组
数组:[T; N]
,长度为 N 的数组,元素类型为 T
元组:(T1, T2, ...)
,可以包含不同类型的元素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 let x : i32 = 5 ; let pi : f64 = 3.14 ;let c : char = 'a' ;let w :i32 = pi as i32 ; let y = 5 ; let z = 5u32 ; let f = 0.1 ; let b = true ;let arr : [i32 ; 5 ] = [1 , 2 , 3 , 4 , 5 ]; let zero_arr = [0 ; 5 ]; println! ("First element: {}" , arr[0 ]); println! ("Array length: {}" , arr.len ()); println! ("Slice: {:?}" , &arr[1 ..3 ]); let tup : (i32 , f64 , char ) = (1 , 2.0 , 'a' ); println! ("First element: {}" , tup.0 );
变量绑定
let
用于将值绑定到变量。
绑定默认不可变
使用 mut
关键字使变量可变
1 2 3 4 5 6 7 8 let x = 5 ; let mut y = 10 ; y = 20 ; println! ("x: {}, y: {}" , x, y); let z ; z = 30 ; println! ("z: {}" , z);
变量绑定的作用域限定在一个代码块中
变量可以在同一作用域中被重新绑定,称为遮蔽(shadowing)
1 2 3 4 5 6 7 8 9 10 11 12 fn main () { let mut x = 5 ; { let x = "hello" ; println! ("Inner x: {}" , x); let y = 20 ; } x = 10 ; println! ("Outer x: {}" , x); }
自定义类型
结构体
结构体有三种类型: * 元组结构体:没有字段名,只有字段类型 * 命名结构体:有字段名和字段类型 * 单元结构体:没有字段,只有名称
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 struct Unit ; struct Point (i32 , i32 ); #[derive(Debug)] struct Person { name: String , age: u8 , } let p = Point (1 , 2 ); let (x, y) = (3 , 4 );let p = Point (x, y); let p = Point { 0 : 5 , 1 : 6 }; println! ("Point: ({}, {})" , p.0 , p.1 ); let alice = Person { name: String ::from ("Alice" ), age: 22 }; let name = String ::from ("Bob" );let age = 25 ;let bob = Person { name, age }; let charlie = Person { name: String ::from ("Charlie" ), ..person };
枚举
枚举用于定义一个类型,它可以是多个不同的值之一。Rust 中的枚举有点类似于 C 语言中的联合体,但更强大。
1 2 3 4 5 6 7 8 9 10 11 12 13 enum Direction { Up, Down, Left, Right, } enum Action { Move { x: i32 , y: i32 }, Speak (String ), Stop, }
关联函数和方法
结构体和枚举都可以使用 impl
块来实现关联函数或方法
方法是依赖于实例的函数,必须有一个 self
参数。self
是结构体或枚举的实例
关联函数和方法类似于面向对象语言中的静态方法和实例方法
1 2 3 4 5 6 7 8 9 10 11 12 13 impl Person { fn new (name: String , age: u8 ) -> Person { Person { name, age } } fn greet (&self ) { println! ("Hello, my name is {} and I'm {} years old." , self .name, self .age); } fn rename (&mut self , name: String ) { self .name = name; } }
类型别名
类型别名用于给现有类型起一个新的名字。它不会创建新的类型,只是给现有类型起一个别名。类似于 C 语言中的 typedef
。
1 2 3 4 5 type Int = i32 ; type Point = (i32 , i32 ); let x : Int = 5 ; let p : Point = (1 , 2 );
控制流
判断条件不加括号
if
语句
1 2 3 4 5 6 7 8 9 if x > 5 { println! ("x is greater than 5" ); } else if x < 5 { println! ("x is less than 5" ); } else { println! ("x is equal to 5" ); }let positive = if x > 0 { true } else { false };
loop
循环
loop
是无限循环
1 2 3 4 5 6 7 8 9 let cnt = 0 ;loop { println! ("cnt: {}" , cnt); if cnt == 10 { break ; } cnt += 1 ; }
可以从 loop
循环中返回值
1 2 3 4 5 6 7 8 let mut x = 1 ;let result = loop { x = x * 2 if x > 1000 { break x; } };println! ("Result: {}" , result);
while
循环
1 2 3 4 5 let mut x = 1 ;while x < 5 { println! ("x: {}" , x); x += 1 ; }
for
循环
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 for i in 1 ..5 { println! ("i: {}" , i); }for i in (1 ..5 ).rev () { println! ("i: {}" , i); }let arr = [1 , 2 , 3 , 4 , 5 ];for i in arr { println! ("i: {}" , i); }let mut names = vec! ["Alice" , "Bob" , "Charlie" ];for name in names.iter () { println! ("Name: {}" , name); }for name in names.iter_mut () { *name = "Rust" ; }for name in names.into_iter () { println! ("Name: {}" , name); }
循环标签
Rust 允许在循环前加标签,以便在嵌套循环中使用 break
或 continue
跳出指定的循环
1 2 3 4 5 6 7 8 'outer : for i in 0 ..5 { for j in 0 ..3 { if i == 2 && j == 2 { break 'outer ; } println! ("i: {}, j: {}" , i, j); } }
模式匹配
可以在 match
、if let
、while let
、for
、let
和函数参数中使用模式匹配。
match
语句
match
语句用于模式匹配,可以匹配枚举、整数、字符、字符串等类型。类似于 switch
语句,但更强大
1 2 3 4 5 6 7 8 let x = 5 ;match x { 1 => println! ("One" ), 2 | 3 => println! ("Two or Three" ), 4 ..=10 => println! ("Four to Ten" ), _ => println! ("Other" ), }
1 2 3 4 5 6 7 let x = Some (5 );match x { Some (1 ) => println! ("One" ), Some (n) => println! ("Some {}" , n), _ => println! ("None" ), }
if let
和 while let
if let
在表达式满足模式时执行代码块。它是 match
的简化版本,适用于只关心一个模式的情况。
1 2 3 4 5 6 let x = Some (5 );if let Some (n) = x { println! ("Some {}" , n); } else { println! ("None" ); }
while let
在表达式满足模式时执行循环。
1 2 3 4 5 6 7 8 9 10 let mut x = Some (1 );while let Some (n) = x { if n > 9 { println! ("x is {}, enough" , n); x = None ; } else { println! ("x is {}, not enough" , n); x = Some (n + 1 ); } }
守卫语句和 @
绑定
守卫语句可以在模式匹配时添加额外的 if
条件。
1 2 3 4 5 6 7 8 let x = 5 ;let y = 10 ;match x { 1 => println! ("One" ), n if n < y => println! ("{} is less than {}" , n, y), n if n > y => println! ("{} is greater than {}" , n, y), _ => println! ("Other" ), }
@
绑定可以在测试一个值是否匹配模式的同时将该值绑定到变量上
语法为 variable @ pattern
1 2 3 4 5 6 let x = 5 ;match x { n @ 1 ..=10 => println! ("value in range 1 to 10: {}" , n), _ => println! ("other range" ), }
可以绑定整个模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 struct Point { x: i32 , y: i32 , }let point = Point { x: 10 , y: 20 };match point { p @ Point { x: 0 ..=10 , y } => { println! ("点在 x 轴上 0 到 10 的范围内: {:?}, y = {}" , p, y) }, p @ Point { x, y: 0 ..=10 } => { println! ("点在 y 轴上 0 到 10 的范围内: {:?}, x = {}" , p, x) }, Point { x, y } => println! ("点在其他位置: ({}, {})" , x, y), }
解构
解构元组
1 2 3 4 5 6 7 8 9 10 let point = (1 , 2 );let (x, y) = point; println! ("x: {}, y: {}" , x, y); match point { (0 , 0 ) => println! ("Origin" ), (x, 0 ) => println! ("On the x-axis at {}" , x), (0 , y) => println! ("On the y-axis at {}" , y), (x, y) => println! ("Point at ({}, {})" , x, y), }
解构结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct Point { x: i32 , y: i32 , }let point = Point { x: 1 , y: 2 };let Point { x: i, y: j } = point; let Point { x, y } = point; match point { Point { x: 0 , y: 0 } => println! ("Origin" ), Point { x, y: 0 } => println! ("On the x-axis at {}" , x), Point { x: 0 , y } => println! ("On the y-axis at {}" , y), Point { x, y } => println! ("Point at ({}, {})" , x, y), }
解构枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 enum Action { Move { x: i32 , y: i32 }, Speak (String ), Stop, }let action = Action::Move { x: 1 , y: 2 };match action { Action::Move { x, y } => println! ("Move to ({}, {})" , x, y), Action::Speak (message) => println! ("Speak: {}" , message), Action::Stop => println! ("Stop" ), }
嵌套解构
解构的模式类似于初始化时的写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 struct Point { x: i32 , y: i32 , }struct Circle { center: Point, radius: i32 , }let circle = Circle { center: Point { x: 1 , y: 2 }, radius: 5 , };let Circle { center: Point { x, y }, radius } = circle; println! ("Circle center: ({}, {}), radius: {}" , x, y, radius);
忽略模式
在模式匹配中,可以忽略某些值
使用 _
忽略整个值
下划线 _
是一个通配符,可以匹配任何值但不绑定到变量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 fn main () { let x = 5 ; let _ = calculate_value (); match x { 1 => println! ("One" ), 2 => println! ("Two" ), _ => println! ("Other" ), } }fn calculate_value () -> i32 { 42 }
使用下划线忽略部分值
可以在模式中使用 _
忽略元组、结构体或者模式的一部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 let tuple = (1 , 2 , 3 , 4 );let (first, _, third, _) = tuple;println! ("first: {}, third: {}" , first, third);struct Point { x: i32 , y: i32 , z: i32 , }let point = Point { x: 1 , y: 2 , z: 3 };let Point { x, y: _, z } = point;println! ("x: {}, z: {}" , x, z);fn foo (_: i32 , y: i32 ) { println! ("y: {}" , y); }foo (3 , 4 );
使用 ..
忽略剩余值
双点号 ..
可以用来忽略模式中剩余的部分:
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 let tuple = (1 , 2 , 3 , 4 , 5 );let (first, .., last) = tuple;println! ("first: {}, last: {}" , first, last);struct Point3D { x: i32 , y: i32 , z: i32 , label: String , visible: bool , }let point = Point3D { x: 10 , y: 20 , z: 30 , label: String ::from ("point1" ), visible: true , };let Point3D { x, ..} = point;println! ("x: {}" , x);match point { Point3D { x: 0 , .. } => println! ("On the YZ plane" ), Point3D { x: 10 , y, .. } => println! ("x is 10, y is {}" , y), Point3D { x, y, z, .. } => println! ("Located at ({}, {}, {})" , x, y, z), }
..
必须是无歧义的。例如,(first, .., second, ..)
会导致编译错误,因为无法确定哪些值应该匹配第一个 ..
和哪些值应该匹配第二个 ..
。
使用前缀下划线 _var
忽略未使用变量
如果你需要绑定一个变量但不打算使用它,可以给变量名加上下划线前缀来避免未使用变量的警告:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 fn process_data (data: &str ) { let (name, age) = get_user_info (data); println! ("Name: {}" , name); }fn process_data_better (data: &str ) { let (name, _age) = get_user_info (data); println! ("Name: {}" , name); }fn get_user_info (data: &str ) -> (String , u32 ) { (String ::from ("Alice" ), 30 ) }
下划线前缀 _age
与单个下划线 _
的区别在于,_age
仍然会绑定值,而 _
根本不会绑定。
函数和闭包
使用 fn
关键字定义函数
1 2 3 4 5 6 7 8 9 10 11 fn function_name (param1: Type1, param2: Type2) -> ReturnType { if condition { return value; } expression }fn do_nothing () { }
函数可以作为参数,其类型是 fn
1 2 3 4 5 6 7 8 9 fn function (){ println! ("call function" ); }fn call (f:fn ()) { f () }call (function);
闭包 (Closure)
闭包类似其他语言中的 lambda 表达式。它们可以捕获周围的环境。闭包的语法如下:
1 2 3 4 5 6 let closure_name = |param1: Type1, param2: Type2| -> ReturnType { };let add = |x: i32 , y: i32 | x + y; println! ("{}" , add (1 , 2 ));
闭包可以捕获上下文中的变量,闭包有三种捕获方式:
通过引用: &T
通过可变引用: &mut T
通过值: T
1 2 3 4 5 6 7 8 9 10 11 let name = String ::from ("Alice" );let greet = || { println! ("Hello, {}!" , name); };greet (); println! ("Name is still available: {}" , name);
1 2 3 4 5 6 7 8 9 10 11 12 13 let mut counter = 0 ;let mut increment = || { counter += 1 ; println! ("Counter: {}" , counter); };increment (); increment (); println! ("Final counter: {}" , counter);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 let name = String ::from ("Bob" );let greet = || { println! ("Hello, {}!" , name); take_ownership (name); }greet (); fn take_ownership (s: String ) { println! ("Goodbye {}!" , s); }
1 2 3 4 5 6 7 8 9 10 11 let name = String ::from ("Charlie" );let greeting = move || { println! ("Goodbye, {}!" , name); };greeting ();
上面三种捕获方式直接影响闭包的 trait 实现:
Fn
: 通过引用捕获,闭包可以多次调用,但在最后一次调用前不能可变借用被捕获的变量
FnMut
: 通过可变引用捕获,闭包可以多次调用,但在最后一次调用前不能再次借用被捕获的变量
FnOnce
: 通过值捕获,闭包只能调用一次,因为它消耗了捕获的变量
三种 trait 的关系:FnOnce
> FnMut
> Fn
。
只要闭包中有变量是通过值捕获的,就实现 FnOnce
;如果没有值捕获,但有可变引用捕获,则实现了 FnMut
;如果只有引用捕获,则实现了 Fn
。
闭包可以作为函数的参数或返回值。在返回闭包时必须使用 move
关键字来捕获变量的所有权,否则在函数退出后产生无效引用。
1 2 3 4 5 6 7 8 fn apply <F>(f: F) where F: FnOnce () { f (); }fn create_closure () -> impl FnOnce () { let name = String ::from ("Dave" ); move || println! ("Hello, {}!" , name) }
高阶函数
高阶函数是指接受一个或多个函数作为参数,并 / 或返回另一个函数的函数。在迭代器中经常使用高阶函数。
1 2 3 4 5 6 7 8 let numbers = vec! [1 , 2 , 3 , 4 , 5 ];let sum_of_squares_of_odds : i32 = numbers.iter () .filter (|&&x| x % 2 != 0 ) .map (|&x| x * x) .sum ();println! ("{}" , sum_of_squares_of_odds);
发散函数
发散函数不会返回,使用 !
标记返回类型。
1 2 3 fn no_return () -> ! { panic! ("This function never returns!" ); }
!
可以被转换为任何类型,这在有些时候很有用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 fn sum_odd_numbers (up_to: u32 ) -> u32 { let mut acc = 0 ; for i in 0 ..up_to { let addition : u32 = match i%2 == 1 { true => i, false => continue , }; acc += addition; } acc }println! ("Sum of odd numbers up to 9 (excluding): {}" , sum_odd_numbers (9 ));
模块
错误处理
泛型
所有权和生命周期
所有权规则
Rust 的所有权系统遵循三个基本规则:
每个值都有一个所有者(变量)
值在任意时刻只能有一个所有者
当所有者离开作用域时,值会被丢弃
1 2 3 4 5 6 { let s1 = String ::from ("hello" ); let s2 = s1; println! ("{}" , s2); }
栈与堆
理解所有权之前,需要先了解栈与堆的区别:
栈 :先进后出,存储已知固定大小的数据。数据必须具有已知固定的大小
堆 :内存分配器找到足够大的空间,标记为已使用,返回指针。数据大小可以在运行时改变
所有权转移(Move)
当涉及堆数据时,赋值操作会导致所有权转移:
1 2 3 4 let s1 = String ::from ("hello" ); let s2 = s1;
实现了 Copy
trait 的类型不会发生所有权转移,而是进行复制:
所有整数类型,如 u32
布尔类型,bool
浮点数类型,如 f64
字符类型,char
仅包含以上类型的元组,如 (i32, i32)
1 2 3 let x = 5 ;let y = x; println! ("x = {}, y = {}" , x, y);
克隆(Clone)
如果需要深度复制堆数据而不是转移所有权,可以使用 clone
方法:
1 2 3 let s1 = String ::from ("hello" );let s2 = s1.clone (); println! ("s1 = {}, s2 = {}" , s1, s2);
函数与所有权
函数参数和返回值也遵循所有权规则:
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 s1 = String ::from ("hello" ); take_ownership (s1); let s2 = gives_ownership (); println! ("{}" , s2); let s3 = String ::from ("world" ); let s4 = takes_and_gives_back (s3); println! ("{}" , s4); }fn take_ownership (some_string: String ) { println! ("{}" , some_string); } fn gives_ownership () -> String { let some_string = String ::from ("yours" ); some_string }fn takes_and_gives_back (a_string: String ) -> String { a_string }
引用和借用
为了避免每次传递值都转移所有权,Rust 提供了 "引用" 的概念,允许使用值但不获取其所有权。
引用基础
创建引用的行为称为 "借用":
1 2 3 4 5 6 7 8 9 fn main () { let s1 = String ::from ("hello" ); let len = calculate_length (&s1); println! ("'{}' 的长度是 {}." , s1, len); }fn calculate_length (s: &String ) -> usize { s.len () }
引用的示意图:
1 2 3 4 s1 -----> String("hello" ) ^ | s
可变引用
引用默认是不可变的。如果想修改借用的值,需要使用可变引用:
1 2 3 4 5 6 7 8 9 fn main () { let mut s = String ::from ("hello" ); change (&mut s); println! ("{}" , s); }fn change (some_string: &mut String ) { some_string.push_str (", world" ); }
引用规则
引用有两条核心规则:
在任意给定时间,要么只能有一个可变引用,要么只能有多个不可变引用
引用必须始终有效(不能指向已释放的内存)
这些规则在编译时防止数据竞争(data race):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let mut s = String ::from ("hello" );let r1 = &s; let r2 = &s; println! ("{} and {}" , r1, r2);let r3 = &mut s; println! ("{}" , r3);let mut s = String ::from ("hello" );let r1 = &mut s;
悬垂引用
Rust 编译器确保引用永远不会成为悬垂引用(指向已释放的内存):
1 2 3 4 5 6 7 8 fn main () { let reference_to_nothing = dangle (); }fn dangle () -> &String { let s = String ::from ("hello" ); &s }
解决方法是直接返回 String
:
1 2 3 4 fn no_dangle () -> String { let s = String ::from ("hello" ); s }
解引用与自动解引用
使用 *
操作符可以访问引用指向的值:
1 2 3 4 let x = 5 ;let y = &x;assert_eq! (5 , x);assert_eq! (5 , *y);
Rust 在许多情况下会自动解引用,特别是:
方法调用:object_ref.method()
字段访问:object_ref.field
比较操作:ref1 == ref2
与模式匹配结合使用:match *ref_val { ... }
1 2 3 4 5 6 7 8 9 10 struct Point { x: i32 , y: i32 , }let point = Point { x: 1 , y: 2 };let point_ref = &point;println! ("坐标: ({}, {})" , point_ref.x, point_ref.y);
切片(Slice)
切片是对集合中部分连续元素的引用,不拥有所有权:
1 2 3 4 let s = String ::from ("hello world" );let hello = &s[0 ..5 ]; let world = &s[6 ..11 ]; let whole = &s[..];
字符串字面量是字符串切片:
1 let s : &str = "Hello, world!" ;
函数参数应优先使用切片类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 fn first_word (s: &str ) -> &str { let bytes = s.as_bytes (); for (i, &item) in bytes.iter ().enumerate () { if item == b' ' { return &s[0 ..i]; } } &s[..] }let my_string = String ::from ("hello world" );let word = first_word (&my_string[..]); let my_literal = "hello world" ;let word = first_word (my_literal);
数组切片:
1 2 let a = [1 , 2 , 3 , 4 , 5 ];let slice = &a[1 ..3 ];
生命周期
生命周期是 Rust 另一个独特的概念,用于确保引用在使用时始终有效。
生命周期的概念
生命周期是引用有效的范围。大多数情况下,生命周期是隐含的,但有时需要显式标注:
1 2 3 4 5 6 7 8 9 fn main () { let r ; { let x = 5 ; r = &x; } println! ("r: {}" , r); }
生命周期标注语法
生命周期标注以撇号('
)开头,通常使用小写字母如 'a
:
1 2 3 &i32 &'a i32 &'a mut i32
函数中的生命周期标注
当函数有多个引用参数并返回引用时,编译器可能无法推断生命周期之间的关系:
1 2 3 4 5 6 7 8 fn longest (x: &str , y: &str ) -> &str { if x.len () > y.len () { x } else { y } }
使用生命周期标注,明确参数和返回值的关系:
1 2 3 4 5 6 7 fn longest <'a >(x: &'a str , y: &'a str ) -> &'a str { if x.len () > y.len () { x } else { y } }
这表示 x 和 y 至少与返回的引用存活一样长。
使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 fn main () { let string1 = String ::from ("abcd" ); let string2 = "xyz" ; let result = longest (string1.as_str (), string2); println! ("最长的字符串是: {}" , result); let string1 = String ::from ("long string is long" ); let result ; { let string2 = String ::from ("xyz" ); result = longest (string1.as_str (), string2.as_str ()); } }
生命周期省略规则
Rust 有三条生命周期省略规则,让代码更简洁:
每个引用参数都有自己的生命周期参数
如果只有一个输入生命周期参数,它会被赋给所有输出生命周期参数
如果有多个输入生命周期参数,但其中一个是 &self
或 &mut self
,则 self 的生命周期被赋给所有输出生命周期参数
例如,以下两个函数签名是等价的:
1 2 fn first_word (s: &str ) -> &str { }fn first_word <'a >(s: &'a str ) -> &'a str { }
结构体中的生命周期
当结构体持有引用时,需要标注生命周期:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 struct ImportantExcerpt <'a > { part: &'a str , }fn main () { let novel = String ::from ("Call me Ishmael. Some years ago..." ); let first_sentence = novel.split ('.' ).next ().expect ("找不到句号" ); let excerpt = ImportantExcerpt { part: first_sentence, }; println! ("{}" , excerpt.part); }
静态生命周期
'static
生命周期表示引用在整个程序运行期间都有效。所有字符串字面量都有 'static
生命周期:
1 let s : &'static str = "我有静态生命周期" ;
生命周期约束与泛型
生命周期可以与泛型和 trait 约束结合使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 use std::fmt::Display;fn longest_with_an_announcement <'a , T>( x: &'a str , y: &'a str , ann: T, ) -> &'a str where T: Display, { println! ("公告:{}" , ann); if x.len () > y.len () { x } else { y } }
trait
宏