本文最后更新于 2024-03-27T11:44:21+08:00
民间rustlings题解
2023年10月14日
所有权 读者写者问题
1 2 3 4 5 6 7 8 9 10 11 fn main () { let mut s = String ::from ("hello" ); let r1 = &s; let r2 = &s; println! ("{} and {}" , r1, r2); let r3 = &mut s; println! ("{}" , r3); }
解引用 在Rust中,解引用(Dereferencing)是获取一个引用所指向的值的操作。
例如:
1 2 3 4 let x = 5 ;let y = &x; let z = *y;
这里y
是一个指向x
的引用,它的类型是&i32
。*y
表示对y
进行解引用,访问它所指向的x
的值。
简单来说,解引用的作用就是:
另一个常见的例子是解引用智能指针:
1 2 3 let x = Box ::new (5 ); let y = &x; let z = *y;
一些需要注意的地方:
只有当引用和指针是可变的(mutable)时才能解引用为一个可变绑定。
多重引用需要多次解引用:let x = & & 5;
需要 **x
来获取值5。
解引用一个空指针会导致程序panic。
所以解引用是Rust中安全访问指针和引用的重要操作。正确理解解引用可以帮助我们更好地使用Rust中的引用与指针。
总结:
取地址&
、解引用*
和C语言的指针并无区别,用法基本相同
数据类型 结构体 基础 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct User { active: bool , username: String , email: String , sign_in_count: u64 , }fn main () { let mut user1 = User { active: true , username: String ::from ("someusername123" ), email: String ::from ("someone@example.com" ), sign_in_count: 1 , }; user1.email = String ::from ("anotheremail@example.com" ); }
类单元结构体 1 2 struct Millionaire ;let m = Millionaire;
unit-like struct
表示一个类似于()空元组的结构体,它没有任何字段。
关联函数
2023年10月15日
枚举 定义 枚举出的可以是类型,整个枚举也是类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #![allow(unused)] fn main () { enum IpAddrKind { V4, V6, } struct IpAddr { kind: IpAddrKind, address: String , } let home = IpAddr { kind: IpAddrKind::V4, address: String ::from ("127.0.0.1" ), }; }
函数传参中:
1 2 3 4 fn route (ip_type: IpAddrKind) { }route (IpAddrKind::V4); route (IpAddrKind::V6);
可以给枚举值关联数据类型
1 2 3 4 5 6 7 8 9 #![allow(unused)] fn main () {enum IpAddr { V4 (String ), V6 (String ), }let home = IpAddr::V4 (String ::from ("127.0.0.1" ));let loopback = IpAddr::V6 (String ::from ("::1" )); }
每个成员可以处理不同类型和数量的数据:
1 2 3 4 5 6 7 8 9 #![allow(unused)] fn main () { enum IpAddr { V4 (u8 , u8 , u8 , u8 ), V6 (String ), } let home = IpAddr::V4 (127 , 0 , 0 , 1 ); let loopback = IpAddr::V6 (String ::from ("::1" )); }
成员可以是各种类型:
1 2 3 4 5 6 enum Message { Quit, Move { x: i32 , y: i32 }, Write (String ), ChangeColor (i32 , i32 , i32 ), }
枚举上也可以定义方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #![allow(unused)] fn main () {enum Message { Quit, Move { x: i32 , y: i32 }, Write (String ), ChangeColor (i32 , i32 , i32 ), }impl Message { fn call (&self ) { } }let m = Message::Write (String ::from ("hello" )); m.call ();
options 它表示一个值可能存在或不存在,相当于对NULL
进行了封装
样例:
1 2 3 4 5 6 7 8 9 10 #![allow(unused)] fn main () { enum Option <T> { Some (T), None , } let some_number = Some (5 ); let some_string = Some ("a string" ); let absent_number : Option <i32 > = None ; }
match enmu:
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 #[derive(Debug)] enum UsState { Alabama, Alaska, }enum Coin { Penny, Nickel, Dime, Quarter (UsState), }fn value_in_cents (coin: Coin) -> u8 { match coin { Coin::Penny => 1 , Coin::Nickel => 5 , Coin::Dime => 10 , Coin::Quarter (state) => { println! ("State quarter from {:?}!" , state); 25 } } }fn main () { value_in_cents (Coin::Quarter (UsState::Alaska)); }
options
1 2 3 4 5 6 7 8 9 10 11 fn main () { let dice_roll = 9 ; match dice_roll { 3 => add_fancy_hat (), 7 => remove_fancy_hat (), _ => reroll (), } fn add_fancy_hat () {} fn remove_fancy_hat () {} fn reroll () {} }
ref
取消match
对变量的所有权
1 2 3 4 5 6 let y : Option <Point> = Some (Point { x: 100 , y: 200 });match y { Some (ref p) => println! ("Co-ordinates are {},{} " , p.x, p.y), _ => panic! ("no match!" ), }
if let match:
1 2 3 4 5 let mut count = 0 ;match coin { Coin::Quarter (state) => println! ("State quarter from {:?}!" , state), _ => count += 1 , }
if let
1 2 3 4 5 6 let mut count = 0 ;if let Coin ::Quarter (state) = coin { println! ("State quarter from {:?}!" , state); } else { count += 1 ; }
两者完全等价,if let
可用于options
和enmu
Vec 的 pop 函数会套一层 Option
while let 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 fn main () { let mut optional = Some (0 ); while let Some (i) = optional { if i > 9 { println! ("Greater than 9, quit!" ); optional = None ; } else { println! ("`i` is `{:?}`. Try again." , i); optional = Some (i + 1 ); } } }
样例 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 enum Message { ChangeColor (u8 ,u8 ,u8 ), Echo (String ), Move (Point), Quit, }struct Point { x: u8 , y: u8 , }struct State { color: (u8 , u8 , u8 ), position: Point, quit: bool , message: String }impl State { fn change_color (&mut self , color: (u8 , u8 , u8 )) { self .color = color; } fn quit (&mut self ) { self .quit = true ; } fn echo (&mut self , s: String ) { self .message = s } fn move_position (&mut self , p: Point) { self .position = p; } fn process (&mut self , message: Message) { match message{ Message::ChangeColor (a,b,c) => self .change_color ((a,b,c)), Message::Echo (s)=>self .message=s, Message::Move (p)=>self .move_position (p), Message::Quit=>self .quit (), } } }fn main (){ let mut state = State { quit: false , position: Point { x: 0 , y: 0 }, color: (0 , 0 , 0 ), message: "hello world" .to_string (), }; state.process (Message::ChangeColor (255 , 0 , 255 )); state.process (Message::Echo (String ::from ("hello world" ))); state.process (Message::Move (Point { x: 10 , y: 15 })); state.process (Message::Quit); }
字符串 有三种(两种)
let hello = String::from("hello");
:类型为String
let he = &hello[..2];
:类型为&str
let he="he";
:类型为&str
拼接:
1 2 3 let s1 =String ::from ("Hello, " );let s2 =String ::from ("world!" );let s3 =s1+&s2;
1 2 3 fn compose_me (input: &str ) -> String { format! ("{} world!" , input) }
trim:
1 2 3 let mut string =String ::from (" ,aaaaa " );let chars_to_trim : &[char ] = &[' ' , ',' ];let trimmed_str : &str =string.trim_matches (chars_to_trim);
1 2 3 fn trim_me (input: &str ) -> String { input.trim ().to_string () }
trim
返回的是&str
,再调用to_string
可转为String
替换:
1 2 3 4 let alice = String ::from ("I like dogs" );let bob : String = alice.replace ("dog" , "cat" );
转换
&str到String:在不考虑格式化转换或往返一个Vec或[u8]的情况下,至少存在这么多种,String::from(),to_string(),to_owned(),into(),format!
String到&str:as_str(),as_ref(),Deref<Target=str>,&x[..]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 fn string_slice (arg: &str ) { println! ("{}" , arg); }fn string (arg: String ) { println! ("{}" , arg); }fn main () { string_slice ("blue" ); string ("red" .to_string ()); string (String ::from ("hi" )); string ("rust is fun!" .to_owned ()); string ("nice weather" .into ()); string (format! ("Interpolation {}" , "Station" )); string_slice (&String ::from ("abc" )[0 ..1 ]); string_slice (" hello there " .trim ()); string ("Happy Monday!" .to_string ().replace ("Mon" , "Tues" )); string ("mY sHiFt KeY iS sTiCkY" .to_lowercase ()); }
转大写:to_uppercase()
在向量中这样使用:
1 2 3 4 fn main () { let mut shopping_list : Vec <&str > = Vec ::new (); shopping_list.push ("milk" ); }
hashmap 样例:
1 2 3 4 5 6 7 8 9 10 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 = scores.get (&team_name).copied ().unwrap_or (0 );
查看hashmap中是否有键,返回布尔值
1 basket.get (&fruit).is_some ()
返回数值,不存在则返回0:
1 basket.get (&fruit).copied ().unwrap_or (0 )
or_insert
方法在键对应的值存在时就返回这个值的可变引用,如果不存在则将参数作为新值插入并返回新值的可变引用
1 2 3 4 5 6 7 let team_1_name = v[0 ].to_string (); let g = scores.entry (team_1_name).or_insert ( Team { goals_scored: 0 , goals_conceded: 0 , }, );
Vector 创建:
1 let v : Vec <i32 > = Vec ::new ();
1 2 3 4 5 let v : Vec <i32 > = vec! [];let v = vec! [1 , 2 , 3 , 4 , 5 ];let v = vec! [0 ; 10 ];
增删:
1 2 v.push (3 );let two = v.pop ();
函数传参:
1 2 3 4 5 fn array_and_vec () -> ([i32 ; 4 ], Vec <i32 >) { let a = [10 , 20 , 30 , 40 ]; let v = vec! [10 , 20 , 30 , 40 ]; (a, v) }
包和模块
参考:https://course.rs/basic/crate-module
模块 代码可见性pub 包和函数默认都是私密的,想要从外部访问,必须加上pub
关键字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 pub mod sausage_factory { fn get_secret_recipe () -> String { String ::from ("Ginger" ) } pub fn make_sausage () { get_secret_recipe (); println! ("sausage!" ); } }fn main () { sausage_factory::make_sausage (); }
同一个模块同一层级下,不必考虑可见性:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 mod delicious_snacks { pub use self::fruits::PEAR as fruit; pub use self::veggies::CUCUMBER as veggie; mod fruits { pub const PEAR: &'static str = "Pear" ; pub const APPLE: &'static str = "Apple" ; } mod veggies { pub const CUCUMBER: &'static str = "Cucumber" ; pub const CARROT: &'static str = "Carrot" ; } }
错误处理 panic!解析 在不可恢复的错误处调用,调用后会直接退出
Result和? Result
是自带的一个枚举类型
1 2 3 4 enum Result <T, E> { Ok (T), Err (E), }
读取文件:
1 2 3 4 5 6 7 8 9 10 11 12 use std::fs::File;fn main () { let f = File::open ("hello.txt" ); let f = match f { Ok (file) => file, Err (error) => { panic! ("Problem opening the file: {:?}" , error) }, }; }
1 2 3 4 5 6 7 8 9 pub fn generate_nametag_text (name: String ) -> Result <String ,String > { if name.is_empty () { Err ("`name` was empty; it must be nonempty." .to_string ()) } else { Ok (format! ("Hi! My name is {}" , name)) } }
关于?
:
关于unwrap
如果 Result
值是成员 Ok
,unwrap
会返回 Ok
中的值。
如果 Result
是成员 Err
,unwrap
会为我们调用 panic!
。不会有返回值
泛型、Trait 泛型 使用:
1 2 3 4 5 6 7 8 9 struct Wrapper <T>{ value: T, }impl <T> Wrapper<T> { pub fn new (value: T) -> Wrapper<T> { Wrapper { value } } }
trait 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 trait AppendBar { fn append_bar (self ) -> Self ; }impl AppendBar for String { fn append_bar (self ) -> Self { format! ("{}Bar" , self ) } }fn main () { let s = String ::from ("Foo" ); let s = s.append_bar (); println! ("s: {}" , s); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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) } }fn main () { let weibo = Weibo{username: "sunface" .to_string (),content: "好像微博没Tweet好用" .to_string ()}; println! ("{}" ,post.summarize ()); }
针对vec添加:
1 2 3 4 5 6 7 8 9 10 11 12 13 trait AppendBar { fn append_bar (self ) -> Self ; }impl AppendBar for Vec <String >{ fn append_bar (mut self )-> Self { self .push ("Bar" .to_string ()); self } }let mut foo = vec! [String ::from ("Foo" )].append_bar ();
使用特征作为参数 1 2 3 pub fn notify (item: &impl Summary ) { println! ("Breaking news! {}" , item.summarize ()); }
item
必须实现了Summary
这个特征
特征约束 多重约束
1 2 3 fn some_func (item: impl SomeTrait + OtherTrait) -> bool { item.some_function () && item.other_function () }
生命周期 1 只是为了糊弄编译器
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 } }
和泛型一样,使用生命周期参数,需要先声明 <'a>
x
、y
和返回值至少活得和 'a
一样久(因为返回值要么是 x
,要么是 y
)
'a
的定义:'z
的大小就是x和y的作用域的重合部分,也就是比较短命的那个
因此,返回值的生命周期也是x和y中作用域较小的那个
解决悬垂指针 错误的:
1 2 3 4 fn longest <'a >(x: &str , y: &str ) -> &'a str { let result = String ::from ("really long string" ); result.as_str () }
result
在函数结束后就被释放,但是在函数结束后,对 result
的引用依然在继续
正确的:
1 2 3 4 5 6 7 fn longest <'a >(_x: &str , _y: &str ) -> String { String ::from ("really long string" ) }fn main () { let s = longest ("not" , "important" ); }
返回内部字符串的所有权,然后把字符串的所有权转移给调用者
结构体的生命周期 1 2 3 4 5 6 7 8 9 10 11 12 struct Book <'a > { author: &'a str , title: &'a str , }fn main () { let name = String ::from ("Jill Smith" ); let title = String ::from ("Fish Flying" ); let book = Book { author: &name, title: &title }; println! ("{} by {}" , book.title, book.author); }
结构体引用的字符串必须要比结构体活得长
安全性保证 自动化测试 1 2 3 4 5 6 7 8 #[cfg(test)] mod tests { #[test] #[should_panic] fn it_works () { assert_eq! (2 + 2 , 4 ); } }
Unsafe Rust 使用 unsafe
非常简单,只需要将对应的代码块标记下即可:
1 2 3 4 5 6 7 8 9 fn main () { let mut num = 5 ; let r1 = &num as *const i32 ; unsafe { println! ("r1 is: {}" , *r1); } }
上面代码中, r1
是一个裸指针(raw pointer),由于它具有破坏 Rust 内存安全的潜力,因此只能在 unsafe
代码块中使用,如果你去掉 unsafe {}
,编译器会立刻报错。
作用:
解引用裸指针,就如上例所示
调用一个 unsafe
或外部的函数
访问或修改一个可变的静态变量
实现一个 unsafe
特征
访问 union
中的字段
闭包与迭代 迭代
into_iter
会夺走所有权
iter
是借用
iter_mut
是可变借用
1 2 let mut my_iterable_fav_fruits = my_fav_fruits.iter (); assert_eq! (my_iterable_fav_fruits.next (), Some (&"banana" ));
1 2 let mut my_iterable_fav_fruits = my_fav_fruits.into_iter (); assert_eq! (my_iterable_fav_fruits.next (), Some ("banana" ));
可用方法 filter 1 2 3 for num in (0 ..=100 ).filter (|x| x % 3 == 0 ) { eprint!("{} " , num); }
map 1 2 3 4 let vec =vec! [1 ,2 ,3 ,4 ,5 ];for num_str in vec.iter ().map (|x|x.to_string ()){ eprint!("{}" ,num_str); }
collect 1 let vec = (0 ..=100 ).collect::<Vec <_>>();
rev 逆序遍历
1 2 3 4 for i in (0 ..=100 ).rev () { eprint!("{} " , i); }
max max是求迭代元素的最大值,比较简单不多说,给个例子。
1 2 3 let vec = vec! [1 , 5 , 3 , 4 , 2 ];let max = vec.iter ().max ().unwrap (); eprint!("{}" , max);
sum sum是求迭代元素的和,需要指定一下结果的类型。
1 2 3 let vec = vec! [1 , 2 , 3 , 4 , 5 ];let sum = vec.iter ().sum::<i32 >(); eprint!("{}" , sum);
fold 类似js的reduce
1 2 3 let vec = vec! [1 , 2 , 3 , 4 , 5 ];let res = vec.iter ().fold (0 , |acc, x| acc + x); eprint!("{}" , res);
求阶乘:
1 (1 ..num + 1 ).fold (1 , |acc, x| acc * x)
scan scan和fold很类似,但是它允许你直接修改累计值,并且允许你选择什么时候停止迭代,取决于你传入的闭包何时返回None。比如我不仅要求数组的和,还要获取每次累加的结果,就可以这么写。
1 2 3 4 5 6 7 let vec = vec! [1 , 2 , 3 , 4 , 5 ];for step in vec.iter ().scan (0 , |acc, x| { *acc+= *x; Some (*acc) }) { eprint!("{} " , step); }
智能指针 Box Rc Arc Rc单线程,Arc多线程
都是只读
Rc 1 2 3 4 5 6 7 8 use std::rc::Rc;fn main () { let a = Rc::new (String ::from ("hello, world" )); let b = Rc::clone (&a); assert_eq! (2 , Rc::strong_count (&a)); assert_eq! (Rc::strong_count (&a), Rc::strong_count (&b)) }
Rc::new(var)
:创建一个智能指针并返回
Rc::strong_count()
:查询引用计数
Rc::clone()
:克隆智能指针,并增加引用计数
Arc 1 2 3 4 5 6 7 8 9 10 11 12 use std::sync::Arc;use std::thread;fn main () { let s = Arc::new (String ::from ("多线程漫游者" )); for _ in 0 ..10 { let s = Arc::clone (&s); let handle = thread::spawn (move || { println! ("{}" , s) }); } }
Cow
Cow::Borrowed(&'a T)
: 表示一个不可变的引用,可以用于读取数据;
Cow::Owned(T)
: 表示一个可变的数据,可以用于修改数据。
Cow::from()
:根据数据类型的不同,返回一个 Cow::Borrowed 或 Cow::Owned 类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 fn main () { let name = "Tom" .to_string (); let species = "Cat" .to_string (); let animal = Animal { name: Cow::from (&name), age: 3 , species: Cow::from (species), }; println! ("Name: {}" , animal.name); println! ("Species: {}" , animal.species); }
多线程 样例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 use std::thread;use std::time::{Duration, Instant};fn main () { let handle = thread::spawn (|| { let start = Instant::now (); println! ("开始睡觉..." ); thread::sleep (Duration::from_millis (250 )); start.elapsed ().as_millis () }); println! ("我在thread::spawn下面" ); let duration = handle.join ().unwrap (); println! ("睡醒啦!!!睡了{}ms" ,duration); }
修改数据 使用Arc
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 use std::sync::{Mutex,Arc};use std::thread;use std::time::Duration;struct JobStatus { jobs_completed: u32 , }fn main () { let status = Arc::new (Mutex::new (JobStatus { jobs_completed: 0 })); let mut handles = vec! []; for _ in 0 ..10 { let status_shared = Arc::clone (&status); let handle = thread::spawn (move || { thread::sleep (Duration::from_millis (250 )); let mut num = status_shared.lock ().unwrap (); num.jobs_completed += 1 ; }); handles.push (handle); } for handle in handles { handle.join ().unwrap (); let mut num = status.lock ().unwrap (); println! ("jobs completed {}" , num.jobs_completed); } }
mpsc库 在 Rust 中,mpsc::channel
的作用是创建一个多生产者(mpsc)通道,用于线程之间发送消息。
mpsc
代表 multiple producer, single consumer(多生产者,单消费者)。
主要作用有:
在线程间建立消息通道
mpsc::channel
返回一个 transmitter 和 receiver, transmitter 可以通过通道发送消息,receiver 可以接收消息。
发送端和接收端可在不同线程
transmitter 和 receiver 可以移动到不同的线程,这样可以在线程间传递消息。
提供非阻塞的异步消息传递
如果通道已满,发送会立即返回错误,不会阻塞线程。
示例:
1 2 3 4 5 6 7 8 9 10 use std::thread;use std::sync::mpsc;let (tx, rx) = mpsc::channel (); thread::spawn (move || { tx.send (10 ).unwrap (); });let message = rx.recv ().unwrap ();
所以 mpsc 通道非常适合在 Rust 中低开销地在线程间传递消息。
宏 声明式宏 macro_rules
对于声明宏(declarative macro),必须要先定义后使用。
声明宏主要包括自定义派生(derive)和属性宏(attribute macro)。这类宏需要在编译前定义,所以必须先定义后调用。
对于过程宏(procedural macro),则可以先调用后定义。
使模块中的宏可以在当前模块可用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #[macro_use] mod macros { macro_rules! my_macro { () => { println! ("Check out my macro!" ); }; ($val:expr) => { println! ("Look at this other macro: {}" , $val); } } }fn main () { my_macro!(); }
过程宏 三种: