0%

  白熊的专业是微电子科学与工程,并不是传统计算机大类中的专业,但是看到朋友转发的操作系统训练营宣传后,想借这个机会熟悉一些关于操作系统的内容,增强自己对硬件驱动、指令集等(尤其是RISC-V)的了解。由于之前没有系统地学过任何计算机方面的知识,而且还是个freshman,基础可以说是相当薄弱,在第一阶段学习Rust的过程中,我也生出了不少傻头傻脑的见解,甚至被贺兰神评价“少见多怪”。不过经过一段时间的学习,我最终完成了第一阶段rustlings的所有题目。本文就写我初见Rust的一些想法咯~

对Rust的认知变化(做rustlings有感)

  在学习Rust之前,飞飞神有跟我普及过Rust,说它很安全,但写起来很麻烦(举了Rust变量所有权的例子)。当时我没太理解,但不明觉厉,很长一段时间里我认为Rust安全且牛X,是某种高贵而神秘(雾)的语言。

  参加训练营刚接触Rust半小时时,因为看到了很多熟悉的语法,我心里对Rust的期望似乎有所降低,觉得Rust不过是语法与其他语言略微不同……直到我看到了所有权、借用的概念,才真正感觉到这语言非同一般。可是rustlings中所有权一章的题目都挺简单的,没有为难人,这给了我一种“我已经完全了解所有权”的错觉……事实证明并非如此,在后续的题目中,随着代码复杂度的上升,经常会出现把一通报错改成另一通报错的情况,不过正是这些拷打,让我对这些新概念的理解慢慢扎实起来。

  rustlings中间做得还蛮快的,不过感觉我自己有点囫囵吞枣,有一种知识从光滑的大脑溜过的感觉……直到多线程那一块,我彻底卡住了,大脑宕机……后来想了想原因有二:一是对前面知识点根本没有理解透彻;二是我完全没有接触过多线程方面的知识(以前写代码都是傻乎乎单个过程)。最让我崩溃的是支持多线程的可变借用的题,第一次做的时候调来调去搞不明白,挺打击自信的……后来把Rust学习扔在一边,两周后重新拾起来,居然很快解决了,看来学习真的需要“沉淀”的过程。

  接着便没什么困难,直接杀完了基础的100题。算法题我感觉不算很难,毕竟都是很简单的数据结构。不过我认为我的答案不太简洁,可能(一定)包含很多没必要的操作(传值),或有优化的多的写法。归根到底还是自己对Rust的变量体系不熟悉,希望在日后的学习中,慢慢强化这方面,有些问题还是得交给时间(笑)。

一些展望

  正如开头所说,我基础很薄弱,不指望能完成整个训练营,所以便怀着“主打学习”的心态。希望在第二阶段中我能建立对操作系统的认知框架,并尝试将其与我自己的专业知识结合——说不定暑假可以亲手用数字电路实现处理器,跑操作系统,想一想都觉得很酷(前提是我得会)。

2024春季开源操作系统训练营第一阶段总结

作为一个非计算机科班的自动化人,对于计算机的学习有着很高的热情,在好友的推荐下,我了解到了开源操作系统训练营。刚好最近想要做一些相关的lab,就借此良机,加入到这个大的训练营家庭中和大家一起学习。在以前的学习中,常用C/C++,这次新学习了Rust语言,编程习惯还是有些不同(悲),要花大功夫去掌握难点。

学习过程:

  • 我主要使用了《Rust 程序设计语言》官方文档作为学习指南,并结合rustling进行知识点的学习和巩固。
  • 开始的基础语法较容易,很快就做完了,但是到后面的tests和algorithm部分,难度增大了,花费了不少时间才完成题目(难难难)。
  • 坚持就是胜利,每天都感觉有进步。
  • 主要难点部分在所有权系统、模式匹配、trait和生命周期,还需要多加联系。

总结与展望:

通过这20天的学习,我对Rust语言有了更深入的了解,也掌握了一些基本的编程技能。期待进入第二阶段的学习了。加油干小伙子!

Rust语法学习

这里将学习Rust中感到困惑的一些Rust语法进行整理

变量隐藏

1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
using namespace std;

int main() {
int x = 5;
x = x + 1;
{
int x = x * 2;
printf("in for : %d\n", x);
}
printf("out of for : %d\n", x);
}

这段代码的输出结果是

1
2
in for : 0
out of for : 6

