0%

CreatorHell LearningOS 第一阶段总结

智能指针

  • Box:这是一种在堆上分配内存并管理所有权的智能指针。它允许你在堆上创建数据,并且当你不再需要这些数据时,Box会自动释放它们所占用的内存。
  • Rc:这是一种用于引用计数类型的智能指针,它允许多个所有者同时拥有同一个数据。当最后一个所有者离开作用域时,Rc会自动减少引用计数,并在计数达到零时释放内存。
  • Arc:与Rc类似,Arc也是引用计数类型,但它是线程安全的,可以在多线程环境中使用。
  • Cow<’a, T>:这是一种写时克隆(Clone-on-Write)的智能指针,类似于虚拟内存管理的写时复制。它包含一个只读借用,如果调用方需要所有权做修改操作,它会克隆借用的数据。

    所有权

  • 所有权概念:在Rust中,每个值都有一个被称为其所有者(owner)的变量,且每个值有且只有一个所有者。这意味着当一个变量被创建时,它就被分配给了一个特定的所有者。
  • 作用域关联:值的生命周期与其所有者的作用域相关联。当所有者变量离开其作用域时,值将被清理,从而防止内存泄漏。
  • 移动语义:当值从一个作用域移动到另一个作用域时,原作用域将失去对该值的所有权。这个过程称为移动(move),它是Rust内存管理的关键部分,有助于减少不必要的数据复制。
  • 借用机制:虽然Rust的所有权原则保证了内存安全,但它也引入了借用机制来允许多个引用同时访问数据。借用分为可变借用和不可变借用,它们通过借用检查器来确保数据的并发安全。
  • 克隆与复制:对于实现了Clone trait的类型,可以通过调用clone方法来创建数据的副本。而对于实现了Copy trait的类型,数据会在赋值或作为参数传递时自动复制,这适用于那些可以安全地复制数据的情况。

    多线程

  • 线程创建:在 Rust 中,可以使用标准库中的 std::thread 模块来创建新的线程。通过 spawn 函数可以启动一个线程,该函数接受一个闭包(匿名函数),这个闭包将在新线程中运行。
  • 线程控制:创建的线程可以通过 join 方法等待其执行完毕。如果主线程结束而子线程还在运行,Rust 会触发 panic 以防止资源泄露。因此,通常需要调用 join 或 detach 来避免这种情况。
  • 线程安全:Rust 的所有权和借用机制有助于保证多线程环境下的内存安全。当需要在多个线程间共享数据时,可以使用 Arc, Mutex, RwLock 等智能指针来确保同一时间只有一个线程能够访问或修改数据。
  • 消息传递:对于需要线程间通信的场景,Rust 提供了消息传递机制,如 mpmc(Multiple Producer, Multiple Consumer)队列,允许多个生产者和消费者线程安全地交换信息。

前言

由于对操作系统的相当的感兴趣,很巧的是当时在网上不经意间看到了这个开源操作系统训练营的相关信息,就赶紧报名参加了,同时这也是我第一次参加这样的开源课程,也是我第一次很深入的来学习操作系统的相关知识 ,对Rust也不是很了解,在报名之后就开始查找相关的资料学习了点Rust,去学习一个不熟悉的语言和框架,其实对我而言是非常有挑战性的一次学习旅程。

第一阶段学习总结

总体来说在第一阶段这个学习过程还是有点痛苦的,因为学习Rust的过程中我需要查阅许多相关的资料去了解相关知识点,同时,在学习的过程也会出现许多前所未有的问题,这就需要找方法解决,开始了一种循环模式。不过慢慢的能掌握,同时做练习也更能帮助我理解,总体还是较为顺利的!

Rust的学习

学习历程

rust语言学习起来还是挺困难的,难度比较高,我是通过《Rust语言圣经》这本书边学边看,还同时参考了《Rust程序设计语言》、Rust 语言备忘单等,遇到不会的看书再者看网上的教学边看边学,Rust虽然难度高但是从性能上和从安全性上都是非常不错的,优势也很突出!就比如rust最具特色的就是他的内存所有权机制。一块内存只能有一个所有者。当遇到和函数的参数传递的时候,所有权便会转移。rust中没有Null这个关键字,有的只是Option枚举类型。这就是其他语言所不具备的特性。
其实这个阶段主要还是作业练习这一块占有很大的比重,刚开始都是较为简单的语法题目,很好上手,越到后面的难度就逐渐往上升,特别是test和algorithm这一块,需要好好掌握rust的基础语法和rust实现数据结构算法等相关知识,将其灵活巧妙的运用起来。
在学习过程中整理了学习笔记, 对于理解不清晰的模块和问题会仔细梳理代码逻辑直到过程清晰,这对我来说是个非常好的掌握方法,可以将一些几节甚至是几章的内容进行串联起来,对前面的内容进行快速的梳理。

