欢迎来到第二天,现在我们已经看到了相当数量的 Rust 代码,我们将继续:
结构、枚举、方法。
模式匹配:解构枚举、结构和数组。
控制流构造:if
, if let
, while
, while let
, break
, 和 continue
.
标准库: String
, Option
和 Result
, Vec
, HashMap
, Rc
和 Arc
.
模块:可见性、路径和文件系统层次结构。
结构体 类似 C 、 C++:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 struct Person { name: String , age: u8 , } fn main () { let mut peter = Person { name: String ::from ("Peter" ), age: 27 , }; println! ("{} is {} years old" , peter.name, peter.age); peter.age = 28 ; println! ("{} is {} years old" , peter.name, peter.age); let jackie = Person { name: String ::from ("Jackie" ), ..peter }; println! ("{} is {} years old" , jackie.name, jackie.age); }
元组结构体 / Tuple Structs 如果字段名不重要,可以使用元组结构体:
1 2 3 4 5 6 struct Point (i32 , i32 );fn main () { let p = Point (17 , 23 ); println! ("({}, {})" , p.0 , p.1 ); }
这通常用于单字段包装器(称为newtypes):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct PoundOfForce (f64 );struct Newtons (f64 );fn compute_thruster_force () -> PoundOfForce { todo!("Ask a rocket scientist at NASA" ) } fn set_thruster_force (force: Newtons) { } fn main () { let force = compute_thruster_force (); set_thruster_force (force); }
字段简写语法 如果已经拥有具有正确名称的变量,则可以使用简写创建结构体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #[derive(Debug)] struct Person { name: String , age: u8 , } impl Person { fn new (name: String , age: u8 ) -> Person { Person { name, age } } } fn main () { let peter = Person::new (String ::from ("Peter" ), 27 ); println! ("{peter:?}" ); }
枚举 enum
关键字允许创建具有几个不同变体的类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 fn generate_random_number () -> i32 { 4 } #[derive(Debug)] enum CoinFlip { Heads, Tails, } fn flip_coin () -> CoinFlip { let random_number = generate_random_number (); if random_number % 2 == 0 { return CoinFlip::Heads; } else { return CoinFlip::Tails; } } fn main () { println! ("You got: {:?}" , flip_coin ()); }
载荷变体 您可以定义更丰富的枚举,其中变量携带数据。然后可以使用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 enum WebEvent { PageLoad, KeyPress (char ), Click { x: i64 , y: i64 }, } #[rustfmt::skip] fn inspect (event: WebEvent) { match event { WebEvent::PageLoad => println! ("page loaded" ), WebEvent::KeyPress (c) => println! ("pressed '{c}'" ), WebEvent::Click { x, y } => println! ("clicked at x={x}, y={y}" ), } } fn main () { let load = WebEvent::PageLoad; let press = WebEvent::KeyPress ('x' ); let click = WebEvent::Click { x: 20 , y: 80 }; inspect (load); inspect (press); inspect (click); }
枚举大小 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 use std::mem::{align_of, size_of};macro_rules! dbg_size { ($t:ty) => { println! ("{}: size {} bytes, align: {} bytes" , stringify! ($t), size_of::<$t>(), align_of::<$t>()); }; } enum Foo { A, B, } #[repr(u32)] enum Bar { A, B = 10000 , C, } fn main () { dbg_size!(Foo); dbg_size!(Bar); dbg_size!(bool ); dbg_size!(Option <bool >); dbg_size!(&i32 ); dbg_size!(Option <&i32 >); }
方法 Rust允许您将函数与新类型关联起来,使用impl
语句:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #[derive(Debug)] struct Person { name: String , age: u8 , } impl Person { fn say_hello (&self ) { println! ("Hello, my name is {}" , self .name); } } fn main () { let peter = Person { name: String ::from ("Peter" ), age: 27 , }; peter.say_hello (); }
方法接收者 上面的&self
表示该方法不可变地借用对象。方法还有其他可能的接收者:
&self
:使用共享的和不可变的引用从调用者那里借用对象。对象之后可以再次使用。
&mut self
:使用唯一且可变的引用从调用者那里借用对象。对象之后可以再次使用。
self
: 获取对象的所有权并将其移离调用者。方法成为对象的所有者。当方法返回时,该对象将被删除(释放),除非显式传输其所有权。
mut self
:与上面相同,但是当方法拥有对象时,它也可以改变它。完全的所有权并不自动意味着可变性。
没有接收者:这将成为结构上的静态方法。通常用于创建按照约定称为new的构造函数。
除了self
上的变量之外,还有一些特殊的包装器类型允许作为接收器类型,例如Box<self>
。
例子 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 #[derive(Debug)] struct Race { name: String , laps: Vec <i32 >, } impl Race { fn new (name: &str ) -> Race { Race { name: String ::from (name), laps: Vec ::new () } } fn add_lap (&mut self , lap: i32 ) { self .laps.push (lap); } fn print_laps (&self ) { println! ("Recorded {} laps for {}:" , self .laps.len (), self .name); for (idx, lap) in self .laps.iter ().enumerate () { println! ("Lap {idx}: {lap} sec" ); } } fn finish (self ) { let total = self .laps.iter ().sum::<i32 >(); println! ("Race {} is finished, total lap time: {}" , self .name, total); } } fn main () { let mut race = Race::new ("Monaco Grand Prix" ); race.add_lap (70 ); race.add_lap (68 ); race.print_laps (); race.add_lap (71 ); race.print_laps (); race.finish (); }
模式匹配 / pattern matching match
关键字允许您根据一个或多个模式匹配一个值。从上到下进行比较,第一个匹配的胜出。
模式可以是简单的值,类似于C和C++中的switch
:
1 2 3 4 5 6 7 8 9 10 fn main () { let input = 'x' ; match input { 'q' => println! ("Quitting" ), 'a' | 's' | 'w' | 'd' => println! ("Moving around" ), '0' ..='9' => println! ("Number input" ), _ => println! ("Something else" ), } }
_
模式是匹配任何值的通配符模式。
解构枚举 模式还可以用于将变量绑定到值的一部分。这是检查类型结构的方法。让我们从一个简单的枚举类型开始:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 enum Result { Ok (i32 ), Err (String ), } fn divide_in_two (n: i32 ) -> Result { if n % 2 == 0 { Result ::Ok (n / 2 ) } else { Result ::Err (format! ("cannot divide {n} into two equal parts" )) } } fn main () { let n = 100 ; match divide_in_two (n) { Result ::Ok (half) => println! ("{n} divided in two is {half}" ), Result ::Err (msg) => println! ("sorry, an error happened: {msg}" ), } }
这里的解构和ES6的解构非常像,这里如果Result如果是Ok则将Ok里的i32类型的值提取出来放到half。
解构结构体 同样可以对结构体进行解构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 struct Foo { x: (u32 , u32 ), y: u32 , } #[rustfmt::skip] fn main () { let foo = Foo { x: (1 , 2 ), y: 3 }; match foo { Foo { x: (1 , b), y } => println! ("x.0 = 1, b = {b}, y = {y}" ), Foo { y: 2 , x: i } => println! ("y = 2, i = {i:?}" ), Foo { y, .. } => println! ("y = {y}, other fields were ignored" ), } }
解构数组 可以通过匹配数组、元组和切片的元素来解构数组、元组和切片
1 2 3 4 5 6 7 8 9 10 #[rustfmt::skip] fn main () { let triple = [0 , -2 , 3 ]; println! ("Tell me about {triple:?}" ); match triple { [0 , y, z] => println! ("First is 0, y = {y}, and z = {z}" ), [1 , ..] => println! ("First is 1 and the rest were ignored" ), _ => println! ("All elements were ignored" ), } }
匹配守卫 在匹配时,您可以向模式添加保护。这是一个任意的布尔表达式,如果模式匹配就会执行:
1 2 3 4 5 6 7 8 9 10 11 #[rustfmt::skip] fn main () { let pair = (2 , -2 ); println! ("Tell me about {pair:?}" ); match pair { (x, y) if x == y => println! ("These are twins" ), (x, y) if x + y == 0 => println! ("Antimatter, kaboom!" ), (x, _) if x % 2 == 1 => println! ("The first one is odd" ), _ => println! ("No correlation..." ), } }
练习题 我们将在两种上下文中讨论方法的实现:
跟踪健康统计数据的简单结构。
绘图库的多结构体和枚举
健康统计 您正在实施一个健康监测系统。作为其中的一部分,需要跟踪用户的健康统计信息。
您将从impl块中的一些存根函数和User结构定义开始。您的目标是在impl块中定义的User结构上实现存根方法。
补上缺少的代码:
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 #![allow(unused_variables, dead_code)] struct User { name: String , age: u32 , weight: f32 , } impl User { pub fn new (name: String , age: u32 , weight: f32 ) -> Self { unimplemented! () } pub fn name (&self ) -> &str { unimplemented! () } pub fn age (&self ) -> u32 { unimplemented! () } pub fn weight (&self ) -> f32 { unimplemented! () } pub fn set_age (&mut self , new_age: u32 ) { unimplemented! () } pub fn set_weight (&mut self , new_weight: f32 ) { unimplemented! () } } fn main () { let bob = User::new (String ::from ("Bob" ), 32 , 155.2 ); println! ("I'm {} and my age is {}" , bob.name (), bob.age ()); } #[test] fn test_weight () { let bob = User::new (String ::from ("Bob" ), 32 , 155.2 ); assert_eq! (bob.weight (), 155.2 ); } #[test] fn test_set_age () { let mut bob = User::new (String ::from ("Bob" ), 32 , 155.2 ); assert_eq! (bob.age (), 32 ); bob.set_age (33 ); assert_eq! (bob.age (), 33 ); }
我的答案:
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 struct User { name: String , age: u32 , weight: f32 , } impl User { pub fn new (name: String , age: u32 , weight: f32 ) -> Self { User { name, age, weight, } } pub fn name (&self ) -> &str { &self .name } pub fn age (&self ) -> u32 { self .age } pub fn weight (&self ) -> f32 { self .weight } pub fn set_age (&mut self , new_age: u32 ) { self .age = new_age } pub fn set_weight (&mut self , new_weight: f32 ) { self .weight = new_weight } } fn main () { let mut bob = User::new (String ::from ("Bob" ), 32 , 155.2 ); println! ("I'm {} and my age is {}, weight is {}" , bob.name (), bob.age (), bob.weight ()); bob.set_age (16 ); bob.set_weight (101 as f32 ); println! ("I'm {} and my age is {}, weight is {}" , bob.name (), bob.age (), bob.weight ()); } #[test] fn test_weight () { let bob = User::new (String ::from ("Bob" ), 32 , 155.2 ); assert_eq! (bob.weight (), 155.2 ); } #[test] fn test_set_age () { let mut bob = User::new (String ::from ("Bob" ), 32 , 155.2 ); assert_eq! (bob.age (), 32 ); bob.set_age (33 ); assert_eq! (bob.age (), 33 ); }
多边形结构 我们将创建一个多边形结构,其中包含一些点:
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 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 #![allow(unused_variables, dead_code)] pub struct Point { } impl Point { } pub struct Polygon { } impl Polygon { } pub struct Circle { } impl Circle { } pub enum Shape { Polygon (Polygon), Circle (Circle), } #[cfg(test)] mod tests { use super::*; fn round_two_digits (x: f64 ) -> f64 { (x * 100.0 ).round () / 100.0 } #[test] fn test_point_magnitude () { let p1 = Point::new (12 , 13 ); assert_eq! (round_two_digits (p1.magnitude ()), 17.69 ); } #[test] fn test_point_dist () { let p1 = Point::new (10 , 10 ); let p2 = Point::new (14 , 13 ); assert_eq! (round_two_digits (p1.dist (p2)), 5.00 ); } #[test] fn test_point_add () { let p1 = Point::new (16 , 16 ); let p2 = p1 + Point::new (-4 , 3 ); assert_eq! (p2, Point::new (12 , 19 )); } #[test] fn test_polygon_left_most_point () { let p1 = Point::new (12 , 13 ); let p2 = Point::new (16 , 16 ); let mut poly = Polygon::new (); poly.add_point (p1); poly.add_point (p2); assert_eq! (poly.left_most_point (), Some (p1)); } #[test] fn test_polygon_iter () { let p1 = Point::new (12 , 13 ); let p2 = Point::new (16 , 16 ); let mut poly = Polygon::new (); poly.add_point (p1); poly.add_point (p2); let points = poly.iter ().cloned ().collect::<Vec <_>>(); assert_eq! (points, vec! [Point::new (12 , 13 ), Point::new (16 , 16 )]); } #[test] fn test_shape_perimeters () { let mut poly = Polygon::new (); poly.add_point (Point::new (12 , 13 )); poly.add_point (Point::new (17 , 11 )); poly.add_point (Point::new (16 , 16 )); let shapes = vec! [ Shape::from (poly), Shape::from (Circle::new (Point::new (10 , 20 ), 5 )), ]; let perimeters = shapes .iter () .map (Shape::perimeter) .map (round_two_digits) .collect::<Vec <_>>(); assert_eq! (perimeters, vec! [15.48 , 31.42 ]); } } #[allow(dead_code)] fn main () {}