在 C++ 中,当你在 for 循环内部声明一个新的变量 x 并试图初始化它为 x * 2 时,这里的 x 实际上是指向新声明的 x,而不是外部作用域的 x。因为新声明的 x 在这个时候还没有被初始化,所以 x * 2 的结果是未定义的,但在大多数情况下,它会被初始化为 0。

这是因为在 C++ 中,新的变量 x 的声明和初始化是在同一条语句中完成的,所以在 x 的值被计算(即 x * 2)时,新的 x 已经遮蔽了外部作用域的 x

1
2
3
4
5
6
7
8
9
fn main() {
let x = 5;
let x = x + 1;
{
let x = x * 2;
println!("inner {x}");
}
println!("outter {x}");
}

而rust中的结果是

1
2
inner 12
outter 6

这段代码中的 x 变量在不同的作用域中有不同的值。在 Rust 中,可以在一个作用域中重新声明一个与外部作用域同名的变量,这被称为变量遮蔽(shadowing)。

首先,x 被初始化为 5。然后,x 被重新声明并赋值为 x + 1,所以 x 的值变为 6。

然后,进入一个新的作用域(由 {} 定义)。在这个作用域中,x 被重新声明并赋值为 x * 2,所以在这个作用域中,x 的值变为 12。这个值在 println!("inner {x}"); 语句中被打印出来。

当离开这个作用域时,我们回到了外部作用域,x 的值再次变为 6。这个值在 println!("outter {x}"); 语句中被打印出来。

字符串与所有权

结构体元组

成员相同但名称不同的元组不是同一种元组,不能相互赋值

生命周期的3个规则

生命周期约束

'a : 'b 表示 a >= b

子类型的生命周期

SubSuper 的子类型, Sub 的生命周期要包含 Super 的范围,有可能更大

目前Rust生命周期的子类型关系对于泛型存在三种映射

  • 如果 TU 的一个子类型意味着 F<T>F<U> 的一个子类型(即子类型化“通过(passes through)”),则 F<T>T 上是协变的(covariant)_。
  • 如果 UT 的一个子类型意味着 F<U>F<T> 的一个子类型,则 F<T>T 上是_逆变的(contravariant)_。
  • 其他情况下(即不能由参数类型的子类型化关系推导出此泛型的型变关系),F<T>T 上是的_不变的(invariant)_。

引用的生命周期

引用的生命周期从借用处开始,一直到最后一次使用的地方

再引用(ReBorrow), 将指针解引用后再引用的行为,如let ptr2 : Point = &*ptr1;

const泛型

无界生命周期

闭包

闭包中如果未声明参数类型那么一定要使用,否则编译器无法判断是什么类型

闭包根据参数在函数体内如何使用判断捕获参数的类型,可变还是不可变

使用move关键字强制获取所有权

智能指针

表现类似指针,同时拥有额外的元数据和功能,如String在内存中的分布

Box<T>

  • 当有一个在编译时未知大小的类型,而又想要在需要确切大小的上下文中使用这个类型值的时候
  • 当有大量数据并希望在确保数据不被拷贝的情况下转移所有权的时候
  • 当希望拥有一个值并只关心它的类型是否实现了特定trit而不是其具体类型的时候

    使用Box<T>创建递归类型

示例

1
2
3
4
5
6
enum List {
Cons(i32, List),
Nil,
}

let list = List::Cons(1, List::Cons(2, List::Cons(3, List::Nil)));

实现Deref以及函数和方法的隐式转换

通过std::mem::drop提早丢弃值

Rc<T>引用计数

Rc::new 创建

Rc::clone(&a),获取只读所有权

调用Rc::strong_count获取计数

RefCell<T>和内部可变性

对于引用Box<T>借用规则不可变性作用于编译时,对于RefCell<T>不可变性作用于运行时,如果违反则panic

原子计数引用Arc<T>

多线程中的Rc<T>

第一阶段总结

感谢活动主办方提供的宝贵平台和资源!

希望自己至少能坚持做完二阶段。

RUST

学习资料:

rustlings 110题 https://github.com/LearningOS/rust-rustlings-2024-spring-zhouyecs(已完成)

rust语言圣经 https://course.rs

rust练习实践 https://practice-zh.course.rs/

rust by example https://doc.rust-lang.org/rust-by-example/

半小时学习rust https://fasterthanli.me/articles/a-half-hour-to-learn-rust(已完成)

