大家好
今天 完成 2024年自动驾驶OS开发训练营-初阶营第四期-导学
Day2用 rustlings 练习 Rust 语言 -Move Semantics
![alt](https://img-blog.csdnimg.cn/img_convert/4f92488d396970cee957a868e52d0dc2.png)
![alt](https://img-blog.csdnimg.cn/img_convert/5ca79e9c436ba20936ceae4eb1403352.png)
![alt](https://img-blog.csdnimg.cn/img_convert/c613956df8622c0c069540580ea80f40.png)
https://doc.rust-lang.org/stable/book/ch04-00-understanding-ownership.html
提交代码时候 提示 没有权限怎么出来
![alt](https://img-blog.csdnimg.cn/img_convert/318eee6206e3230675fa37704ffe570e.png)
![alt](https://img-blog.csdnimg.cn/img_convert/0c7bd5e716f669023dd0fd4a8b7aa471.png)
aciton
![alt](https://img-blog.csdnimg.cn/img_convert/03a6aac8bd953a90935a9c2a8f6e90d8.png)
参考开发环境配置
https://rcore-os.cn/arceos-tutorial-book/ch01-02.html
我的题目
https://github.com/cicvedu/rustlings-semester-4-watchpoints
创建ssh key,用于ssh方式克隆github代码。 在linux环境下,使用ssh-keygen -t rsa -b 4096 -C "你的邮箱"命令,创建ssh key, 下面的选项全部直接敲回车即可。 随后使用 cat ~/.ssh/id_rsa.pub
Primitive Types
Rust has a couple of basic types that are directly implemented into the compiler. In this section, we'll go through the most important ones.
Further information
![alt](https://img-blog.csdnimg.cn/img_convert/3b0537277608e31e5d916d700b76dedc.png)
The Slice Type Slices let you reference a contiguous sequence of elements in a collection rather than the whole collection.
A slice is a kind of reference, so it does not have ownership.
![alt](https://img-blog.csdnimg.cn/img_convert/51bdcbf8384d8f05626341e0b0e8de73.png)
![alt](https://img-blog.csdnimg.cn/img_convert/ca2d708d4ba22a8fa578029559c9ac3b.png)
在 Rust 中,切片(slice)是从数组或另一个切片中引用一系列连续元素的视图。要创建一个切片,你需要指定切片的开始和结束位置(不包含结束位置的索引)。在数组 a
中,如果你想得到 [2, 3, 4]
这个切片,你需要从索引 1 开始,到索引 4 结束(不包含索引 4)。
下面是如何修复你的测试代码:
#[test]
fn slice_out_of_array() {
let a = [1, 2, 3, 4, 5];
// 从索引 1 开始,到索引 4 结束的切片
let nice_slice = &a[1..4];
assert_eq!([2, 3, 4], nice_slice);
}
在这个例子中,&a[1..4]
创建了一个从数组 a
中索引 1 开始到索引 3 结束的切片,因为 Rust 的切片语法是左闭右开区间(包含开始,不包含结束)。这样 nice_slice
就包含了数组 a
中的 [2, 3, 4]
。
在命令行输入rustlings watch 开始编辑代码的循环实验
![alt](https://img-blog.csdnimg.cn/img_convert/8595cacf89e4e3dfb10e9956fa0047ec.png)
Understanding Ownership
Ownership is Rust’s most unique feature and has deep implications for the rest of the language.
It enables Rust to make memory safety guarantees without needing a garbage collecto
What Is Ownership?
![alt](https://img-blog.csdnimg.cn/img_convert/16ce36714b376614c910f9cb55ca0e95.png)
Ownership Rules First, let’s take a look at the ownership rules. Keep these rules in mind as we work through the examples that illustrate them:
-
Each value in Rust has an owner. -
There can only be one owner at a time. -
When the owner goes out of scope, the value will be dropped
![alt](https://img-blog.csdnimg.cn/img_convert/75ed8fc260701ec665adb384b7ba12b9.png)
![alt](https://img-blog.csdnimg.cn/img_convert/67e2ddb9f0a70d43c529508b4af90193.png)
代码示例本身是正确的,并且展示了 Rust 中所有权和复制的概念。但是,如果您想要确保代码能够编译并运行,并且想要满足一些可能的改进或更正,这里有一些建议:
-
确保
takes_ownership
和makes_copy
函数在main
函数之前定义,因为 Rust 中的函数需要先声明后使用。 -
添加注释,说明为什么
s
在takes_ownership
调用后不能被使用,而x
在makes_copy
调用后仍然可以被使用。 -
添加
main
函数的返回类型,虽然在简单的程序中这不是必需的,但这是一个好的编程习惯。 -
使用 Rust 的格式化工具,比如
rustfmt
,来保持代码的整洁。
下面是根据上述建议修改后的代码:
fn takes_ownership(some_string: String) { // 函数定义在 main 之前
println!("{}", some_string);
} // some_string 的作用域结束,调用 Drop
fn makes_copy(some_integer: i32) { // 函数定义在 main 之前
println!("{}", some_integer);
} // some_integer 的作用域结束,没有调用 Drop,因为 i32 是 Copy 类型
fn main() -> () { // 明确 main 函数的返回类型为 ()
let s = String::from("hello"); // s 拥有 "hello" 的所有权
takes_ownership(s); // s 的值移动到 takes_ownership 函数
// 下面的代码尝试使用 s 将会导致编译错误,因为 s 的值已经移动
// let len = s.len(); // 错误:s 已移动
let x = 5; // x 包含值 5
makes_copy(x); // x 的值被复制到 makes_copy 函数
// x 仍然有效,因为 i32 是 Copy 类型
// x 仍然可以使用
println!("x is {}", x);
}
在这个修改后的版本中,我们添加了 main
函数的返回类型 ()
,这表示 main
函数不返回任何值。同时,注释被添加来解释为什么 s
在 takes_ownership
调用后不能被使用,而 x
在 makes_copy
调用后仍然有效。此外,我们还展示了尝试在 takes_ownership
调用后使用 s
会导致编译错误的例子,以及 x
在 makes_copy
调用后仍然可以使用的例子。
Vectors
Vectors are one of the most-used Rust data structures.
In other programminglanguages, they'd simply be called Arrays, but since Rust operates on a bit of a lower level, an array in Rust is stored on the stack (meaning it can't grow or shrink, and the size needs to be known at compile time), and a Vector is stored in the heap (where these restrictions do not apply).
Vectors are a bit of a later chapter in the book, but we think that they're useful enough to talk about them a bit earlier. We shall be talking about the other useful data structure, hash maps, later.
Further information
vec_map
函数的作用是接受一个 Vec<i32>
类型的引用(即一个包含 32 位整数的向量),然后返回一个新的 Vec<i32>
,这个新向量中的每个元素都是原向量中对应元素的两倍。
具体来说,vec_map
函数中的 map
和 collect
方法是这样工作的:
-
**
v.iter()
**:这个方法创建了一个迭代器,它遍历v
向量的每个元素。iter
方法返回的迭代器提供的是向量中元素的不可变引用。 -
**
.map(|element| { element * 2 })
**:map
方法接受一个闭包(匿名函数),在这个例子中,闭包接受一个参数element
(向量中的一个元素的不可变引用),然后返回这个元素值乘以 2 的结果。闭包体内的操作element * 2
实际上是在对引用指向的数据进行解引用(通过*
操作符)并执行乘法操作。 -
**
.collect()
**:map
方法返回的是一个惰性迭代器,它逐个应用闭包中的操作,但不会立即收集结果。collect
方法被调用来将这些经过map
处理的元素收集到一个新的Vec<i32>
向量中。
使用 map
和 collect
的好处是,它们允许你以一种声明性的方式对集合中的每个元素执行操作,并将结果收集到新的集合中,而不需要编写显式的循环。这种方式代码更简洁,也更符合 Rust 的风格。
这里是一个如何使用 vec_map
函数的示例:
fn main() {
let original_vec = vec![1, 2, 3, 4];
let doubled_vec = vec_map(&original_vec);
println!("{:?}", doubled_vec); // 这将打印:[2, 4, 6, 8]
}
在这个示例中,vec_map
接收 original_vec
的引用,创建了一个新的向量 doubled_vec
,其中包含了原向量每个元素的两倍值,并打印出来。
Move Semantics
These exercises are adapted from pnkfelix's Rust Tutorial -- Thank you Felix!!!
Further information
For this section, the book links are especially important.
move_semantics1.rs
![alt](https://img-blog.csdnimg.cn/img_convert/1353e66f0cb3a6d2e32d13c8b28ea4d2.png)
move_semantics2.rs
![alt](https://img-blog.csdnimg.cn/img_convert/c7c35d73aad68107d431315847f805b9.png)
![alt](https://img-blog.csdnimg.cn/img_convert/3a3d524711fca5aba3d621e320bfff21.png)
// move_semantics5.rs
![alt](https://img-blog.csdnimg.cn/img_convert/a9c288e1e8b82c119855fb5a7bb9cc61.png)
Rust 中的可变引用有一条规则:在任意给定时间,你可以拥有任意数量的不可变引用,或者至多一个可变引用。这条规则确保了内存安全,防止了数据竞争。
在提供的代码中,存在一个问题:main
函数试图创建两个可变引用 y
和 z
,都指向同一个变量 x
。这是不允许的,因为 Rust 的借用规则不允许在同一作用域内对同一变量有多个可变引用。这就是为什么代码无法编译的原因。
要修复这个问题,你需要确保在任何给定时间,只有一个可变引用存在。这里是修复后的代码:
fn main() {
let mut x = 100;
{
let y = &mut x;
*y += 100; // 此时 x 的值变为 200
} // 'y' 的作用域结束,可变引用被销毁
{
let z = &mut x; // 可以创建新的可变引用,因为 'y' 已经不存在了
*z += 1000; // 此时 x 的值变为 1200
} // 'z' 的作用域结束,可变引用被销毁
assert_eq!(x, 1200); // 断言 x 的值是 1200,这是正确的
}
在这个修复的版本中,我们通过创建内部作用域来确保在任何时候只有一个可变引用存在。首先,我们创建了 y
的作用域,对其进行了操作,然后这个作用域结束,y
不再有效。之后,我们创建了 z
的作用域,再次对 x
进行操作。这样就遵守了 Rust 的借用规则,使得代码可以编译并按预期运行。
![alt](https://img-blog.csdnimg.cn/img_convert/7781c418476c4ada5c046ff7db602a21.png)
![alt](https://img-blog.csdnimg.cn/img_convert/e4a6f2402e2cceb17aaf96b61bd2c34d.png)
![alt](https://img-blog.csdnimg.cn/img_convert/e614f67cbf911c44e0099e1cf4c9eab8.png)
这段编译错误信息指出了 Rust 代码中的一个生命周期问题。问题出在 string_uppercase
函数的实现上,它试图将一个临时值赋给一个引用,这是不允许的。
这里是出错的代码行:
data = &data.to_uppercase();
在这行代码中,data.to_uppercase()
创建了一个 String
的新实例,这个实例是一个临时值,它的生命周期依赖于 data
的借用。然而,尝试将这个临时值的引用赋给 data
是不合法的,因为临时值会在语句结束时立即被销毁,而 data
需要一个比临时值更长的生命周期。
要修复这个问题,我们需要修改 string_uppercase
函数,让它接受一个可变引用,并且直接在该引用指向的数据上调用 make_uppercase()
方法,而不是尝试重新赋值。这里是修复后的代码:
fn string_uppercase(data: &mut String) {
data.make_uppercase(); // 直接在 data 上调用 make_uppercase 方法
println!("{}", data);
}
在这个修复版本中,我们移除了 mut data: &String
中的 mut
,因为 data
已经是一个可变引用。然后我们直接在 data
上调用 make_uppercase()
,这样就不会有临时值的问题。make_uppercase()
方法会修改 data
引用指向的 String
实例,而不需要创建新的 String
。
此外,main
函数中的调用也需要相应地更新,以传递一个可变引用:
fn main() {
let mut data = "Rust is great!".to_string();
string_uppercase(&mut data); // 传递一个可变引用
println!("{}", data); // 打印修改后的字符串
}
现在,string_uppercase
函数接受 data
的可变引用,直接修改它,并且 main
函数中的 data
是可变的,可以被 string_uppercase
所修改。这样代码就可以编译并按预期工作了。
![alt](https://img-blog.csdnimg.cn/img_convert/2b91c441dad8cf3dc090f5c4ff5fa1f4.png)
![alt](https://img-blog.csdnimg.cn/img_convert/b1ffd5898dac84546161421e4fbac9cb.png)
![alt](https://img-blog.csdnimg.cn/img_convert/1c1851d842dc496a00ac021e4bd2a5db.png)
/**
【昵称】小王同学
【坐标】山东
【自我介绍】
1. 高级后端工程师,擅长c++并发编程。
2. 专注分布式存储,分布式数据库。
3. 时间的践行者,立志成为一名讲师。
【我的成绩】
1. 为了走出农村,2次高考
一次考研失败,
遇挫之后不知道干什么,开启被动之旅。
2. 为了找到合适工作,
深入研究c++书籍和leetcode 200题目
3. 为了提高项目能力,参与开源项目建设。
4. 为了把简单事情说清楚/要干啥
按照《只管去做》,《福格行为模型>>方法。
纸上得来终觉浅,绝知此事要躬行
做一个践行者。
【我能提供】
1. 后端程序员的简历优化+就业辅导+职业规划
2. 全栈工程师(c++,rust,go,python )项目开发
3. 一年践行12本书践行记录。
【希望一起解决什么,开启破圈之旅】
1. 交接更多朋友,抱团取暖。
寻找意义本身就更加有意义。
2. 无法做整个系统,聚焦一个模块
道可道也,非恒道也
名可名也,非恒名也。
无名 万物之始也
有名 万物之母也
别想太多,只管去做,躬身入局
链接我: # + v(github):watchpoints
#众号:后端开发成长指南
**/
本文由 mdnice 多平台发布