0%

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震惊, 可以在凌晨两点欢快地讨论, 可以直接和负责上课的老师交流, 可以谈天说地, 还可以欣赏各位大佬编程时花里胡哨的二次元背景🥰当然最重要的是, 真的可以学到东西. 已经在狠狠期待第二阶段的内容了!

Rust

虽然 Rust 语言非第一次接触了,但 Rustlings 是我第一次接触到这种奇妙的交互式训练模式。
通过一百多题给人带来了一个 Rust 的初步印象,后面 10 题甚至还涉及到了一些数据结构与算法。

Rustlings 巩固了我的一部分 Rust 的基础,也很好奇在后续的学习中会接触到更多的新事物。

展望

希望我能把三阶段给坚持下来,因为大佬实在太多了,我怕我坚持不下来跟不上。

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

半年前就已经听说过该训练营了,可惜错过了报名时间。

只好提前花时间为这次训练营做准备了,这次终于可以和大家一起学习,也是非常开心。

实验

rustlings里面的题目绝大部分还是挺容易的,只是涉及了一些语法。只有最后10题有难度,我有几题也是靠gpt帮忙才完成的。唉😔,怪我太菜了,还是得继续学rust啊!

总结

因为有提前学习rust,所以rustlings也是很平稳的通过了。

rustlings这个学习项目非常适合入门rust语言,给我一种很惊艳的感觉。可惜其他语言没有类似的项目, 用来入门新语言真的非常不错。

我的Rustling学习总结:

在2024年春夏季训练营中,我专注于通过Rustling项目来学习Rust编程语言,并在这个过程中获得了丰富的经验和成就:

  1. 深入理解Rust语法: 通过逐个完成Rustling项目中的练习,我深入理解了Rust语言的各种语法和特性。从简单的变量绑定到复杂的模式匹配,我对Rust的语法规则有了全面的了解。

  2. 掌握Rust生态系统: 在完成练习的过程中,我不仅熟悉了Rust的核心语言特性,还接触了一些常用的标准库和常见的编程模式。这让我对Rust的生态系统有了更清晰的认识,为将来的项目开发打下了坚实的基础。

  3. 解决问题的能力: 每个Rustling练习都是一个小问题,通过自己的努力和思考,我逐步解决了这些问题。在这个过程中,我培养了解决问题的能力,包括分析问题、查找文档、调试代码等技能。

  4. 持续学习的动力: 完成一个个Rustling练习的过程中,我不断获得成就感和满足感,这激发了我持续学习的动力。我意识到学习是一个持续的过程,而Rustling项目只是我学习之旅的起点。

  5. 扩展视野: 通过Rustling项目,我不仅学会了Rust语言本身,还接触了更广泛的编程概念和方法论。这让我对软件开发的整体流程和原则有了更清晰的认识,为我未来的职业发展提供了宝贵的经验。

下一步计划:

  • 实践项目开发: 我计划通过参与实际的Rust项目开发,将所学的知识应用到实践中,提升自己的实战经验和技能水平。
  • 深入学习领域知识: 我打算进一步深入学习Rust语言的高级特性和相关领域的知识,比如并发编程、网络编程等。
  • 持续贡献社区: 我希望能够积极参与Rust社区的活动,为社区做出自己的贡献,与其他Rust开发者分享经验和成果。

通过Rustling项目的学习,我不仅掌握了Rust编程语言的基础知识,还培养了解决问题和持续学习的能力,这将为我未来的发展和成长奠定坚实的基础。

前言

之前就从网上看到过训练营的信息,一直很想参加,今年看到报名后果断就参加了.第一次接触rust是前年跳槽来到新公司,当时我还是一名gopher,整天沉迷于go的简单和高性能,觉得写go真的好爽,入职后才知道主要是go和rust,而且rust是主要开发语言,遂转入rust的怀抱,不得不说入门rust真的蛮难的,即使现在我还是觉得自己就是个入门水平,当然,之前从go中学到的知识又不少可以类比到rust帮助自己学习,比如tokio就借鉴了很多go runtime的设计.好了,不多说了,下面是我的一阶段总结

一阶段总结

一阶段主要是rust的基础,对我自身而言实际上就是又复习了一遍rust的学习之路,由于工作语言的原因,对于rust的所有权、生命周期、Trait、async\await等机制早就被ra调教的很熟了,所以这些部分基本上下班写了写就完成了,对于剩下的cargo env、算法等部分,基本就是靠查book、chatgpt来辅助完成的,整体而言难度还可以,新手花点时间用心看book基本都能完成的,对于后续的二阶段,我还在持续学习,希望可以顺利通过二阶段!哦,对了,参加训练营之前正好又刷了一遍phil-opp的blog-os,是个不错的学习资料,有时间的可以去看看