首页
关于
Search
1
2023 年保研经验贴
123 阅读
2
Linux Kernel-THP阅读分析[Hang up]
41 阅读
3
CSAPP 1. 计算机系统漫游
40 阅读
4
Rust入门笔记 2.函数、所有权与复合类型
40 阅读
5
Rust入门笔记 3.切片、向量与字符串
38 阅读
内核
源码分析
Rust
语法
读书笔记
深入理解计算机系统
技术之外
登录
Search
yyi
累计撰写
19
篇文章
累计收到
0
条评论
首页
栏目
内核
源码分析
Rust
语法
读书笔记
深入理解计算机系统
技术之外
页面
关于
搜索到
5
篇与
的结果
2023-09-06
Rust入门笔记 5.方法和关联函数[WIP]
使用 impl "struct_name" 的方式声明关联函数struct Point { x: f64, y: f64, } impl Point { fn origin() -> Point { Point { x: 0.0, y:0.0 } } fn new(x: f64, y:f64) -> Point { Point {x:x, y:y} } }在第一个参数处声明一个Self类型的变量,以为结构创建方法self <=> self: Self &self <=> self: &Self &mut self <=> self: &mut Self // self 会拿走当前结构体实例(调用对象)的所有权,而 &self 却只会借用一个不可变引用,&mut self 会借用一个可变引用 struct Rectangle { p1: Point, p2: Point, } impl Rectangle { // 这是一个方法 // `&self` 是 `self: &Self` 的语法糖 // `Self` 是当前调用对象的类型,对于本例来说 `Self` = `Rectangle` fn perimeter(&self) -> f64 { let Point { x: x1, y: y1 } = self.p1; let Point { x: x2, y: y2 } = self.p2; 2.0 * ((x1 - x2).abs() + (y1 - y2).abs()) } }以self声明的方法会拿走调用者的所有权关联函数使用 :: 操作符来调用fn main() { let rectangle = Rectangle { // 关联函数的调用不是通过点操作符,而是使用 `::` p1: Point::origin(), p2: Point::new(3.0, 4.0), }; }方法使用 . 操作符调用println!("Rectangle perimeter: {}", rectangle.perimeter());struct Rectangle { width: u32, height: u32, } impl Rectangle { // 完成 area 方法,返回矩形 Rectangle 的面积 // fn area fn area(&self) -> u32 { self.width * self.height } } fn main() { let rect1 = Rectangle { width: 30, height: 50 }; assert_eq!(rect1.area(), 1500); }// 只填空,不要删除任何代码行! #[derive(Debug)] struct TrafficLight { color: String, } impl TrafficLight { pub fn show_state(&self) { // 如果这里使用 self 而不是 &self,就会拿走所有权 println!("the current state is {}", self.color); } } fn main() { let light = TrafficLight{ color: "red".to_owned(), }; // 不要拿走 `light` 的所有权 light.show_state(); // 否则下面代码会报错 println!("{:?}", light); }
2023年09月06日
12 阅读
0 评论
0 点赞
2023-08-13
Rust入门笔记 4.流程控制
Rust 入门笔记 —— 流程控制1. 分支rust 的 if/else 和 go 的类似即使是单句也不能缺少大括号条件可以不用括号包裹// Fill in the blanks fn main() { let n = 5; if n < 0 { println!("{} is negative", n); } else if n > 0 { println!("{} is positive", n); } else { println!("{} is zero", n); } } rust 的 if/else 结构可以用来赋值// Fix the errors fn main() { let n = 5; let big_n = if n < 10 && n > -10 { println!(", and is a small number, increase ten-fold"); 10 * n } else { println!(", and is a big number, halve the number"); n / 2 }; println!("{} -> {}", n, big_n); } 2 循环2.1 for这个 for 和 py 的 for 比较类似,是一个 foreach 型的fn main() { for n in 1..100 { // modify this line to make the code work if n == 100 { panic!("NEVER LET THIS RUN") } } println!("Success!"); } 当遍历数组的时候,循环变量会获得被遍历的所有权(但是基本类型还是复制的) let names = [String::from("liming"),String::from("hanmeimei")]; for name in names { // Do something with name... } println!("{:?}", names); // wrong! borrow of moved value: `names`所以我们可以用切片来遍历引用 let names = [String::from("liming"),String::from("hanmeimei")]; for name in &names { // Do something with name... } println!("{:?}", names);通过数组的iter.enumrate方法,可以获得(index, value)的 tuplefn main() { let a = [4, 3, 2, 1]; // Iterate the indexing and value in 'a' for (i,v) in a.iter().enumerate() { println!("The {}th element is {}",i+1,v); } }2.2 whilewhile 循环和 C 的 while 是一样的,不过条件还是可以不加括号// Fill in the blanks to make the last println! work ! fn main() { // A counter variable let mut n = 1; // Loop while the condition is true while n < 10 { if n % 15 == 0 { println!("fizzbuzz"); } else if n % 3 == 0 { println!("fizz"); } else if n % 5 == 0 { println!("buzz"); } else { println!("{}", n); } n += 1; } println!("n reached {}, so loop is over",n); } Rust也有 continue 和 break 关键字Rust 使用 loop 代替 while true// Fill in the blanks fn main() { let mut count = 0u32; println!("Let's count until infinity!"); // Infinite loop loop { count += 1; if count == 3 { println!("three"); // Skip the rest of this iteration continue; } println!("{}", count); if count == 5 { println!("OK, that's enough"); break; } } assert_eq!(count, 5); println!("Success!"); }loop是一个表达式,所以 break 可以用来返回值// Fill in the blank fn main() { let mut counter = 0; let result = loop { counter += 1; if counter == 10 { break counter * 2; } }; assert_eq!(result, 20); println!("Success!"); } 通过 label 的方式,可以精确地 break 或 continue 嵌套循环// Fill in the blank fn main() { let mut count = 0; 'outer: loop { 'inner1: loop { if count >= 20 { // This would break only the inner1 loop break 'inner1; // `break` is also works. } count += 2; } count += 5; 'inner2: loop { if count >= 30 { // This breaks the outer loop break 'outer; } // This will continue the outer loop continue 'outer; } } assert!(count == 30); println!("Success!"); } 3 模式匹配3.1 matchmatch有点类似于 C 的 switch,其基本形式如下match target { pattern1 => experssion1, pattern2 => { sth; expression }, _ => expression }match需要穷举出所有的可能,用_代表所有未列出的可能(类似于 default)match 的分支必须是表达式,且分支表达式的类型必须相同pattern 可以用 X|Y的形式表示,代表 X 或 Y// 填空 enum Direction { East, West, North, South, } fn main() { let dire = Direction::South; match dire { Direction::East => println!("East"), Direction::South|Direction::North => { // 在这里匹配 South 或 North println!("South or North"); }, _ => println!("West"), }; }match 本身也是一个表达式,因此可以用来赋值fn main() { let boolean = true; // 使用 match 表达式填空,并满足以下条件 // // boolean = true => binary = 1 // boolean = false => binary = 0 let binary = match boolean { true => 1, false => 0, }; assert_eq!(binary, 1); }match可以取出模式中绑定的值,还记得我们的 enum 类型么// 填空 enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(i32, i32, i32), } fn main() { let msgs = [ Message::Quit, Message::Move{x:1, y:3}, Message::ChangeColor(255,255,0) ]; for msg in msgs { show_message(msg) } } fn show_message(msg: Message) { match msg { Message::Move{x: a, y: b} => { // 这里匹配 Message::Move assert_eq!(a, 1); assert_eq!(b, 3); }, Message::ChangeColor(_, g, b) => { assert_eq!(g, 255); assert_eq!(b, 0); } __ => println!("no data in these variants") } } 3.2 if letif let 对应于只有一个模式需要处理的情况,这时候用 match 需要写一个无用的 default下面两份代码等价fn main() { let o = Some(7); // 移除整个 `match` 语句块,使用 `if let` 替代 match o { Some(i) => { println!("This is a really long string and `{:?}`", i); } _ => {} }; } fn main() { let o = Some(7); if let Some(i) = o { println!("This is a really long string and `{:?}`", i); } }3.3 matches!Rust提供了一个宏 matches,把表达式和模式进行匹配,返回是否匹配let foo = 'f'; println!("{}", matches!(foo, 'A'..='Z' | 'a' ..= 'z')) // trueenum MyEnum { Foo, Bar } fn main() { let mut count = 0; let v = vec![MyEnum::Foo,MyEnum::Bar,MyEnum::Foo]; for e in v { // if e == MyEnum::Foo { // 修复错误,只能修改本行代码 if matches!(e, MyEnum::Foo) { count += 1; } } assert_eq!(count, 2); }
2023年08月13日
26 阅读
0 评论
0 点赞
2023-08-11
Rust入门笔记 3.切片、向量与字符串
Rust入门笔记——切片、向量与字符串我在第一节中写了关于元组和数组的笔记,这章来扩充一些其他的复合类型1 切片还记得刚学 Go 的时候,我对切片和数组感到非常迷惑。切片和数组类似,但它本质上是对数据值的部分引用。切片的长度在编译时未知。切片是一个两个字的对象,第一个字是指向数据的指针,第二个是切片的长度,可用于借用数组的一部分。// 修复代码中的错误,不要新增代码行! fn main() { let arr = [1, 2, 3]; let s1: [i32] = arr[0..2]; let s2: str = "hello, world" as str; } // Sol : fn main() { let arr = [1, 2, 3]; let s1: &[i32] = &arr[0..2]; let s2: &str = "hello, world" as &str; }原来的[i32]和str都是切片类型,复习一下:切片是对数据值的引用。因此,切片不拥有数据的所有权,所以在尝试把切片赋值给变量的时候,变量的类型必须是对切片的引用。fn main() { let arr: [char; 3] = ['中', '国', '人']; let slice = &arr[..2]; // 修改数字 `8` 让代码工作 // 小提示: 切片和数组不一样,它是引用。如果是数组的话,那下面的 `assert!` 将会通过: '中'和'国'是char类型,char类型是Unicode编码,大小固定为4字节,两个字符为8字节。 assert!(std::mem::size_of_val(&slice) == 16); // orig : 8 }64 位系统下,一个字是 8bytes,所以一个切片是 16.fn main() { let arr: [i32; 5] = [1, 2, 3, 4, 5]; // 填空让代码工作起来 // let slice: __ = __; let slice: &[i32] = &arr[1..4]; assert_eq!(slice, &[2, 3, 4]); }2 字符串和字符串切片字符串字面量的类型是&str,标准库中的 String也是 Rust 的字符串类型str 就是字符串切片,我们前面声明字符串都是用String::from而不是直接写字符串,就是因为 let s = "hello" 中这个 s 会成为一个 &str 类型的变量。String 使用 UTF-8 编码,底层存储是 Vec<u8> 本节后面会提到 Vec。// 基础类型转换为字符串 let one = 1.to_string(); let slice = "slice".to_string(); let slice1 = String::from("this is &str") // 字符串追加 let mut s1 = String::from("base"); s1.push_str("str"); s1.push('c'); // 用 + 拼接 let s1 = String::from("Hello, "); let s2 = String::from("world!"); let s3 = s1 + "-" + &s2; // 字符串长度 let len = s1.len(); // 字符串截取 let sub = &s1[..2]; // 字符串替换 let s = String::from("Hello, dogs"); let s1 = s.replace("dogs", "world");// 修复所有错误,并且不要新增代码行 fn main() { let s = String::from("hello"); s.push(','); s.push(" world"); s += "!".to_string(); println!("{}", s) } // Sol fn main() { let mut s = String::from("hello"); s.push(','); s.push_str(" world"); s += &"!".to_string(); println!("{}", s) }// String只能和 &str进行拼接,并且会移动 String 的所有权。 fn main() { let s1 = String::from("hello,"); let s2 = String::from("world!"); // let s3 = s1 + s2; let s3 = s1.clone() + &s2; assert_eq!(s3,"hello,world!"); println!("{}",s1); }/* 填空并修复所有错误 */ fn main() { // 和其他语言一样,使用 \ 来转义 let raw_str = "Escapes don't work here: \x3F \u{211D}"; assert_eq!(raw_str, "Escapes don't work here: ? ℝ"); // 使用成对的 #" 、"# 来标定字符串,这样字符串中间就可以有"了 let quotes = r#"And then I said: "There is no escape!""#; println!("{}", quotes); // 如果希望在字符串中使用 # 号,可以如下使用: let delimiter = r###"A string with "# in it. And even "##!"###; println!("{}", delimiter); // 填空 let long_delimiter = r###"Hello, "##""###; assert_eq!(long_delimiter, "Hello, \"##\"") }str、&str 和 String 都是 UTF-8 字符串,可以用 u8 数组实现字节数组使用切片而不是索引访问字符串中的某个字符fn main() { let s1 = String::from("hi,中国"); // let h = s1[0]; let h = &s1[..1]; assert_eq!(h, "h"); // let h1 = &s1[3..5]; let h1 = &s1[3..6]; assert_eq!(h1, "中"); }3 向量向量是线性表,用Vec<T>表示,其中 T 是向量中存放的数据类型使用 push 方法追加单个元素使用 append 拼接。let vec: Vec<i32> = Vec::new(); let mut vec1 = vec![1, 2, 4, 8]; let vec2 = vec![32, 64]; vec1.push(16); vec1.append(&mut vec2); println!("{:?}", vec1); // 1, 2, 4, 8, 16, 32, 64使用 get 方法取出向量中的值let v = vec![1, 2, 4, 8]; v.get(0); // 这是一个 Option,后面流程控制会讲到当然也可以直接用索引let tmp1 = v[2]; let tmp2 = v[4]; // panic : index out of bounds
2023年08月11日
38 阅读
0 评论
0 点赞
2023-08-11
Rust入门笔记 2.函数、所有权与复合类型
Rust入门笔记 —— 函数,所有权与复合类型1 结构体与枚举1.1 结构体先讲结构体是因为后面方法需要结构体做支撑,其实这种用例子解释语法的方式,还是需要学过其他语言。因为前期肯定会用到后面才讲的东西结构体主要有三种类型tuple : struct Pair(i32, f32)经典的 C struct,当然,结构体也是可以复合的struct Point { x: f32, y: f32 }单元(Unit)结构体:没有字段结构体和 C 一样,用.+field_name引用变量let point: Point = Point{ x: 10.3, y: 0.4 }; // 这里的 x 和 y 并不能省略 println!("coord : {}, {}", point.x, point.y); let point_ext: Point = Point{ x: 1.2, ..point}; // 使用这种方法继承字段 let pair = Pair(1, 0.1) // 元组类型的就可以省略啦(不省略也没名字)1.2 枚举用 enum 关键字声明一个枚举,相比与 C,rust 的枚举有更多功能,任何对于结构体合法的内容也可以用于 enumenum WebEvent { PageLoad, PageUnload, // Unit KeyPress(char), Paste(String), // Tuple Click { x: i64, y: i64}, // 标准的结构 } // 使用这种 match 结构就可以引用枚举字段及其内容 fn inspect(event: WebEvent) { match event { WebEvent::PageLoad => println!("page loaded"), WebEvent::PageUnload => println!("page unloaded"), // Destructure `c` from inside the `enum` variant. WebEvent::KeyPress(c) => println!("pressed '{}'.", c), WebEvent::Paste(s) => println!("pasted \"{}\".", s), // Destructure `Click` into `x` and `y`. WebEvent::Click { x, y } => { println!("clicked at x={}, y={}.", x, y); }, } }2 函数2.1 函数与返回值Rust的函数以 fn fn_name(arg_list) -> ret_type {body} 定义,函数的最后一行是返回值Rust函数名称风格是snake_case(区别于Go的CamelCase)Rust不要求被调用的函数必须在调用者之前定义,只要有定义即可Rust的函数返回值可以是函数体尾部的表达式,也可以显式的使用return语句返回()类型的函数,返回值类型声明可以省略fn main() { // 不要修改下面两行代码! let (x, y) = (1, 2); let s = sum(x, y); assert_eq!(s, 3); } fn sum(x : i32, y:i32) -> i32 { // original : (x, y:i32) { return x + y; // original : x + y; } 上述答案中第10行可以是 x + y,可以是return x + y;,但是不能是 x + y; 因为分号会让表达式变为语句,语句的值是()// 用两种方法求解 fn main() { never_return(); } fn never_return() -> ! { // 实现这个函数,不要修改函数签名! }这题没有想到答案,两种方法分别可以是fn never_return() -> ! { panic!("I return nothing!") } fn never_return() -> ! { loop{} }其实感觉有点扯淡还有类似于成员函数的概念:把函数和类型加以关联,Rust 中用关联函数和方法实现,我门后面再讲2.2 函数体表达式我斗胆猜测这就是 Rust 中匿名函数的表现,Rust 使用大括号来编写一个较为复杂的表达式let y = { let x = 3; x + 1 }; // y == 4可以看到,大括号中的内容和函数中的内容一致,但是函数体表达式不能使用 return3 所有权、引用和借用C 使用手动管理内存,开发者申请释放,这常常造成资源浪费和内存泄漏。Java 的 JVM 会负责在运行时回收内存,然而 GC 会导致运行效率的下降。Rust 采用所有权机制,让它在编译阶段能更有效的分析内存资源。Rust 中的每个值都有一个变量,称为其所有者一个值同时只能有一个所有者当所有者不在程序运行范围内时,该值会被删除3.1 变量范围{ // 无效,未声明 let s = "str"; // 可用 } // 无效上面这段代码描述了 s 这个变量的作用范围,它代表变量的可行域,默认从声明变量开始有效,直到变量域结束。变量和数据的交互方式主要有移动 Move 和克隆 Clone3.2 所有权移动与克隆移动,即直接复制,对于基本数据类型(bool,整形、浮点等)Rust 会在栈上复制一份。let x = 5; let y = x;在这样的操作后,栈上会有两个 5但是如果数据在堆上,只会复制指针,类似于浅拷贝。let s1 = String::from("str"); let s2 = s1; println!("{}", s1); // Wrong! borrow of moved value: `s1`当 s2 被绑定到堆上的 "str" 时,s1 就已经失效。为了降低程序运行成本,默认的情况下非基本类型采用移动,但是如果需要复制一份数据,需要使用克隆let s1 = String::from("str"); let s2 = s1.clone(); println!("{}, {}", s1, s2);此时堆中会有两个 str,分别绑定给 s1 和 s2,释放的时候也会分别释放。对于练习:fn main() { // 使用尽可能多的方法来通过编译 let x = String::from("hello, world"); let y = x; println!("{},{}",x,y); } // 显然,编译会因为 x 失效而失败 // Sol1 : let y = x.clone(); // Sol2 : let x = "hello, world"; // 在栈上,会移动所有权转移的时候,可变性也会跟着改变fn main() { let s = String::from("hello"); let mut s1 = s; s1.push_str(" world"); println!("{}", s1); }涉及函数的情况调用函数传入的参数,其所有权会被传递给 callee,因此会失效。但是基本类型不会失效,道理和上面一样。换句话说,传参和移动的效果是一样的。// 不要修改 main 中的代码 fn main() { let s1 = String::from("hello, world"); let s2 = take_ownership(s1); // s1 在此处失效 println!("{}", s2); } // 只能修改下面的代码! fn take_ownership(s: String) { println!("{}", s); } // Sol1 fn take_ownership(s: String) -> String { println!("{}", s); s } 对于返回值,其所有权会被移动出函数,返回到调用函数的地方,不会被释放fn main() { let s = give_ownership(); println!("{}", s); } // 只能修改下面的代码! fn give_ownership() -> String { let s = String::from("hello, world"); // 将 String 转换成 Vec 类型 let _s = s.into_bytes(); s } // sol1 : let _s = s.clone().into_bytes(); // sol2 : let _s = s.as_bytes();上面这道题中,s 的所有权在into_bytes方法中被移动,因此不能在返回处使用。但是修改之后,s 的所有权被移交回 main,所以 main 中可以使用。引用和租借引用类似于取指针,和 C++ 里面的引用感觉差不多fn main() { let s = String::from("hello, world"); print_str(&s); // print_str(s); println!("{}", s); } fn print_str(s: &String) { // fn print_str(s: String) { println!("{}",s); }在变量的值被引用的时候,变量不会被认定为无效。引用不会获得值的所有权,只会 租借 值的所有权,未显式声明可变租借的所有权不可以修改数据引用本身也是一个类型,它存的值其实就是被引用的值的地址。fn main() { let s1 = String::from("hello"); let s2 = &s1; let s3 = s1; println!("{}", s2); } // 错误,s2 只租借了 s1 的所有权,当 s1 的所有权失效,s2 租借的也失效了fn main() { let s1 = String::from("run"); let s2 = &s1; println!("{}", s2); s2.push_str("oob"); // 错误,禁止修改租借的值 println!("{}", s2); }fn main() { let mut s1 = String::from("run"); // s1 是可变的 let s2 = &mut s1; // s2 是可变的引用 s2.push_str("oob"); println!("{}", s2); }可变引用不允许多重引用,但是不可变引用可以。
2023年08月11日
40 阅读
0 评论
0 点赞
2023-08-04
Rust入门笔记 1.概述、变量与基本类型
Rust入门笔记 —— 概述,变量与基本类型在我学习的过程中,一直坚持着实践掌握的更快的方法论。本笔记是我使用Rust By Example和practice.rs学习Rust过程的记录,可能更多偏向于后者,因为他是中文的... 。我打算使用的路线是通过这两个Wiki快速掌握Rust的语法,然后阅读一些优秀的项目来让自己的代码变的优雅。最后开发几个中型项目来巩固,可能会包括Redis(我一直想通过开发的方式让我深入了解Redis)和一个小内核(如果时间允许),或者一个小编译器(如果时间不允许)。我的最终目的是可以让Rust成为我在学习计算机系统结构中的主要开发语言。我使用 Vscode + Docker作为我的学习环境,我对C/C++有比较高的熟练度,Python和Go也是我的主力语言。对其他语言也有一定了解,能勉强使用(如Swift、Java和Js)。我不认为Rust适合作为第一门语言学习。希望这能让你了解到我的大概水平,以便更好的让这篇笔记帮助到你。0 环境配置我是Windows下的,直接用Docker了docker run -it --name rust-dev --privileged=true \ -p 2222:22 -v /f/Dev_rs:/rs/work rust bash注意-v是挂载的目录,要符合你的实际情况-p是把docker的22端口映射到本机的2222端口上,方便使用CLion的ssh,入门期间没有太大的项目,以Vscode为主。1 变量、变量绑定Rust是一类静态语言,变量需要在初始化后使用通常,我们使用 let var_name[: var_type] = [value]; 的格式对变量进行声明。let有类似auto的功效,如果有value,可以不声明type,rust会自动进行类型推断。对于这种声明,我们称为把value绑定到变量1.1 变量与常量rust 包括变量和常量,其中变量还会分为可变变量和不可变变量,最显著的区别如下:let a = 123; // 被自动推断为i32, 如果需要,显式声明为i64 a = 456; // Error : cannot assign twice to **immutable** variable `a` let a = 456; // Right : 重新绑定了 // ---------------- const a: i32 = 123; // const 需要声明类型 let a = 456; // missing patterns are not covered because `a` is interpreted as a constant pattern, not a new variable1.2 可变变量注意到第三行报错中的immutable了么,我们需要显式的为一个变量声明为可变,才能在不重新绑定的前提下对其进行修改、运算等操作let mut a = 1; a += 2; println!("a = {}", a); // a = 3这里出现了一个 'println!',我们暂时不探究宏是什么以及他是怎么工作的,我们只需要知道它可以格式化的输出内容,形式上类似于Python的格式化字符串,即使你不会,你也能猜到它都输出了些什么,在上面的例子中,编译器把第二个参数a填入到了格式串的{}中,所以输出了 "a = 3" 的结果。1.3 未使用、未生明、作用域对于未使用的变量,rust编译器会报错。我们可以在变量前加'_',相当于显式的声明他是一个可能不会被用到的变量。fn main() { let a = 10; } // warning: unused variable: `a`rust编译器禁止使用未初始化的变量,会在编译期报错。对于变量作用域,和其他语言差不多,不做赘述。rust不允许全局的普通变量,必须是const或者static量1.4 变量遮蔽(Shadowing)变量遮蔽我理解类似于重载,在C中也很常见,只不过我们一般不这么用。后声明的变量只会在其作用域中有效,出了自己的作用域后就归还到原来的变量。fn main() { let x: i32 = 5; { assert_eq!(x, 5); let x = 12; assert_eq!(x, 12); } assert_eq!(x, 5); let x = 42; println!("{}", x); // 输出 "42". } 1.5 变量解构fn main() { let (mut x, y) = (1, 2); x += 2; assert_eq!(x, 3); assert_eq!(y, 2); }对于Python,我们经常使用对等的独立变量去接一个Tuple的拆分(典型的是函数返回值),Rust也可以这样在Rust 1.59+中,解构式还可以进行赋值。更多在后面 tuple 提到fn main() { let (mut x, mut y); (x, y) = (3, 4); [x, y] = [1, 2]; // 填空,让代码工作 assert_eq!([x,y], [1, 2]); } 2 基本类型2.1 数值类型对于整形,包括(8、16、32、64、128) x (i/u) 的笛卡尔积种,字面意思很好理解这个让我们填as后面的,了解到通过as进行字面量的强转fn main() { let v: u16 = 38_u8 as u16; }可以看到默认的字面整形量推断出来的变量类型是i32// 修改 `assert_eq!` 让代码工作 fn main() { let x = 5; assert_eq!("i32".to_string(), type_of(&x)); } // 以下函数可以获取传入参数的类型,并返回类型的字符串形式,例如 "i8", "u8", "i32", "u32" fn type_of<T>(_: &T) -> String { format!("{}", std::any::type_name::<T>()) } // 填空,让代码工作 fn main() { assert_eq!(i8::MAX, 127); assert_eq!(u8::MAX, 255); }对于字面量,用 [0-9_] 表示十进制数,0x[0-9A-F]表示十六进制数、0o、0b表示八进制、二进制数。对于浮点数,f32、f64分别代表32和64位浮点数,字面量默认为64位浮点数对于布尔,bool只能为true或者false, 不能用0代表false、非0代表true对于字符,用char表示,char大小为4个字节,是Unicode标量这个例子让我们改assert的char大小,因为是四字节,直接改为4// 修改2处 `assert_eq!` 让代码工作 use std::mem::size_of_val; fn main() { let c1 = 'a'; assert_eq!(size_of_val(&c1),4); // original : 1 let c2 = '中'; assert_eq!(size_of_val(&c2),4); // original : 3 println!("Success!") } 2.2 tuple & list用 () 表示 tuple 元组,典型的例子如下:let tup: (i32, f64, u8) = (500, 6.4, 1); // tup.0 等于 500 // tup.1 等于 6.4 // tup.2 等于 1 let (x, y, z) = tup; // y 等于 6.4imp函数返回的是一个空tuple (虽然暂时我还不知道为什么是这样),但是为了让它们相等,我们给v一个空tuple即可// 让代码工作,但不要修改 `implicitly_ret_unit` ! fn main() { let _v: () = (); let v = (); // Orig : (2, 3) assert_eq!(v, implicitly_ret_unit()); println!("Success!") } fn implicitly_ret_unit() { println!("I will return a ()") } // 不要使用下面的函数,它只用于演示! fn explicitly_ret_unit() -> () { println!("I will return a ()") }这个例子说明空tuple的占用空间为0,而非cpp中空类的1// 让代码工作:修改 `assert!` 中的 `4` use std::mem::size_of_val; fn main() { let unit: () = (); assert!(size_of_val(&unit) == 0); println!("Success!") }当然,tuple 也可以复合let tuple_of_tuples = ((1u8, 2u16, 2u32), (4u64, -1i8), -2i16);tuple可以直接通过下表解构、可以直接被 println 打印println!("{:?}", tuple_of_tuples.0); // (1, 2, 2)和 python 一样,需要一个 ', '区别括起来的数和 tupleprintln!("{:?} {:?}", (1), (1, )); // 1 (1,)用 [] 表示数组,包括同类型数据let a = [1, 2, 3, 4, 5]; // a 是一个长度为 5 的整型数组 let b = ["January", "February", "March"]; // b 是一个长度为 3 的字符串数组 let c: [i32; 5] = [1, 2, 3, 4, 5]; // c 是一个长度为 5 的 i32 数组 let d = [3; 5]; // 等同于 let d = [3, 3, 3, 3, 3]; let first = a[0]; let second = a[1]; // 数组访问 let first = a.get(0); // 会返回一个 Option,提供给我们 match 用,后面再说2.3 Rangerange的表达方式很简单,就是 a..b表示a到b左闭右开, 比如 -3..2表示 [-3, 2),a..=b表示闭区间两个目标: 1. 修改 assert! 让它工作 2. 让 println! 输出: 97 - 122fn main() { let mut sum = 0; for i in -3..2 { sum += i } assert!(sum == -5); for c in 'a'..='z' { println!("{}",c as u8); // original : no as u8 } }// 填空 use std::ops::{Range, RangeInclusive}; fn main() { assert_eq!((1..5), Range{ start: 1, end: 5 }); assert_eq!((1..=5), RangeInclusive::new(1, 5)); }
2023年08月04日
21 阅读
0 评论
0 点赞