rust algorithm club https://rust-algo.club/(已完成)

《rust实战》https://www.amazon.com/Rust-Action-TS-McNamara/dp/1617294551

(rust资料太多,慢慢学……)

RISC-V

之前学习过《计算机组成与设计(RISC-V版)》,所以这次只是简单地看了一下PPT for RISC-V特权指令级架构,打算后面有时间学习RISC-V手册:一本开源指令集的指南Berkeley CS61C: Great Ideas in Computer Architecture (Machine Structures)

rustlings 难点记录

rustlings 110题 https://github.com/LearningOS/rust-rustlings-2024-spring-zhouyecs

参考资料

- The Book - The most comprehensive resource for learning Rust, but a bit theoretical sometimes. You will be using this along with Rustlings!

- Rust By Example - Learn Rust by solving little exercises! It’s almost like rustlings, but online

做题的时候忘记边做边记录了,所以选了些难点记下来。

引用

这里和C/C++类似,引用使用 & ,解引用使用 *

1
2
3
4
5
6
7
8
9
10
11
fn main() {
let needle = 42;
let haystack = [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862];

for reference in haystack.iter() {
let item = *reference;
if item == needle {
println!("{}", item); // 42
}
}
}

字符串

Rust中,strString是两种不同的数据类型,特别容易搞混,str是字符串切片类型,是一个不可变引用,而String是字符串类型,是一个可变的字符串。

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
fn main() {
// 使用字符串字面量创建一个str类型的字符串切片
let str_slice = "Hello, World!";
println!("str_slice: {}", str_slice);
// 尝试修改str_slice会发生报错

// 使用String结构体创建一个可变的字符串
let mut string = String::from("Hello");
println!("string: {}", string);

// 修改String类型的字符串
string.push_str(", World!");
println!("string: {}", string);

// String -> &str
let s1 = String::from("PKU");

let s2 = &s1[..];
let s3 = s1.as_str();

// &str -> String
let s4 = "THU";

let s5 = s4.to_string();
let s6 = String::from(s4);
let s7 = s4.to_owned();
}

还有更多就没写了,还是得多练练。

训练营第一阶段主要是通过上课以及Rustlings练习的方式学习Rust语言。Rust语言在学校的时候已经学过一遍了所以做起来还算轻松。

Rust语言学习心得

在学习Rust语言的过程中,我深深地感受到了这门语言的独特之处以及学习的挑战。通过完成第一阶段的Rustlings练习和复习Rust语法知识,我收获了许多宝贵的经验和见解。

Rustlings

Rustlings是学习Rust非常好的资料。

通过解决各种练习题,我不仅熟悉了Rust语言的基本语法,还学会了如何处理各种常见的编程问题。尤其是在处理字符串和迭代器等常见操作时,我学会了如何巧妙地利用Rust语言特性来简化代码,提高效率。

第一遍做的时候以为全过,过了n久才发现fail了三道,飞速赶工修好了。。。

另外,Copilot和ChatGPT真的非常好用。

未来期望

希望能够入门Rust-for-linux,真正在各种现实项目中用起来Rust。

第一阶段总结

回顾

这是第二次参加rcore训练营了,上一次的时候是2023秋季的时候,那时候还没有学过rust,也不知道risc-v。而是在学过操作系统课后,想要找一个操作系统实验来做,锻炼自己。因为自己平时很喜欢c++,而各种地方都在不断拿Rust和C++对比,因而开始去接触Rust,也因此,正巧碰上了2023秋季rcore训练营的开启。去年时间很短,个人基础也很差,所以学得很费劲,磕磕绊绊才把第二阶段过了。刚到第三阶段,碰上了期末周,导致没有继续持续下去,很是遗憾。所以又在等啊等啊,2024年春季的rcore终于开始了……。

rustling

由于上次做过,所以本次的前一百道题做得很顺利,花费的时间很少。直到今年多出来的10道算法题,其实思想也是很简单,就是用Rust来写很让我难受。尤其第一题的链表,让我实在……,用c++写得挺感觉很简洁或者轻松。但rust写起来……拖着最后还是不得不写完了。

Rust

Rust算是我学过的一种蛮特别的语言。开始C->C++->Java/python。我感觉基础语法什么的基本都差不多。但是Rust确实是给我的感觉不太一样,风格不一样。但学的时候,却是在不断对比C++来学习,学起来确是不断加深对两门语言的印象。总之Rust促使C++更多思考安全!,但对Rust也越来越喜欢了。总之,两个我都要。