学习氛围

交流群内大家互帮互助,起到了很好的交流作用
方向一致,共同进步
技术技巧性的学术氛围浓厚
师生交流,积极沟通

学习成果

在第一阶段的学习中,我掌握了rust基础,逐渐学习操作系统相关的知识。通过和群友的交流分析,我也展开了对更多其它知识点的思考,在进一步讲在学习更多Rust关于智能指针和并行方面内容后,关于Rust的安全性保障和性能保障这两方面的疑惑又加深了。Rust在尽可能为安全保驾护航,它也做得非常好,但确实有时为了保障安全需要牺牲性能,这也许是不可避免地,Rust在尽可能做到最好。

展望

Rust 是一门全新的语言,它会带给我们前所未有的体验,同时也能提升通用编程水平,甚至于赋予我们全新的编程思想。我希望能够坚持完成第二阶段的学习,完善Rust的知识体系,深入了解操作系统带来的魅力。

致谢

首先十分感谢陈渝,向勇老师提供的这次参与夏令营的机会,其次是非常感谢给我们第一阶段讲课的两位讲师,认真负责并且提供帮助,我也成功的完成了第一阶段的学习。

参考资料

《Rust语言圣经》:https://course.rs/about-book.html
《Rust程序设计语言》:https://rustwiki.org/zh-CN/book/
《Rust指南》:https://tourofrust.com/
Rust 语言备忘单:https://cheats.rs/
Rust教程:https://www.youtube.com/
Rust 语言中文社区:https://rustcc.cn/


第一阶段学习总结-ZhongkaiXu

写在前面

这是第二次写rustlings,两个月前应实习要求第一次学习rust和rcore,这次和上次相比稍微熟练了一些,从最开始的一头雾水,到现在已经对rust的简洁性佩服得五体投地。接下来要一些smmu相关的开发,借着这个机会再熟悉了一下rust。以下是一些学习心得。

学习心得

我认为的rust的核心思想

我觉得rust里最重要的是所有权机制,一个value只能对应一个所有者,即使是函数传参也是一样,虽然刚开始有些不习惯,但是慢慢发现对于锻炼系统软件开发者的思维很有效。

文档

在做题的时候我会同时打开rust文档,因为太多东西记不住了,需要随用随查,抛开水平不够,感觉这还算是一个不错的习惯,开了一个好头,毕竟后面几个阶段的学习必须依赖riscv/arm的手册。

最有挑战性的

我觉得在使用rust开发过程中,最难的地方在于如何把程序写的“rust”,比如对于寄存器的访问,rust中有一些优秀的宏如 registers_struct! ,第一次看到这个东西之前还一直停留在c风格的编程思想,包括写的一些算法题也是没有完全发挥出rust的语言特性,希望在后面学习的过程中多参考一些优秀代码,养成好的习惯。

未来展望

马上进入二阶段的学习,临近6月也要做很多其他的工作,希望能做到WLB吧,把握和优秀的同学们交流的机会,学到更多东西。

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

写在前面

今年三月初的时候被推荐了这个训练营,当时只抱着功利的目的想着参加这个训练营提升一下自己的能力,也丰富一下自己的履历,但是在学习的过程中逐渐发现了rust语言和操作系统的魅力,不知不觉也坚持做完了第一阶段

对于刚参加训练营的我来说,无论是Rust,Risv-v,还是OS,彼时都是一个较为陌生的概念,虽然本科是计科但是自己掌握的知识其实很有限,而且OS是这学期才开的课所以也没什么基础,每天看着群里大佬们的聊天都感觉自己大学是不是白学了,也是在那个时候下定决心要认认真真的学习

emm…不管怎么样,在复习考研,准备公司面试,进实验室打工的种种压力之下,我还是糊里糊涂地开始了自己的学习历程,并且有幸能够坚持下来

学习内容

主要参考的学习资料:关于本书 - Rust语言圣经(Rust Course) 以及 网上各路大佬的博客

印象比较深刻的内容:

Rust的对象所有权机制,生命周期,Option和trait等概念的引入,宏编程,unsafe机制,智能指针,多线程

尤其是智能指针的概念,让我对对象内存分配机制,以及Rust的安全机制和Java中的GC机制有了更深的理解

