👴❤恰🦀
Rustlings
intro
略过 不想写
Variables
1
加个 let 就彳亍…
2
…
3
…
4
声明一个 mut 即可.
5
Shadowing , 申请一个同名的变量.
6
Constants types must also always be annotated, 常量声明时必须标注类型.
Functions
1
smjbdx…
2
函数参数必须标注类型.
3
… 没参数, 随便给一个就行…
4
sale_price
函数没有给返回值类型, 填上即可.
5
返回的语句多了一个分号, 有这个分号的话返回值类型会变成 ()
, 去掉这个分号的话就是一个表达式了.
IF
1
1 2 3 4 5 6 7 pub fn bigger (a: i32 , b: i32 ) -> i32 { if a >= b { a } else { b } }
2
同理随便改改…
3
随便改改…
quiz1
1 2 3 4 5 6 7 fn calculate_price_of_apples (num:i32 ) -> i32 { if num <= 40 { num * 2 } else { num } }
primitive_types
1 bools
写个变量 = true 即可.
2 char
1 let your_character = '👴';
👴, rust 中的 char 可以是任何一个字符, 记得没错的话是 unicode .
3 array
数组的声明方法, 和 c 的不怎么一样, 声明一个元素和元素个数.
4 slice
1 let nice_slice = &a[1 ..4 ];
如果要把索引 4 也加上就要 [1..=4]
.
5 tuple
1 2 let cat = ("Furry McFurson" , 3.5 );let (name,age) = cat;
let 可以对变量进行解构, 于是就可以对一个元组这么写.
6 tuple index
元组索引可以直接这么写啊, 彳亍.
vec
1
1 let v = vec! [10 ,20 ,30 ,40 ];
如果不这么写就要:
1 2 let v = Vec ::new (); pushpushpushpush...
第一种写法用的是 vec![]
这个宏, 这个宏可以方便的创建一个带有初始元素的 vec .
2
感觉这个有点意思(
1 let v: Vec<i32> = (1 ..).filter(|x| x % 2 == 0 ).take(5 ).collect();
这行代码创建了一个 Vec<i32>
类型的变量 v
。它使用了 Rust 的迭代器(iterator)功能。具体来说:
(1..)
创建了一个无限的整数范围,从1开始。
.filter(|x| x % 2 == 0)
过滤器用于保留能被2整除的整数,即偶数。
.take(5)
用于仅获取前5个满足条件的整数。
.collect()
将这些整数收集到一个 Vec<i32>
向量中。
主体逻辑就是造了个只有偶数的向量传进要写的函数里面, 提供两种对这个向量进行操作的方法:
1 2 3 4 5 6 7 8 9 1 .fn vec_loop (mut v: Vec <i32 >) -> Vec <i32 > { for element in v.iter_mut () { *element *= 2 ; } v }
v.iter_mut()
是针对 Vec
(向量)类型的一个方法调用,用于创建一个可变引用的迭代器 ,该迭代器允许以可变的方式遍历向量中的元素。与 v.iter()
方法不同,v.iter_mut()
返回的迭代器允许修改向量中的元素。
后续修改的时候只需要对这些可变引用解引用 + 操作即可, 这是第一种.
1 2 3 4 5 6 7 8 2 .fn vec_map (v: &Vec <i32 >) -> Vec <i32 > { v.iter ().map (|element| { element * 2 }).collect () }
v_iter
相对的就是创建一个不可变引用的迭代器 , 后面的闭包捕获了迭代器中的元素作为它的参数 (大概是这样) , 然后对每个元素进行 *2 操作, 最后使用 .collect()
方法放进一个新的向量中, 再返回.
Move_semantics
1
改个 mut .
2
感觉这个也彳亍.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 fn main () { let vec0 = vec! [22 , 44 , 66 ]; let mut vec1 = fill_vec (&vec0); assert_eq! (vec0, vec! [22 , 44 , 66 ]); assert_eq! (vec1, vec! [22 , 44 , 66 , 88 ]); }fn fill_vec (vec: &Vec <i32 >) -> Vec <i32 > { let mut vec = vec.clone (); vec.push (88 ); vec }
这里涉及到所有权的概念, 原本将 vec0 当做参数传进函数中, 但是这时候堆上数据的所有权已经给到了函数的参数而非之前的 vec0 , 以至于 vec0 不能在后面的 assert 中使用.
解决方法可以是再创建一个 vec 传进函数, 也可以像上面这样传一个引用进去, 使用 clone 方法拿到值再返回去, 这样不存在所有权的改变.
3
和第一个的区别就是参数, 其实这里在参数前面加个 mut 就可以.
4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 fn main () { let mut vec1 = fill_vec (); assert_eq! (vec1, vec! [22 , 44 , 66 , 88 ]); }fn fill_vec () -> Vec <i32 > { let mut vec = vec! [22 ,44 ,66 ]; vec.push (88 ); vec }
感觉这个东西有点多余, 但是似乎思想是 “这里可以不需要之前那种所有权的移动”.
5
1 2 3 4 5 6 7 8 fn main () { let mut x = 100 ; let y = &mut x; *y += 100 ; let z = &mut x; *z += 1000 ; assert_eq! (x, 1200 ); }
同一作用域中, 一个变量的可变引用只能有一个, 并且这个引用在最后一次使用后被销毁.
所以把声明 z 的位置放在 y 最后一次使用之后即可.
6
先记录一下函数:
1 2 3 fn get_char (&data: String ) -> char { data.chars ().last ().unwrap () }
data.chars()
这部分调用了 String
类型的 chars()
方法,该方法返回一个迭代器,用于遍历字符串 data
中的每个字符。
.last()
last()
方法用于获取迭代器中的最后一个元素,即最后一个字符。由于 chars()
返回的是一个字符迭代器,因此 .last()
方法返回的是一个 Option<char>
类型的值,表示可能有字符或可能为空。
.unwrap()
unwrap()
方法用于从 Option
类型的值中获取其内部的值。在这里,因为我们知道字符串不会为空(否则会出现 panic),所以可以安全地调用 unwrap()
方法来获取 Option<char>
类型的字符值。
to_uppercase()
变大写.
本体没那么难, 加加减减几个 &
的事.
Struct
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 struct ColorClassicStruct { red :u8 , green :u8 , blue :u8 , }struct ColorTupleStruct (u8 ,u8 ,u8 );#[derive(Debug)] struct UnitLikeStruct ;#[cfg(test)] mod tests { use super::*; #[test] fn classic_c_structs () { let green = ColorClassicStruct { red: 0 , green: 255 , blue: 0 , }; assert_eq! (green.red, 0 ); assert_eq! (green.green, 255 ); assert_eq! (green.blue, 0 ); } #[test] fn tuple_structs () { let green = ColorTupleStruct (0 ,255 ,0 ); assert_eq! (green.0 , 0 ); assert_eq! (green.1 , 255 ); assert_eq! (green.2 , 0 ); } #[test] fn unit_structs () { let unit_like_struct = UnitLikeStruct; let message = format! ("{:?}s are fun!" , unit_like_struct); assert_eq! (message, "UnitLikeStructs are fun!" ); } }
一些基础的结构体的定义方法.
2
照着 assert 写就彳亍, 其中 String::from()
是对切片/ &str
类型的转换.
1 2 3 4 5 6 7 8 9 let your_order = Order { name: String ::from ("Hacker in Rust" ), year: 2019 , made_by_phone: false , made_by_mobile: false , made_by_email: true , item_number: 123 , count: 1 , };
3
1 2 3 4 5 6 7 8 9 10 11 12 13 fn is_international (&self ) -> bool { if self .sender_country == self .recipient_country { false } else { true } } fn get_fees (&self , cents_per_gram: u32 ) -> u32 { cents_per_gram * self .weight_in_grams } }
在 Rust 中,self
是一个特殊的关键字,用于表示结构体或者枚举类型的实例。在结构体或者枚举的方法中,self
用来引用当前实例。方法定义中的 &self
表示一个不可变的引用,而 &mut self
则表示一个可变的引用。这种引用的机制允许在方法中访问当前实例的字段或者调用其他方法。
结构体 + 方法的使用.
Enums
1
把几个变体写上去就行.
2
1 2 3 4 5 6 enum Message { Move {x:u32 ,y:u32 }, Echo (String ), ChangeColor (i32 ,i32 ,i32 ), Quit, }
写上变体规定点类型就行.
3
可能用的没那么明白, 但是道理就是写一个 match 去匹配 enums 即可.
1 2 3 4 5 6 7 8 fn process (&mut self , message: Message) { match message { Message::ChangeColor (c) => self .change_color (c), Message::Echo (c) => self .echo (c), Message::Move (c) => self .move_position (c), Message::Quit => self .quit (), } }
#[cfg(test)]
是 Rust 中的条件编译属性(Conditional Compilation Attribute)。它告诉 Rust 编译器在特定条件下是否编译代码块。
在这个例子中,#[cfg(test)]
用于标记测试代码。通常情况下,测试代码不会在发布版本中使用,而是在开发过程中用于测试和调试。因此,通过 #[cfg(test)]
,可以告诉编译器只在运行测试时编译这部分代码,而在发布时忽略它们。
Strings
1
函数返回值是个字面量, 但是返回值是 String
.
2
传个引用就行.
3
字符串有关操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 fn trim_me (input: &str ) -> String { String ::from (input.trim ()) }fn compose_me (input: &str ) -> String { input.to_string () + " world!" }fn replace_me (input: &str ) -> String { input.to_string ().replace ("cars" ,"balloons" ) }
去除空格的 trim , 转换和字符串连接, 替换 replace .
4
区分 &str
和 String
型.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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 ()); }
笔者遇到的坑是 format!
宏和 to_lowercase()
方法, 这两个方法的返回值都是 String
类型.
Module
1
可见性问题, 加一个 pub
即可在 mod 外的作用域访问.
2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 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" ; } }fn main () { println! ( "favorite snacks: {} and {}" , delicious_snacks::fruit, delicious_snacks::veggie ); }
根据 main 里面的使用来修改 mod 的内容即可, use 要加可见性限制.
3
1 2 3 4 5 6 7 8 use std::time::{SystemTime,UNIX_EPOCH};fn main () { match SystemTime::now ().duration_since (UNIX_EPOCH) { Ok (n) => println! ("1970-01-01 00:00:00 UTC was {} seconds ago!" , n.as_secs ()), Err (_) => panic! ("SystemTime before UNIX EPOCH!" ), } }
导入标准库的包和成员, 整体是个计算时间的函数.
Hashmap
1
介绍 hashmap , 每个 hashmap 包括了 key 和 value 两部分.
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 fruit_basket () -> HashMap<String , u32 > { let mut basket = HashMap::new (); basket.insert (String ::from ("banana" ), 2 ); basket.insert (String ::from ("otto" ), 6 ); basket.insert (String ::from ("dog" ), 6 ); basket }#[cfg(test)] mod tests { use super::*; #[test] fn at_least_three_types_of_fruits () { let basket = fruit_basket (); assert! (basket.len () >= 3 ); } #[test] fn at_least_five_fruits () { let basket = fruit_basket (); assert! (basket.values ().sum::<u32 >() >= 5 ); } }
hashmap 的长度是由对数决定的, 有多少个键值对就有多长.
basket.values().sum::<u32>()
是一个快捷计算 hashmap 中的 value 和的方法.
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 #[derive(Hash, PartialEq, Eq)] enum Fruit { Apple, Banana, Mango, Lychee, Pineapple, }fn fruit_basket (basket: &mut HashMap<Fruit, u32 >) { let fruit_kinds = vec! [ Fruit::Apple, Fruit::Banana, Fruit::Mango, Fruit::Lychee, Fruit::Pineapple, ]; for fruit in fruit_kinds { basket.entry (fruit).or_insert (1 ); } }#[cfg(test)] mod tests { use super::*; fn get_fruit_basket () -> HashMap<Fruit, u32 > { let mut basket = HashMap::<Fruit, u32 >::new (); basket.insert (Fruit::Apple, 4 ); basket.insert (Fruit::Mango, 2 ); basket.insert (Fruit::Lychee, 5 ); basket } #[test] fn test_given_fruits_are_not_modified () { let mut basket = get_fruit_basket (); fruit_basket (&mut basket); assert_eq! (*basket.get (&Fruit::Apple).unwrap (), 4 ); assert_eq! (*basket.get (&Fruit::Mango).unwrap (), 2 ); assert_eq! (*basket.get (&Fruit::Lychee).unwrap (), 5 ); } #[test] fn at_least_five_types_of_fruits () { let mut basket = get_fruit_basket (); fruit_basket (&mut basket); let count_fruit_kinds = basket.len (); assert! (count_fruit_kinds >= 5 ); } #[test] fn greater_than_eleven_fruits () { let mut basket = get_fruit_basket (); fruit_basket (&mut basket); let count = basket.values ().sum::<u32 >(); assert! (count > 11 ); } #[test] fn all_fruit_types_in_basket () { let mut basket = get_fruit_basket (); fruit_basket (&mut basket); for amount in basket.values () { assert_ne! (amount, &0 ); } } }
题中主要保证键值对数量 > 5 , 并且枚举的每一种都要用到.
所以这里的关键是:
1 2 3 for fruit in fruit_kinds { basket.entry (fruit).or_insert (1 ); }
查询这个 hashmap 中有没有这个东西, 没有就插入一个, value = 1