第二阶段好好搞!!!

Rustlings 学习感悟

不得不说,Rust对于我这种只会C/C++的语言的人来说还是很有一定难度的,这种难度来自于对于函数式编程的不熟悉。但一旦熟悉后就感觉到了函数式编程的便捷,特别是迭代器的处理。同时这也增加了一些压力,相较于啥都自己手写的C/C++,Rust等函数编程语言的重点则是熟悉各种各样的函数方法,有时候自己冥思苦想几天的代码,一个简单的函数就能解决,这种经历还是很难受的。

Rust语言除去函数编程以及一些语法糖之外,令人啧啧称道的就是他的安全机制了。确实,初次接触Rust感觉十分惊艳,觉得取代C/C++只是时间问题。但做过Rustlings后,感觉到可能这还是存在一些距离。一方面对于程序员要求太高了,难度感觉比C/C++高出了很多,另一方面尽管尽力屏蔽了底层,但却不得不使用一些unsafe的代码块实现底层操作,导致有点emmm怪怪的。

难度方面,我感觉Rustlings中的algorithm系列题目可以展现出Rust的“刁钻之处”,特别是对泛型T的处理。由于泛型T既可能有Copy也有可能有Clone,这导致很多常规的赋值方法并不能使用,在老师的讲解中我了解到了

1
2
let mut k = T::default();
std::mem::swap(&mut k, &mut self.data[self.top]);