Rustlings前面的100道题总体来说比较简单,根据参考资料和编译器的错误提示基本都能解决出来,有几个题目有点小疑问的通过大佬们写的博客也能够顺利解决

最后10道题目应该是难度比较大,在这个地方卡了一段时间,不过后面看了一些资料之后也尝试着自己做了出来,只不过用了不少unsafe😔,以后有时间的话希望能把rust的知识再深入巩固一下,看看这部分的题目有没有更好的做法

最后,在一阶段到二阶段这个期间,希望自己能够根据大佬们的提示赶快把指令集架构和rCore的基本知识先看一下吧

学习心得 后续

虽然做完了rustlings的110道题目,但是我深知我自己对rust的理解只能说是迈出了第一步,如果离开了chatGPT的帮助和网上大佬写的博客我可能rustlings还要做好久吧😔

以及后续趁着自己有时间赶快把OS和Risv-v的知识再巩固一下

希望自己能把第二阶段rCore的内容跟下来吧

2024-04-27

在RUST中文社区看到这个活动,从组队到因停电用手机参加开营仪式,到每周一三五参加线上课程学习。

因之前用写过rust程序,有一定基础,但写rustlings还是有一定难度,对泛型、生命周期以及智能指针
还有许多知识要加强

rust相关的问题

rust标准库中重名方法太多,要熟悉常用的数据类型的方法

实现堆栈

以前没实现过堆栈,对此没有概念,加上用rust写堆栈繁琐,这方面要多补充

完成第一阶段期间用到的一些链接

API文档


Table of Contents

  1. rustlings
    1. conversions
      1. as
      2. frominto
      3. fromstr
      4. tryfrominto
      5. asrefmut
    2. unsafe
      1. modify by address
      2. raw pointer to box
    3. build.rs
    4. extern
    5. 算法

rustlings

conversions

as

fn average(values: &[f64]) -> f64 {
    let total = values.iter().sum::<f64>();
    total / values.len() as f64
}

frominto

struct Person{}


impl From<&str> for Person {
    fn from(s: &str) -> Person {
    }
}
// then you can use

let p = Person::from("Mark,20");

fromstr

//可以自定义错误
impl FromStr for Person {
    type Err = ParsePersonError;
    fn from_str(s: &str) -> Result<Person, Self::Err> {
    }
}

tryfrominto

// 与from类似,但可以定义转换错误处理
// Tuple implementation
// https://doc.rust-lang.org/std/convert/trait.TryFrom.html


// Tuple implementation
impl TryFrom<(i16, i16, i16)> for Color {
    type Error = IntoColorError;
    fn try_from(tuple: (i16, i16, i16)) -> Result<Self, Self::Error> {
        let red = u8::try_from(tuple.0).map_err(|e| IntoColorError::IntConversion)?;
        let green = u8::try_from(tuple.1).map_err(|e| IntoColorError::IntConversion)?;
        let blue = u8::try_from(tuple.2).map_err(|e| IntoColorError::IntConversion)?;

        Ok(Color { red, green, blue })
    }
}

//可见,从i16->u8 可以使用 u8::try_from(i16)

asrefmut

https://doc.rust-lang.org/std/convert/trait.AsRef.html

unsafe

modify by address

  1. raw pointer to ref

    pub struct Adapter<T: Driver>(T);
    
    impl<T: Driver> driver::DriverOps for Adapter<T> {
    type RegType = bindings::pci_driver;
    
    unsafe fn register(
            reg: *mut bindings::pci_driver,
    ) -> Result {
            // SAFETY: By the safety requirements of this function (defined in the trait definition),
            // `reg` is non-null and valid.
            let pdrv: &mut bindings::pci_driver = unsafe { &mut *reg };
    
            pdrv.name = name.as_char_ptr();
    
            //...
    }
    }
//linux/rust/kernel/net.rs

unsafe extern "C" fn get_stats64_callback(
        netdev: *mut bindings::net_device,
        storage: *mut bindings::rtnl_link_stats64,
) {
        // SAFETY: The C API guarantees that `net_device` isn't released while this function is running.
        let dev = unsafe { Device::from_ptr(netdev) };
}


