记录一下学习 course.rs 过程中容易忘记的点,内容包括基础入门方法到格式化输出
方法 实现私有属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 pub struct Rectangle { width: u32 , height: u32 , } impl Rectangle { pub fn new (width: u32 , height: u32 ) -> Self { Rectangle { width, height } } pub fn width (&self ) -> u32 { return self .width; } } use grep_rs::Rectangle;fn main () { let rect1 = Rectangle::new (30 , 50 ); println! ("{}" , rect1.width ()); }
impl 可以存在多个,目的是提供更多的灵活性和代码组织性,例如当方法多了后,可以把相关的方法组织在同一个 impl 块中,那么就可以形成多个 impl 块,各自完成一块儿目标
不止结构体可以实现方法,枚举也可以。
泛型和特征 结构体和枚举使用泛型 1 2 3 4 struct Point <T> { x: T, y: T, }
语言自带的两个常用枚举
1 2 3 4 5 6 7 8 enum Option <T> { Some (T), None , } enum Result <T, E> { Ok (T), Err (E), }
泛型函数和方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 fn add <T: std::ops::Add<Output = T>>(a:T, b: T) -> T { a + b } fn find_max <T: std::cmp::Ord + Copy >(list: &Vec <T>) -> &T { let mut max = &list[0 ]; for item in list.iter () { if item > max { max = item; } } max } fn main () { println! ("add i8: {}" , add (2i8 , 3i8 )); println! ("add i32: {}" , add (20 , 30 )); println! ("add f64: {}" , add (1.23 , 1.23 )); println! ("find_max: {}" , find_max (vec! [1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ])); }
为具体的泛型类型实现方法
1 2 3 4 5 impl Point <f32 > { fn distance_from_origin (&self ) -> f32 { (self .x.powi (2 ) + self .y.powi (2 )).sqrt () } }
const 泛型(Rust 1.51 版本引入的重要特性) 1 2 3 4 5 6 7 8 9 10 fn display_array (arr: [i32 ; 3 ]) { println! ("{:?}" , arr); } fn main () { let arr : [i32 ; 3 ] = [1 , 2 , 3 ]; display_array (arr); let arr : [i32 ;2 ] = [1 ,2 ]; display_array (arr); }
很简单,只要使用数组切片,然后传入 arr 的不可变引用即可。可以这样改:
1 2 3 fn display_array (arr: &[i32 ]) { println! ("{:?}" , arr); }
接着,将 i32 改成所有类型的数组:
1 2 3 4 5 6 7 8 9 10 fn display_array <T: std::fmt::Debug >(arr: &[T]) { println! ("{:?}" , arr); } fn main () { let arr : [i32 ; 3 ] = [1 , 2 , 3 ]; display_array (&arr); let arr : [i32 ;2 ] = [1 ,2 ]; display_array (&arr); }
不过,它们会为每个长度都单独实现一个函数。。。
现在有了const泛型,正好用于处理数组长度
1 2 3 4 5 6 7 8 9 10 fn display_array <T: std::fmt::Debug , const N: usize >(arr: [T; N]) { println! ("{:?}" , arr); } fn main () { let arr : [i32 ; 3 ] = [1 , 2 , 3 ]; display_array (arr); let arr : [i32 ; 2 ] = [1 , 2 ]; display_array (arr); }
重点在于 N
这个泛型参数,它是一个基于值的泛型参数!因为它用来替代的是数组的长度。
const 泛型表达式 假设我们某段代码需要在内存很小的平台上工作,因此需要限制函数参数占用的内存大小,此时就可以使用 const 泛型表达式来实现:
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 #![allow(incomplete_features)] #![feature(generic_const_exprs)] fn something <T>(val: T)where Assert<{ core::mem::size_of::<T>() < 768 }>: IsTrue, { } fn main () { something ([0u8 ; 0 ]); something ([0u8 ; 512 ]); something ([0u8 ; 1024 ]); } pub enum Assert <const CHECK: bool > { } pub trait IsTrue { } impl IsTrue for Assert <true > { }
特征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 pub trait Summary { fn summarize (&self ) -> String ; } pub struct Post { pub title: String , pub author: String , pub content: String , } impl Summary for Post { fn summarize (&self ) -> String { format! ("文章{}, 作者是{}" , self .title, self .author) } } pub struct Weibo { pub username: String , pub content: String } impl Summary for Weibo { fn summarize (&self ) -> String { format! ("{}发表了微博{}" , self .username, self .content) } }
默认实现
1 2 3 4 5 6 7 8 9 10 11 12 13 pub trait Summary { fn summarize_author (&self ) -> String ; fn summarize (&self ) -> String { format! ("(Read more from {}...)" , self .summarize_author ()) } } impl Summary for Weibo { fn summarize_author (&self ) -> String { format! ("@{}" , self .username) } } println! ("1 new weibo: {}" , weibo.summarize ());
使用特征作为函数参数
1 2 3 4 5 6 7 8 9 10 pub fn notify (item: &impl Summary ) { println! ("Breaking news! {}" , item.summarize ()); } pub fn notify <T: Summary>(item: &T) { println! ("Breaking news! {}" , item.summarize ()); } pub fn notify (item1: &impl Summary , item2: &impl Summary ) {} pub fn notify <T: Summary>(item1: &T, item2: &T) {}
多重约束
1 2 3 4 pub fn notify (item: &(impl Summary + Display)) {}pub fn notify <T: Summary + Display>(item: &T) {}
where约束
1 2 3 4 5 6 7 fn some_function <T: Display + Clone , U: Clone + Debug >(t: &T, u: &U) -> i32 {}fn some_function <T, U>(t: &T, u: &U) -> i32 where T: Display + Clone , U: Clone + Debug {}
使用特征约束有条件地实现方法或特征
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;struct Pair <T> { x: T, y: T, } impl <T> Pair<T> { fn new (x: T, y: T) -> Self { Self { x, y, } } } impl <T: Display + PartialOrd > Pair<T> { fn cmp_display (&self ) { if self .x >= self .y { println! ("The largest member is x = {}" , self .x); } else { println! ("The largest member is y = {}" , self .y); } }
返回impl Trait 1 2 3 4 5 6 7 8 9 10 11 12 13 14 fn getPostOrWeibo (switch: bool ) -> Box <dyn Summary> { if switch { Box ::new (Post { title: "Hello" .to_string (), author: "World" .to_string (), content: "Hello World" .to_string () }) } else { Box ::new (Weibo { username: "Rust" .to_string (), content: "Hello World" .to_string () }) } }
因为要保证返回值类型统一、大小统一,所以需要Box包装。
限制:
方法的返回类型不能是 Self
方法没有任何泛型参数
标准库中的 Clone
特征就不符合对象安全的要求
1 2 3 pub struct Screen { pub components: Vec <Box <dyn Clone >>, }
报错
1 2 3 4 5 6 7 8 error[E0038]: the trait `std::clone::Clone` cannot be made into an object --> src/lib.rs:2:5 | 2 | pub components: Vec<Box<dyn Clone>>, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::clone::Clone` cannot be made into an object | = note: the trait cannot require that `Self : Sized`
关联类型 标准库中的迭代器特征 Iterator
1 2 3 4 5 pub trait Iterator { type Item ; fn next (&mut self ) -> Option <Self ::Item>; }
1 2 3 4 5 6 7 8 9 10 11 12 impl Iterator for Counter { type Item = u32 ; fn next (&mut self ) -> Option <Self ::Item> { } } fn main () { let c = Counter{..} c.next () }
为何不用泛型,观察下面代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 trait Container <A,B> { fn contains (&self ,a: A,b: B) -> bool ; } fn difference <A,B,C>(container: &C) -> i32 where C : Container<A,B> {...} trait Container { type A ; type B ; fn contains (&self , a: &Self ::A, b: &Self ::B) -> bool ; } fn difference <C: Container>(container: &C) {}
默认泛型类型参数
1 2 3 4 5 6 trait Add <RHS=Self > { type Output ; fn add (self , rhs: RHS) -> Self ::Output; }
相同类型相加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 use std::ops::Add;#[derive(Debug, PartialEq)] struct Point { x: i32 , y: i32 , } impl Add for Point { type Output = Point; fn add (self , other: Point) -> Point { Point { x: self .x + other.x, y: self .y + other.y, } } } fn main () { assert_eq! (Point { x: 1 , y: 0 } + Point { x: 2 , y: 3 }, Point { x: 3 , y: 3 }); }
不同类型相加
1 2 3 4 5 6 7 8 9 10 11 12 use std::ops::Add;struct Millimeters (u32 );struct Meters (u32 );impl Add <Meters> for Millimeters { type Output = Millimeters; fn add (self , other: Meters) -> Millimeters { Millimeters (self .0 + (other.0 * 1000 )) } }
调用同名的方法 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 trait Pilot { fn fly (&self ); } trait Wizard { fn fly (&self ); } struct Human ;impl Pilot for Human { fn fly (&self ) { println! ("This is your captain speaking." ); } } impl Wizard for Human { fn fly (&self ) { println! ("Up!" ); } } impl Human { fn fly (&self ) { println! ("*waving arms furiously*" ); } } fn main () { let person = Human; Pilot::fly (&person); Wizard::fly (&person); person.fly (); }
方法没有 self 参数,使用完全限定语法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 trait Animal { fn baby_name () -> String ; } struct Dog ;impl Dog { fn baby_name () -> String { String ::from ("Spot" ) } } impl Animal for Dog { fn baby_name () -> String { String ::from ("puppy" ) } } fn main () { println! ("A baby dog is called a {}" , <Dog as Animal>::baby_name ()); }
特征定义中的特征约束(supertrait) 1 2 3 4 5 6 7 8 9 10 11 12 13 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! ("*{}*" , " " .repeat (len + 2 )); println! ("* {} *" , output); println! ("*{}*" , " " .repeat (len + 2 )); println! ("{}" , "*" .repeat (len + 4 )); } }
在外部类型上实现外部特征(newtype) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 use std::fmt;struct Wrapper (Vec <String >);impl fmt ::Display for Wrapper { fn fmt (&self , f: &mut fmt::Formatter) -> fmt::Result { write! (f, "[{}]" , self .0 .join (", " )) } } fn main () { let w = Wrapper (vec! [String ::from ("hello" ), String ::from ("world" )]); println! ("w = {}" , w); }
集合类型 Vector 1 2 3 4 5 6 7 let v : Vec <i32 > = Vec ::new ();let mut v = Vec ::new ();v.push (1 ); let v = vec! [1 , 2 , 3 ];
从 Vector 中读取元素 1 2 3 4 let v = vec! [1 , 2 , 3 , 4 , 5 ];let does_not_exist = &v[100 ];let does_not_exist = v.get (100 );
&v[100]
的访问方式会导致程序无情报错退出,因为发生了数组越界访问
v.get
不会,它在内部做了处理,有值的时候返回 Some(T)
,无值的时候返回 None
1 2 3 4 let mut v = vec! [1 , 2 , 3 , 4 , 5 ];let first = &v[0 ];v.push (6 ); println! ("The first element is: {first}" );
push后动态数组可能回扩容重新分配空间,&v[0]
地址可能并不是扩容后的地址。
迭代遍历 Vector 中的元素 1 2 3 4 5 6 7 8 let v = vec! [1 , 2 , 3 ];for i in &v { println! ("{i}" ); } let mut v = vec! [1 , 2 , 3 ];for i in &mut v { *i += 10 }
每次都会检测边界,比下标访问更安全,但是性能不如下标访问。
存储不同类型的元素 枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #[derive(Debug)] enum IpAddr { V4 (String ), V6 (String ) } fn main () { let v = vec! [ IpAddr::V4 ("127.0.0.1" .to_string ()), IpAddr::V6 ("::1" .to_string ()) ]; for ip in v { show_addr (ip) } } fn show_addr (ip: IpAddr) { println! ("{:?}" ,ip); }
特征对象
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 trait IpAddr { fn display (&self ); } struct V4 (String );impl IpAddr for V4 { fn display (&self ) { println! ("ipv4: {:?}" ,self .0 ) } } struct V6 (String );impl IpAddr for V6 { fn display (&self ) { println! ("ipv6: {:?}" ,self .0 ) } } fn main () { let v : Vec <Box <dyn IpAddr>> = vec! [ Box ::new (V4 ("127.0.0.1" .to_string ())), Box ::new (V6 ("::1" .to_string ())), ]; for ip in v { ip.display (); } }
数组方法 常用方法:
insert:在指定索引插入数据,索引值不能大于 v 的长度,比如:v.insert(2, 3);
remove:移除指定位置的元素并返回
pop:删除并返回尾部的元素
clear:清空数组
append:拼接数组:v.append(&mut v2);
truncate:截断数组,只保留指定长度的元素
retain:只保留满足条件的元素:v.retain(|&mut x| x % 2 == 0);
排序分稳定排序:sort
和 sort_by
,非稳定排序:sort_unstable
和 sort_unstable_by
1 2 3 4 5 fn main () { let mut vec = vec! [1.0 , 5.6 , 10.3 , 2.0 , 15f32 ]; vec.sort_unstable (); assert_eq! (vec, vec! [1.0 , 2.0 , 5.6 , 10.3 , 15f32 ]); }
浮点数中可能存在 NAN
,无法进行比较,如果确定在浮点数数组当中,不包含 NAN
值,可以这样写
1 2 3 4 5 fn main() { let mut vec = vec![1.0, 5.6, 10.3, 2.0, 15f32]; vec.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap()); assert_eq!(vec, vec![1.0, 2.0, 5.6, 10.3, 15f32]); }
自定义数据结构排序
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 #[derive(Debug, Ord, Eq, PartialEq, PartialOrd)] struct Person { name: String , age: u32 , } impl Person { fn new (name: String , age: u32 ) -> Person { Person { name, age } } } fn main () { let mut people = vec! [ Person::new ("Zoe" .to_string (), 25 ), Person::new ("Al" .to_string (), 60 ), Person::new ("Al" .to_string (), 30 ), Person::new ("John" .to_string (), 1 ), Person::new ("John" .to_string (), 25 ), ]; people.sort_unstable (); println! ("{:?}" , people); }
HashMap 创建
1 2 3 4 5 6 7 8 9 use std::collections::HashMap;let mut my_gems = HashMap::new ();my_gems.insert ("红宝石" , 1 ); my_gems.insert ("蓝宝石" , 2 ); my_gems.insert ("河边捡的误以为是宝石的破石头" , 18 );
如果 insert
的内容实现了 Copy
,该类型会被 Copy
,否则会移动,不能使用。
vec转为HashMap
1 2 3 4 5 6 7 let teams_list = vec! [ ("中国队" .to_string (), 100 ), ("美国队" .to_string (), 10 ), ("日本队" .to_string (), 50 ), ]; let teams_map : HashMap<_,_> = teams_list.into_iter ().collect ();
HashMap<_,_>
表示让编译器推导类型
查询
1 2 3 4 5 6 7 8 9 use std::collections::HashMap;let mut scores = HashMap::new ();scores.insert (String ::from ("Blue" ), 10 ); scores.insert (String ::from ("Yellow" ), 50 ); let team_name = String ::from ("Blue" );let score : Option <&i32 > = scores.get (&team_name);
get方法返回值为 Option<T>
,如果查找不到,返回 None
,否则返回 Some(T)
。
更新
1 2 3 4 5 6 7 8 9 10 11 12 use std::collections::HashMap;let text = "hello world wonderful world" ;let mut map = HashMap::new ();for word in text.split_whitespace () { let count = map.entry (word).or_insert (0 ); *count += 1 ; } println! ("{:?}" , map);
or_insert
返回了 &mut v
引用,因此可以通过该可变引用直接修改 map 中对应的值
生命周期 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! ("Announcement! {}" , ann); if x.len () > y.len () { x } else { y } }
错误处理 使用 ?
语法糖
1 2 3 4 5 6 7 8 9 10 11 use std::fs::File;use std::io;use std::io::Read;fn read_username_from_file () -> Result <String , io::Error> { let mut s = String ::new (); File::open ("hello.txt" )?.read_to_string (&mut s)?; Ok (s) }
?
语法糖可以链式调用,也可以进行类型隐式转换。只要函数返回的错误 ReturnError
实现了 From<OtherError>
特征,那么 ?
就会自动把 OtherError
转换为 ReturnError
。
除了Result
,Option
也可以用 ?
语法糖
1 2 3 fn last_char_of_first_line (text: &str ) -> Option <char > { text.lines ().next ()?.chars ().last () }
另外一种形式的main
1 2 3 4 5 6 7 8 use std::error::Error;use std::fs::File;fn main () -> Result <(), Box <dyn Error>> { let f = File::open ("hello.txt" )?; Ok (()) }
文档 文档测试(Doc Test) 1 2 3 4 5 6 7 8 9 10 11 12 13 pub fn add_one (x: i32 ) -> i32 { x + 1 }
ide打开能看到代码左边有个运行,可以直接运行这个test
运行会panic的的例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 pub fn div (a: i32 , b: i32 ) -> i32 { if b == 0 { panic! ("Divide-by-zero error" ); } a / b }
需要加上 rust,should_panic
保留测试,隐藏文档 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 pub fn try_div (a: i32 , b: i32 ) -> Result <i32 , String > { if b == 0 { Err (String ::from ("Divide-by-zero" )) } else { Ok (a / b) } }
文档注释中的代码跳转 跳转到标准库
1 2 3 4 pub fn add_one (x: i32 ) -> Option <i32 > { Some (x + 1 ) }
在 IDE 中,使用 Command + 鼠标左键(macOS),CTRL + 鼠标左键(Windows)
在文档中直接点击链接
路径跳转
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 use std::sync::mpsc::Receiver;pub struct AsyncReceiver <T> { sender: Receiver<T>, } impl <T> AsyncReceiver<T> { pub async fn recv () -> T { unimplemented! () } } pub mod a { pub fn add_one (x: i32 ) -> Option <i32 > { Some (x + 1 ) } } pub struct MySpecialFormatter ;
同名
1 2 3 4 5 6 7 8 9 10 11 12 13 pub struct Bar ;pub struct Foo {}pub fn Foo () {}#[macro_export] macro_rules! foo { () => {} }
文档搜索别名
1 2 3 4 5 6 #[doc(alias = "x" )] #[doc(alias = "big" )] pub struct BigX ;#[doc(alias("y" , "big" ))] pub struct BigY ;
格式化输出 1 2 3 4 5 6 7 8 9 10 11 12 13 14 println! ("Hello" ); println! ("Hello, {}!" , "world" ); println! ("The number is {}" , 1 ); println! ("{:?}" , (3 , 4 )); println! ("{value}" , value=4 ); println! ("{} {}" , 1 , 2 ); println! ("{:04}" , 42 ); let s1 = format! ("{}, world" , s);print! ("{}" , s1);print! ("{}\n" , "!" ); eprint!() eprintln! ()
占位符
{}
适用于实现了 std::fmt::Display
特征的类型,用来以更优雅、更友好的方式格式化文本,例如展示给用户
{:?}
适用于实现了 std::fmt::Debug
特征的类型,用于调试场景
{:#?}
类似 {:?}
但是经过格式化,更加好看
实现display:
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 struct Person { name: String , age: u8 , } use std::fmt;impl fmt ::Display for Person { fn fmt (&self , f: &mut fmt::Formatter) -> fmt::Result { write! ( f, "大佬在上,请受我一拜,小弟姓名{},年芳{},家里无田又无车,生活苦哈哈" , self .name, self .age ) } } struct Array (Vec <i32 >);use std::fmt;impl fmt ::Display for Array { fn fmt (&self , f: &mut fmt::Formatter) -> fmt::Result { write! (f, "数组是:{:?}" , self .0 ) } } fn main () { let p = Person { name: "sunface" .to_string (), age: 18 , }; println! ("{}" , p); let arr = Array (vec! [1 , 2 , 3 ]); println! ("{}" , arr); }
具名参数
1 2 3 4 5 6 fn main () { println! ("{argument}" , argument = "test" ); println! ("{name} {}" , 1 , name = 2 ); println! ("{a} {c} {b}" , a = "a" , b = 'b' , c = 3 ); println! ("{abc} {1}" , abc = "def" , 2 ); }
带名称的参数必须放在不带名称参数的后面
格式化参数 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 fn main () { println! ("Hello {:5}!" , 5 ); println! ("Hello {:+}!" , 5 ); println! ("Hello {:05}!" , 5 ); println! ("Hello {:05}!" , -5 ); println! ("Hello {:<5}!" , "x" ); println! ("Hello {:>5}!" , "x" ); println! ("Hello {:^5}!" , "x" ); println! ("Hello {:&<5}!" , "x" ); let v = 3.1415926 ; println! ("{:.2}" , v); println! ("{:+.2}" , v); println! ("{:.0}" , v); println! ("{:.1$}" , v, 4 ); let s = "hi我是Sunface孙飞" ; println! ("{:.3}" , s); println! ("Hello {:.*}!" , 3 , "abcdefg" ); println! ("{:#b}!" , 27 ); println! ("{:#o}!" , 27 ); println! ("{}!" , 27 ); println! ("{:#x}!" , 27 ); println! ("{:#X}!" , 27 ); println! ("{:x}!" , 27 ); println! ("{:#010b}!" , 27 ); println! ("{:2e}" , 1000000000 ); println! ("{:2E}" , 1000000000 ); let v = vec! [1 , 2 , 3 ]; println! ("{:p}" , v.as_ptr ()) println! (" Hello \"{{World}}\" " ); }
在格式化字符串时捕获环境中的值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 fn get_person () -> String { String ::from ("sunface" ) } fn main () { let p = get_person (); println! ("Hello, {}!" , p); println! ("Hello, {0}!" , p); println! ("Hello, {person}!" , person = p); println! ("Hello, {person}!" ); let (width, precision) = get_format (); for (name, score) in get_scores () { println! ("{name}: {score:width$.precision$}" ); } }