这种写法,但实际使用中还是存在很多限制。不过经过思考和练习,发现很多赋值是可以优化的,特别是在使用相对应的数据结构的方法。感觉这其实有点像编译器强制程序员思考优化代码(被鞭策的感觉。

底层操作方面,则主要还是指针的使用,不得不说,链表之类的使用指针写代码是真的快。不过网上关于这个争议还是蛮多的,我有一段实现safe的双向链表代码,可惜这里太小了粘不下(笑

Rustlings 感觉练习还是蛮不错的,对于我这种0基础的学生来说,三四天看完b站课程后刷完成就感还是蛮强的(虽然看了不少题解,特别是经过最后algorithm部分的练习,感觉自己算是初步入门了Rust

RCore 学习感悟

作为一个只会408操作系统的电子学生,很早之前就想从代码阶段了解操作系统的构成,很感谢训练营能够给我这次机会。

但不得不说,使用Rust语言实现的RCore对我这种只会C语言的难度有点大,各种各样的闭包、智能指针让我看得经常找不到北。坦白来说,就算经过RCore的学习,RCore的很多细节我仍然不是很熟悉,只能说大概框架是有的,这一方面是因为操作系统的本身难度,作为一个之前没有太多接触操作系统的学术,这样一个包含汇编、C语言、Rust的大型工程实在是令人压力山大,另一方面则是Rust本身的难度,这语言是有点抽象的(o(╥﹏╥)o),本以为经过Rustlings学习能够大展拳脚,结果发现自己只是入门的阶段,经常忘记用闭包,函数式编程的思想,常常都是C语言的编程逻辑(还是自己太菜了)。

不过,经过RCore的学习我还是收获颇丰,不仅精进了我的Rust技术,更是极大地扩充了我的操作系统知识,原先只能死记的知识点,现在又有了不一样的理解,比如说管道的实现、内核态和用户态的地址,另外也学习到的书本上很少涉及的知识,比如说用户态空间与内核态空间分离(RCore称之为双页表),SV39多级页表机制等等。

经过RCore学习也能感受到自己的不足,特别是汇编部分,不得不说RCore的汇编涉及太巧妙了,经常是读下来感觉耳目一新,醍醐灌顶。希望以后有机会还能参加编译器的训练营。

一阶段 小结

这是我第二次参加了,秋冬季的时候二阶段适逢期中考试,完成的不是很好,三阶段也没有参加。这次期中考试和一阶段又撞一起了,挑了时间一口气赶进度是相当的折磨。

通过rustlings可以快速对Rust的特性进行了解,但是感觉学习的效果并不是非常的好,题目做过之后就忘的差不多了,归根到底还是一直用cpp而不习惯使用rust,在学习的时候很容易在想”这个东西就相当于cpp里的…”

最后几个算法题挺有意思的,做起来也很头疼- -

一阶段暂且告一段落,虽然笔记做了一大堆,但是实际上大部分是复制文档内容和代码样例,勉勉强强算给自己制造完成了很多工作量的错觉来给自己一点正反馈吧。

二阶段 小结

写在开头,如果没有rust-analyzer,我可能对着这一大堆东西愣半天也写不了几段正确且符合逻辑的代码。

做PA的时候总是看到强调要多RTFSC,这确实是相当的重要。所以刚开始的时候对着指导书,并结合源码,把每一个模块的实现的源码阅读了一遍,虽然并不是自己敲的,而且有的地方也不是很理解为什么要这么写,但是总体上有了大概的了解,不至于那么的无从下手。

这个学期学校课程安排开设了操作系统课程,相比于秋冬季时没有系统的学习操作系统而直接开始阅读Tutorial来说,这一次重新阅读,结合课本的相关知识,这些概念和代码实现亲切了许多。

简单说说在各个章节中的收获:

多道程序

本章学习了任务相关的知识,包括任务切换,任务调度,任务控制块TCB数据结构的设计等,相比于课本上生硬地写出TCB需要保存的内容来说,使用代码实现TCB的设计可以有了更加深刻的理解。

实践作业实现了一个新的系统调用 sys_task_info 用以获取当前正在运行的任务的信息。

地址空间

印象最深的是SV39多级页表。类似于字典树(Trie),大大节省了占用的内存空间。
还有虚拟内存的相关概念,虚拟内存和物理内存的转换。

实践作业实现了 mmap 和 munmap 匿名映射,简化了实现,仅用于申请内存。
主要是要RTFSC,在其他的系统调用中查看如何转换地址,如何查页表。

进程

进程和线程在学校课程学习中是很重要的一部分,本章介绍了进程的概念和相关系统调用,并进行进程管理和进程调度。

实践作业包括两个:

  • 实现spawn
    虽然好像 fork+execve!=spawn,但是我的实现就是把fork的代码复制下来,然后和execve的代码进行了一个缝合…
  • 实现stride调度
    发现了ci的一个bug,即实际上如果只实现了set_priority这个用于设置优先级的系统调用,并没有去实现调度的话也可以通过ci测试…

文件系统

印象非常的深刻的是Unix哲学的一切皆文件

本章的内容相当的多,因为文件系统比较复杂,读起来非常的费劲而且读完就容易忘…

只记得bitmap,inode啥的了2333,在PA中实现过read,open,seek等操作,印象也不是很深刻了,当时好像是可以将不同类型的文件用统一的接口来进行读写操作。

实践作业实现了linkat和unlinkat,关于fstat,在计算inode时遇到了一些困难…

并发

首先介绍了用户态和内核态的线程管理。
线程的并发问题是老生常谈了,用了锁,信号量,条件变量等进行线程同步互斥管理。

实践作业实现死锁检测,一开始就把这个认为是银行家算法,但是潜水看群里大佬们聊天说并不是,只是用了银行家算法的思想,再想了想课本上学习的,好像确实…

比较麻烦,一开始无从下手,因为不知道资源数量,需求资源啥的需要在哪里定义。

小结的小结

二阶段的编程小练习涵盖了操作系统设计的几个重要方面,我认为二刷甚至多刷也是有价值的-。-

三阶段 小结

项目3 Rust for Linux & 跨内核驱动框架

练习一 配环境

工欲善其事,必先利其器。

万事开头难,确实如此。在配环境上折腾了好久,一开始用WSL,但是对于图形界面的支持不够以致QEMU界面始终是黑屏- -…又改用虚拟机,但是性能很差,编译一次要花费好长时间,虽然可以显示画面了,但是到练习二的时候又出现了画面卡死动不了的问题- - …

有群友使用docker整了一个环境,也整下来试了一下,但是被mac的大小写问题给坑了一下,到最后也没配好

最后得到的最优解是在WSL启动QEMU时使用 vnc 进行连接。

算一下为了配好环境前前后后折腾了好几天,编译了不下十几次- -

练习二 实现Miscdev驱动基本读写

本学期学校开设的操作系统课程实验也使用了树莓派进行内核编程,其上烧写的是OpenEuler。于是对于内核模块的编写多少有一点亲切感。

完善了open/read/write的内容,使得杂项字符设备可以实现基本读写操作。

练习三 跨内核驱动框架下的树莓派4B GPIO点灯实现

一开始一头雾水,不知道要怎么下手,又遇上期末考试,于是就暂时搁置了。

考试周结束后又重新捡了起来,去课堂看了之前的回放才知道了如何把写的pure driver编译进内核。

在pure driver中要做的事情主要是完成树莓派的所有寄存器的抽象。(阅读树莓派的手册很重要)

然后在Adapter driver中把寄存器映射到内存地址,通过对于地址的操作,进而控制寄存器,达到GPIO口的电平和输出模式等等的目的。

1
let mapped_base = unsafe { bindings::ioremap(BCM2837_GPIO_BASE, GPIO_SIZE) };

在Arceos上的操作也类似,pure driver可以直接复用,但是要把osl相关的东西给换掉。在映射地址时多了个物理地址转为虚拟地址的操作。

1
let mut gpio_map=RpiGpioPort::new(phys_to_virt((gpio_base as usize).into()).as_mut_ptr());

文档给的点灯驱动参考非常的详细,照着做基本上就能够正常运行起来。(但是有的地方还是不理解为什么要这么操作)

总结

本次训练营的学习内容相当丰富,学到了很多新知识,以及探索了之前没有涉足过的领域。
但是由于课业等等压力,一直都算是在闲时才来跟一下训练营的进度,因此各方面感觉自己学的不够扎实,学习深度也有限,仍需要继续沉淀…
总结就是要多读源码,多看手册,不要因为嫌麻烦偷懒不看- - 问就是在这方面吃了很多的教训,有的问题想好久想不明白,这里为啥这么写?这个寄存器为啥这么操作? 最后一看手册里写的清清楚楚的…

我们旅行时不要像个信使一样,而应当像个探险家一样。我们不仅要考虑起点和终点,还得考虑起点和终点之间的距离

~

繋がる空の下
さあ自分らしく進もう
始まりの歌贈るよ
歩き出す君へ

–献给此刻迈步前行的你

Author:
kami
Xiamen University

Rust学习经验

作为经验分享,我不会写太多,一小篇足以

前言

Rust作为新时代的语言,其语法特性相对于以前的语言有着较大的差别,而且目前的Rust教程都是非常繁琐且没有梳理出一条很好的路线的,具体表现为既不是阶梯式也不是直接深入讲一个点,前后内容穿插令人无法理解。当然这也与Rust本身语言的特性相关,而并非完全是教程编写者的原因。

学习任何语言的思想都应该是读大量的项目源码,Rust也是如此,不过更应该先掌握Rust语法基础。

以下为Rust学习的部分参考资料:

官方学习文档

Rust圣经(可以看作官方文档的中文替代版)

THU-Rust学习建议

Rustlings

我的建议

先通过学习文档练习语法,然后刷Rustlings,学习过程中参考教程代码or别人的代码,通过crate doc文档查询相关crate源码

Rust特色分享

所有权与生命周期

众所周知,Rust同C++一样是没有GC的,虽然C++在C++11有过GC提案,但是并未有编译器实现(可以当作没有),在C++23移除。C++可以通过RAII控制资源,那么Rust也有智能指针可以实现RAII,但是本文不讨论这个。本篇的主角是所有权

既然我要聊所有权,那么我为什么要标注生命周期这个东西呢,那当然是因为所有权和生命周期息息相关,C++的内存安全问题基本上都是对于所有权和生命周期的错误使用导致的。而由于Rust中的所有权是在编译期进行的,对运行期的性能没有任何影响。

Rust的所有权有三个原则

  1. Rust 中每一个值都被一个变量所拥有,该变量被称为值的所有者
  2. 一个值同时只能被一个变量所拥有,或者说一个值只能拥有一个所有者
  3. 当所有者(变量)离开作用域范围时,这个值将被丢弃(drop)

下面将通过一段代码说明以上意思

1
2
3
4
5
6
7
8
9
10
fn main(){
let a = String::from("Hello Rust");
let b = a;//此时所有权转移到b
println!("{:?}", a); //Wrong !!!
let c = b.clone();//c通过克隆产生,不会拿走b所有权
{
let d = b;//d获得b的所有权,并在该代码段结束后drop
}
println!("{:?}", b); //Wrong !!!
}

当然这只是浅浅的一点例子,我无法在这说完,可以查看前言中的资料进行深入学习

生命周期

有一个关于很有意思的概念可以被应用于生命周期,我只会在此介绍这个概念。

三种映射

  1. 协变(covariant) T: R(T<’a>, T<’b>) = R(‘a, ‘b)。
  2. 逆变(contravariant) T: R(T<’a>, T<’b>) = ~R(‘a, ‘b),也就是若 ‘a<’b,则 T<’a> > T<’b>。
  3. 不变(invariant) T: R(T<’a>, T<’b>) = “<> 或 =”,也就是无法推导子类型关系

你会发现,看上面的概念是不是看不懂?

看不懂就对了,其实这个对于编程来说并没有什么卵用,只是一个很有意思的东西。

那么我们不从Rust的角度来看协变和逆变的概念。

A <: B 代表A是B的子类型
A -> B 以 A 为参数类型, 以 B 为返回值类型的函数类型
x : A 这里x是一个变量, 其类型为A

其实这里从继承关系来看就很好接受了,我们有三个类型如下:
A <: B <: C

这里A变成B、C是协变,B变成C是协变,C变成A、B是逆变,B变成A是逆变。

为什么呢?
定义如下:

协变(Covariance)是指子类向基类的转换.
逆变(Contravariance)是指基类向子类的转换.

是不是突然明白了

那这和生命周期有啥关系呢?

生命周期是有长短的,在哪开始在哪结束,那是不是就有变长变短,的概念了,协变和逆变的概念是不是就能套进去了?

1
2
3
4
5
6
7
8
9
10
fn main() {
let r; // ---------+-- 'a
// |
{ // |
let x = 5; // -+-- 'b |
r = &x; // | |
} // -+ |
// |
println!("r: {}", r); // |
} // ---------+

从上面可以看出显示看出r的生命周期和x的生命周期,那是不是生命周期变长的就是协变,生命周期变短的就是逆变了。

非常容易理解!

TODO!
PS:慢慢写

2024年春夏季开源操作系统训练营-基础阶段-rust编程

1. rust编程语言

在软件开发的广阔天地中,Rust以其独特的内存安全性和高性能特性,赢得了越来越多开发者的青睐。作为一位对Rust感兴趣的新手,我决定踏上这段基础学习的旅程,并在这里分享我的学习心得。

2. 学习之前对rust的了解

Rust是一种系统编程语言,它旨在提供内存安全性,而又不牺牲性能。它的主要特点包括所有权系统、生命周期和借用检查器,这些特性共同确保了程序在运行时不会出现内存泄漏或空指针引用等常见问题。在开始学习Rust之前,我先对其背景和发展历程进行了了解。Rust起源于Mozilla的研究项目,旨在为C++提供一种更安全、更高效的替代品。经过多年的发展,Rust已经成为了一个功能强大且稳定的编程语言,被广泛应用于各种领域,包括操作系统开发、网络编程、嵌入式系统等。

3. 基础语法与数据类型

Rust的语法简洁明了,与C++和Java等主流编程语言有许多相似之处。我开始从学习Rust的基本语法和数据类型入手,如变量声明、条件语句、循环结构等。Rust的数据类型包括整数、浮点数、布尔值、字符和字符串等,这些数据类型的使用与其他语言类似,但也有一些独特之处。例如,Rust中的变量默认是不可变的(immutable),如果需要修改变量的值,必须在声明时使用mut关键字将其标记为可变的(mutable)。这种设计有助于减少程序中的错误,提高代码的可读性和可维护性。

4. 所有权与借用

Rust的所有权系统和借用检查器是其内存安全性的核心所在。在学习过程中,我花了大量时间来理解这两个概念。所有权系统决定了谁负责一块内存区域的生命周期,而借用检查器则确保了在任何给定时间,一个值只能有一个可变引用或任意数量的不可变引用。通过编写一些简单的示例程序,我逐渐掌握了如何正确使用引用和所有权来避免常见的内存错误。这虽然是一个挑战,但当我看到程序能够安全地运行而不会出现内存问题时,我感到非常兴奋和满足。

5. 学习过程难点

rust-rustlings-2024-spring中前面题总体还比较容易进行,特别是后10道题,花费了不少时间,经过不断的分析解决,最终解决了这些试题。通过这些试题对链表等数据操作认识更加深刻。

6. 总结

通过这次Rust基础学习之旅,我对Rust有了更深入的了解和认识。虽然Rust的语法和概念相对复杂一些,但我认为这是值得的,因为它为我们提供了一种更加安全和高效的编程方式。