impl Device {
/// # Safety
///
/// The caller must ensure that `ptr` is valid and remains valid for the lifetime of the
/// returned [`Device`] instance.
pub(crate) unsafe fn from_ptr<'a>(ptr: *const bindings::net_device) -> &'a Device {
        // SAFETY: The safety requirements guarantee the validity of the dereference, while the
        // `Device` type being transparent makes the cast ok.
        unsafe { &*ptr.cast() }
}
}
  1. ref to raw pointer

    //linux/rust/kernel/net.rs
    
    impl<T: NapiPoller> NapiAdapter<T> {
    /// Creates a new Napi object.
    pub fn add_weight(dev: &Device, weight: i32) -> Result<Pin<UniqueArc<Napi>>> {
            let mut napi = Pin::from(UniqueArc::try_new(Napi::new())?);
    
            unsafe {
            bindings::netif_napi_add_weight(
                    &*dev as *const Device as *mut bindings::net_device,
                    napi.as_mut().0.get(),
                    Some(Self::poll_callback),
                    weight,
            )
            }
            Ok(napi)
    }
    }
  2. &[u8] to core::ffi::cchar

#[cfg_attr(not(CONFIG_PRINTK), allow(unused_variables))]
unsafe fn printk(&self, klevel: &[u8], msg: fmt::Arguments<'_>) {
        // SAFETY: `klevel` is null-terminated and one of the kernel constants. `self.raw_device`
        // is valid because `self` is valid. The "%pA" format string expects a pointer to
        // `fmt::Arguments`, which is what we're passing as the last argument.
        #[cfg(CONFIG_PRINTK)]
        unsafe {
        bindings::_dev_printk(
                klevel as *const _ as *const core::ffi::c_char,
                self.raw_device(),
                c_str!("%pA").as_char_ptr(),
                &msg as *const _ as *const core::ffi::c_void,
        )
        };
}

raw pointer to box

/// # Safety
///
/// The `ptr` must contain an owned box of `Foo`.
unsafe fn raw_pointer_to_box(ptr: *mut Foo) -> Box<Foo> {
    // SAFETY: The `ptr` contains an owned box of `Foo` by contract. We
    // simply reconstruct the box from that pointer.
    let mut ret: Box<Foo> = unsafe { Box::from_raw(ptr) };
    ret
}

build.rs

set env

let timestamp = std::time::SystemTime::now()
    .duration_since(std::time::UNIX_EPOCH)
    .unwrap()
    .as_secs(); // What's the use of this timestamp here?
let your_command = format!(
    "rustc-env=TEST_FOO={}",
    timestamp
);
println!("cargo:{}", your_command);

set feature

// In tests8, we should enable "pass" feature to make the
// testcase return early. Fill in the command to tell
// Cargo about that.
let your_command = "rustc-cfg=feature=\"pass\"";
println!("cargo:{}", your_command);

extern

//提供ABI接口

mod Foo {
    // No `extern` equals `extern "Rust"`.
    #[no_mangle]
    fn my_demo_function(a: u32) -> u32 {
        a
    }
    #[no_mangle]
    fn my_demo_function_alias(a: u32) -> u32{
        my_demo_function(a)
    }
}

//使用ABI

extern "Rust" {
    fn my_demo_function(a: u32) -> u32;
    fn my_demo_function_alias(a: u32) -> u32;
}

算法

二叉搜索树

https://www.hello-algo.com/chapter_tree/binary_search_tree/

**

阶段一 Rust复习

因为本科期间用 Rust2021 比用c++20更多,所以对Rust的基本用法和tricks比较熟。所以最近时间更多用来做毕设。快到一阶段DDL时开始做 rustlings. 和 quiz.

  1. as_mut, as_ptr, as_ref, 以及rust2021引入的as_deref ,源码虽然都是一行,但是还是要注意很多细节的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    pub const unsafe fn as_mut<'a>(&mut self) -> &'a mut T {
unsafe { &mut *self.as_ptr() }

}
pub const unsafe fn as_ref<'a>(&self) -> &'a T {
unsafe { &*self.as_ptr().cast_const() }
// 实际上就是:
// unsafe {&*self.as_ptr() as *const T}
}

pub const fn as_ptr(self) -> *mut T {
self.pointer as *mut T
}

