首页
关于
Search
1
2023 年保研经验贴 [计算机-末九Rank50%-入北计清深-北计直博]
1,117 阅读
2
FreeBSD Kernel-编译环境搭建
425 阅读
3
Linux Kernel-THP阅读分析[Hang up]
410 阅读
4
Linux Kernel-编译调试环境搭建
327 阅读
5
Linux Kernel-源码阅读环境搭建
315 阅读
内核
源码分析
阅读笔记
Rust
语法
习题
读书笔记
深入理解计算机系统
论文
技术之外
开发
rcore-arm
登录
Search
yyi
累计撰写
49
篇文章
累计收到
2
条评论
首页
栏目
内核
源码分析
阅读笔记
Rust
语法
习题
读书笔记
深入理解计算机系统
论文
技术之外
开发
rcore-arm
页面
关于
搜索到
21
篇与
的结果
2023-12-29
Rust入门练习-Week1-LC387/1696/442/621/377
LC377. Combination Sum IV Med类似于一个完全背包方案数吧。DP给定一系列数字a[i],求用它们组合为 target 的方案数,a[i]可以重复使用状态:dp[i]表示目前已经加和到 i 的方案数转移: dp[i] = sum(dp[i-a[j]])边界条件:dp[0] = 1, ans = dp[target]impl Solution { pub fn combination_sum4(nums: Vec<i32>, target: i32) -> i32 { let mut dp = vec![0; target as usize + 1]; dp[0] = 1; for i in 0..target { for j in &nums { if i+j > target { continue; } dp[(i+j)as usize] += dp[i as usize]; } } dp[target as usize] } }LC621. Task Scheduler Med题目一眼贪心,目的转化为使等待时间最少,考虑一种策略: 把任务 A-Z 按出现的次数排序,并从最多的任务(假设为 M,出现 m 次)开始排列排列必为 M...M...M...M 这类的情况,其中.的数量为 n只要在空缺中插入其他任务,且保证每个空缺不会出现两个相同任务,可知这类的排列必是满足题意的。考虑有任务 X、Y、Z 在这种插入后分别剩 x、y、z 次(显然,任务 X、Y、Z 的总数量都要小于 m)只需要扩充 每两个 M 之间的间隔,直到能插下即可,也必然符合题意,且不会使得等待时间更多。因此问题转化为 (m-1)n > len(tasks)-m ? (m-1)(n+1) : len(tasks)但是这个式子少考虑了如果有任务 K, k = m的情况,只要计数,并在最后累加一下即可(假设有 cnt 个任务都达到了最大值,我们先假设其中 cnt-1 个都只有最大值-1,然后按刚刚的逻辑贪心,最后补 cnt-1 即可)所以我们要做的就是统计每个字母出现的数量并进行排序。我们同过 Rust 的 HashMap 实现该能力。use std::collections::HashMap; impl Solution { pub fn least_interval(tasks: Vec<char>, n: i32) -> i32 { let mut char_count = HashMap::new(); let mut maxn = 0; let mut maxn_cnt = 0; for c in &tasks { let cnt = char_count.entry(*c).or_insert(0); *cnt += 1; maxn = std::cmp::max(*cnt, maxn) } for (_, &v) in &char_count { if v == maxn { maxn_cnt += 1; } } let lhs = (maxn-1) * n; let rhs = tasks.len() as i32 - maxn - maxn_cnt + 1; println!(" {} {} ", maxn, maxn_cnt); if lhs > rhs { return (maxn-1) * (n+1) + maxn_cnt; } else { return tasks.len() as i32; } } }LC442. Find All Duplicates in an Array Med裸的解法:直接扔进桶里,那就开个 Hashmapuse std::collections::HashMap; impl Solution { pub fn find_duplicates(nums: Vec<i32>) -> Vec<i32> { let mut num_count = HashMap::new(); let mut res = Vec::new(); for num in nums { let cnt = num_count.entry(num).or_insert(0); *cnt += 1; if *cnt == 2 { res.push(num); } } res } }但是显然这也太没意思了,就算是面试也不会考你 Map 的使用。其实给了数据范围,开个普通的桶就行,不知道这题为啥也能算中等。。LC1696. Jump Game VI Med一眼 DP,先考虑最朴素的, 状态:f[i]表示已经跳到 i 的最大价值, 转移:f[i]从f[[i-k, i-1]]里更新即可,f[i] = max{f[max(i-k, 0)....i-1]} + nums[i] 边界条件:f[0] = nums[0],ans = f[nums.len()-1]但是这东西是O(nk)的,肯定超时,考虑优化。分析不下去了,这显然是维护前 k 个里最大的,维护一个单调线性结构即可。此结构中的f 的值需要单调减(每次用最左侧的最大值,如果右侧有一个更大的,左侧的值一定没用)。左边出右边进,单调队列即可。use std::collections::VecDeque; struct Node { cur: i32, val: i32, } impl Solution { pub fn max_result(nums: Vec<i32>, k: i32) -> i32 { let mut f = vec![0; nums.len()]; let mut q = VecDeque::new(); q.push_back(Node { cur: 0, val: nums[0] }); f[0] = nums[0]; for i in 1..nums.len() { while !q.is_empty() && q.front().unwrap().cur < i as i32 - k { q.pop_front(); } f[i] = nums[i] + q.front().unwrap().val; while !q.is_empty() && q.back().unwrap().val < f[i] { q.pop_back(); } q.push_back(Node { cur: i as i32, val: f[i] }) } f[nums.len()-1] } }学语法中这块有个小点,就是 q.back() 这类的方法,返回的应该是个Option,我们直接 unwrap() 把 OK 中的值拿回来,如果拿不回来就直接 panic。Option类型是Rust的一个枚举类型,它有两个值:Some(T)和None。Option类型常常用于表示可能存在也可能不存在的值。q.back()和q.front()的返回值类型都是Option<&T>,其中T是队列中元素的类型。这是因为当队列为空时,back()和front()方法没有元素可以返回,所以它们返回的是None。当队列不为空时,back()和front()方法可以返回队列的尾部和头部元素,所以它们返回的是Some(&T)。在处理Option类型时,除了模式匹配和unwrap()方法,还有很多其他的方法,比如unwrap_or()、unwrap_or_else()、map()、and_then()等等。LC387 First Unique Character in a String Ezimpl Solution { pub fn first_uniq_char(s: String) -> i32 { let mut v = vec![0; 26]; let s_chars: Vec<char> = s.chars().collect(); for &ch in s_chars.iter() { v[(ch as i32 - 'a' as i32)as usize] += 1; } for (i, &ch) in s_chars.iter().enumerate() { if v[(ch as u32 - 'a' as u32) as usize] == 1 { return i as i32; } } -1 } }
2023年12月29日
71 阅读
0 评论
0 点赞
2023-09-06
Rust入门笔记 5.方法和关联函数
使用 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日
110 阅读
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日
164 阅读
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日
231 阅读
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日
199 阅读
0 评论
0 点赞
1
...
3
4
5