/// 该函数是Rust中是builtin的,命名为`cast_const`,它用于在不改变类型的情况下改变常量性(constness)。 该函数接受一个指向类型`T`的常量指针`self`作为输入,并返回一个指向类型`T`的常量指针。 它不会在代码重构时默默地改变类型。此外,虽然在大多数情况下,`*mut T`类型可以自动转换为`*const T`类型,但为了与`*const T`上的`cast_mut`函数保持对称性,所以该函数仍然需要(GPT)。 该函数是`const`函数,可以在编译时常量上下文中被评估,也是`inline`函数,总是会被内联到调用处。 该函数的返回类型是`*const T`,其中`T`是函数接受的指针类型`self`所指向的类型。
pub const fn cast_const(self) -> *const T {
self as _
}
  1. 重新复习了 Rust 的异步原语和基本的 tokio runtime.

  2. 对于 智能指针的 leak 方法,之前用的还挺少的。

  3. 参考 dbg! 写了一个 func_name, 使用它来做一些debug,在 no_std 下可能不能正常使用(还没试,有空到二阶段再

1
2
3
4
5
6
7
8
9
10
11
#[macro_export]
macro_rules! func_name {
() => {{
fn f() {}
fn type_name_of<T>(_: T) -> &'static str {
core::any::type_name::<T>()
}
let name = type_name_of(f);
name.strip_suffix("::f").unwrap()
}};
}
  1. 对lifetime的理解一直都不是那么到位。这次稍微注意了一下T: 'static, &'static T的差异

  2. Rust 的 FP 还是很舒服的。

  3. rustlings的 algo1.rs 那道题,没有想到用 safe 的方式怎么实现。那道题里面都是 unsafe, 还在卡顿中。而且我用了 NonNull::read_volatile, 很危险。

没有太多写的总结。一方面是不是很有空,另一方面是这算是一个复习。

二阶段会记录更多的东西。之前之做过 xv6 的labs,因为是时间问题,没有做 JYY OSLab 和 PA我一直觉得很遗憾。作为一个物理学学生,肯定是希望能在二阶段学到更多有趣的东西的。

第二次刷rustlings了,由于这次不用一章一章读《rust程序设计语言》,前面的题刷的比较快。但在后面一部分题慢了下来,主要原因是一些概念,要么现在记不清了,要么本来就没理解透,需要重新看资料。例如:迭代器好像就用了较长时间;第100题虽然参考微信群的提示和题目说明答了上来,但对外部块的引用机制还不是太明白。后面10道算法题主要参考了网上一些算法描述以及rust或其它语言的算法实现,再自己进行修改。从中了解了这些算法的原理,也可以看到别人实现时巧妙的地方。我觉得本次训练营这10道题添加的特别好。
我通过这次刷题,对rust语言的认识进一步加深,尤其是所有权机制。

从公众号文章中看到这次rust OS 夏令营活动, 抱着开阔眼界, 多认识同行的目的, 于是报名参加了。

第一阶段是学习rust 基本语法, 作业是使用rustlings 做110 道题。

我是两年前学习的rust 语言, 这两年也基本上在用rust 做项目,语言基础这块还是比较有信心的。但是做这些题也并没有像砍瓜切菜般,刷刷刷的一晚上搞定,花了两晚上做完100道基础题,又花了两晚上昨晚10道算法题。

主要的原因呢,是这些题目是很全面的,除了future async await 之外,关于rust 的各个方面都涉及到了。
比如 unsafe, 我可以说,写项目快两年,我没有写过一行unsafe,做题的过程也全面复习了一下rust 语言,这也算收获之一吧。

类似的, 我比较少用的方面包括, 自己实现trait, 自己实现macro,

记录一下我遇到的一些比较有趣的问题,或许对新学者有点帮助。很多是群里大家讨论的问题。

statge one, rustlings

重点和非重点(对初学者,应用层)

我学习用到的书是 << programming rust >>,现在有第二版了。

全部基于个人感受,和项目中使用的频率。非重点不代表不重要,只是说,如果你做的项目是偏向应用层的,这些内容需要理解,但很少会自己实现。如果是底层相关的,或者开发rust 库,这些可能会是重点,且常用。

重点:
+ 枚举和表达式, 我常常复习的章节, 真的很常用
+ 迭代器,不用迭代器也能实现各种各样的功能, 但是这样的C/C++ 风格不推荐。迭代器真的很有用,也很好用
+ 闭包,可能很多人第一感觉是很少会用到,但是出人意料的是,闭包还算比较常用。像迭代器一样,rust 推荐使用闭包,对闭包也有专门的性能优化
+ 错误处理,这个是rust 不同与其他很多语言的一个地方,也是很多人不理解的地方。初步处理时可以不用像库作者一样严格,结合枚举和闭包,也可以妥善处理。进阶的处理办法,需要稍微研究一下anyhow 和 thiserror 库。不要 unwrap 了之(重要的话手动乘以三遍)
+ 所有权和生命周期,老生长谈的重点,我就不说了,理解就好

非重点:
+ macro, macro 属于高级技巧, 应用层代码基本不会有自己手写macro 的需求,最多会用别人写好的。对于web 框架这种情形,很多人更喜欢函数实现的库,而非宏实现的库。对于有些不得不使用宏的场景,如DSL,再去学习研究就好了,早期没必要在这儿花很多时间
+ unsafe, 对于写OS 这种场景,unsafe 属于是必备技能了,但是对于普通的应用层,基本用不到。
+ 操作符重载, 知道就好了, 反正我没碰到过一次需要我手写操作符重载的

Copy and Clone

这个问题是群里大家讨论的时候提到的。Copy 是所有权转移的一种例外,实现Copy trait的类型, 赋值和传参数时, 会隐式复制。
参考 << programming rust >> page 71, 4.3 章节, page 236, 13.4 章节。
只说结论, Copy trait 是一种标记特型, 从代码上看 Clone trait 是其父特型,但是这并不意味着需要调用copy 方法的地方,
内部在调用clone 方法。copy 使用的仍然是内存中的按位复制方法。这两个特型之间的关系应该是一种逻辑关系,即可copy 的对象一定都是可clone 的。

unwrap ?

初学者(包括我)都会简单粗暴的使用unwrap,但是写了足够多项目代码之后,才终于明白了unwrap 是啥,到底应该怎么用。
我是在经历过上线的程序突然挂掉,集成第三方库总是莫名其妙的报错之后, debug 到怀疑人生之后, 才终于意识到这个问题的。
+ 结论就是,不要用unwrap, 除非你已经检查过了,能够完全确定这个unwrap 不会报错,然后让你的程序直接挂在这儿。
unwrap 是程序员对rust 程序的一种承诺,我已经检查过了,程序你就大胆的往下执行吧,出错了我也不会怪编译器,不会去问rust 不是号称现代,安全的编程语言吗,为啥会莫名奇妙挂掉了。
初学者往往会滥用unwrap, 在不知道自己已经做出了上述这些承诺的情况下。
函数中如果使用了unwrap, 会有一个标记trait, 标记此函数为非 Infallible, 这样在集成某些第三方库时,由于第三方库接口要求,而导致我们实现的函数不满足第三方trait 的要求,从而导致编译失败。
解决unwrap 滥用的一种常用办法是,使用watch, ? 或map_err 等方法,处理掉每一个 Result/Option 类型。

match expresion too deep nested

使用match 处理 Result 和 Option 类型是常见的操作,但是问题在于这种处理多嵌套了一层,
一不小心就会陷入多重分类讨论,层层嵌套的问题,看不清代码逻辑主干。下面是我应对的一种办法:
  • 一种方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // algorithm4.rs
    fn search(&self, value: T) -> bool {
    match &self.value {
    x if *x == value => { return true; },
    x if *x > value => {
    match &self.left {
    None => false,
    Some(left) => {
    left.search(value)
    }
    }
    },
    x => {
    match &self.right {
    None => false,
    Some(right) => {
    right.search(value)
    }
    }
    }
    }
    }
  • 另一种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
// algorithm4.rs
fn search(&self, value: T) -> bool {
match (&self.value, &self.left, &self.right) {
(root_val, _, _) if *root_val == value => { true },
(root_val, Some(left), _) if value < *root_val => {
left.search(value)
},
(root_val, _, Some(right)) if *root_val < value => {
right.search(value)
},
(_,_,_) => false
}
}

久仰Rust大名, 听说有这么一门语言要撼动C语言的地位, 很难不让人感兴趣. 后面机缘巧合, 研究的课题也与Rust相关, 因此之前就”速成”过Rust. 不过, 毕竟是速成的, 根基不稳… 在公众号里发现了开源操作系统训练营这个活动, 火速加入🤩

原本以为, 之前学过Rust, 也用Rust写过一些玩具项目, 虽然不是啥大佬, 但做Rustlings还不是手到擒来. 然后一个下午就酱紫搭了进去🤡, 不过嘛, 还是收获满满的, 补漏了很多之前不熟悉的概念, 重要的知识点也巩固了许多.

想必没人看我的学习笔记, 所以不妨在此狠狠安利这个活动😏自从加了活动的群, 就不断地被热情的社牛群U震惊, 可以在凌晨两点欢快地讨论, 可以直接和负责上课的老师交流, 可以谈天说地, 还可以欣赏各位大佬编程时花里胡哨的二次元背景🥰当然最重要的是, 真的可以学到东西. 已经在狠狠期待第二阶段的内容了!