2024-04-27
在RUST中文社区看到这个活动,从组队到因停电用手机参加开营仪式,到每周一三五参加线上课程学习。
因之前用写过rust程序,有一定基础,但写rustlings还是有一定难度,对泛型、生命周期以及智能指针
还有许多知识要加强
rust相关的问题
rust标准库中重名方法太多,要熟悉常用的数据类型的方法
实现堆栈
以前没实现过堆栈,对此没有概念,加上用rust写堆栈繁琐,这方面要多补充
fn average(values: &[f64]) -> f64 {
let total = values.iter().sum::<f64>();
total / values.len() as f64
}
struct Person{}
impl From<&str> for Person {
fn from(s: &str) -> Person {
}
}
// then you can use
let p = Person::from("Mark,20");
//可以自定义错误
impl FromStr for Person {
type Err = ParsePersonError;
fn from_str(s: &str) -> Result<Person, Self::Err> {
}
}
// 与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)
https://doc.rust-lang.org/std/convert/trait.AsRef.html
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() }
}
}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)
}
}&[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,
)
};
}
/// # 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
}
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);
//提供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/
**
因为本科期间用 Rust2021 比用c++20更多,所以对Rust的基本用法和tricks比较熟。所以最近时间更多用来做毕设。快到一阶段DDL时开始做 rustlings. 和 quiz.
as_mut, as_ptr, as_ref, 以及rust2021引入的as_deref ,源码虽然都是一行,但是还是要注意很多细节的:1 | pub const unsafe fn as_mut<'a>(&mut self) -> &'a mut T { |
重新复习了 Rust 的异步原语和基本的 tokio runtime.
对于 智能指针的 leak 方法,之前用的还挺少的。
参考 dbg! 写了一个 func_name, 使用它来做一些debug,在 no_std 下可能不能正常使用(还没试,有空到二阶段再
1 | #[macro_export] |
对lifetime的理解一直都不是那么到位。这次稍微注意了一下T: 'static, &'static T的差异
Rust 的 FP 还是很舒服的。
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,
记录一下我遇到的一些比较有趣的问题,或许对新学者有点帮助。很多是群里大家讨论的问题。
我学习用到的书是 << programming rust >>,现在有第二版了。
全部基于个人感受,和项目中使用的频率。非重点不代表不重要,只是说,如果你做的项目是偏向应用层的,这些内容需要理解,但很少会自己实现。如果是底层相关的,或者开发rust 库,这些可能会是重点,且常用。
重点:
+ 枚举和表达式, 我常常复习的章节, 真的很常用
+ 迭代器,不用迭代器也能实现各种各样的功能, 但是这样的C/C++ 风格不推荐。迭代器真的很有用,也很好用
+ 闭包,可能很多人第一感觉是很少会用到,但是出人意料的是,闭包还算比较常用。像迭代器一样,rust 推荐使用闭包,对闭包也有专门的性能优化
+ 错误处理,这个是rust 不同与其他很多语言的一个地方,也是很多人不理解的地方。初步处理时可以不用像库作者一样严格,结合枚举和闭包,也可以妥善处理。进阶的处理办法,需要稍微研究一下anyhow 和 thiserror 库。不要 unwrap 了之(重要的话手动乘以三遍)
+ 所有权和生命周期,老生长谈的重点,我就不说了,理解就好
非重点:
+ macro, macro 属于高级技巧, 应用层代码基本不会有自己手写macro 的需求,最多会用别人写好的。对于web 框架这种情形,很多人更喜欢函数实现的库,而非宏实现的库。对于有些不得不使用宏的场景,如DSL,再去学习研究就好了,早期没必要在这儿花很多时间
+ unsafe, 对于写OS 这种场景,unsafe 属于是必备技能了,但是对于普通的应用层,基本用不到。
+ 操作符重载, 知道就好了, 反正我没碰到过一次需要我手写操作符重载的
这个问题是群里大家讨论的时候提到的。Copy 是所有权转移的一种例外,实现Copy trait的类型, 赋值和传参数时, 会隐式复制。
参考 << programming rust >> page 71, 4.3 章节, page 236, 13.4 章节。
只说结论, Copy trait 是一种标记特型, 从代码上看 Clone trait 是其父特型,但是这并不意味着需要调用copy 方法的地方,
内部在调用clone 方法。copy 使用的仍然是内存中的按位复制方法。这两个特型之间的关系应该是一种逻辑关系,即可copy 的对象一定都是可clone 的。初学者(包括我)都会简单粗暴的使用unwrap,但是写了足够多项目代码之后,才终于明白了unwrap 是啥,到底应该怎么用。
我是在经历过上线的程序突然挂掉,集成第三方库总是莫名其妙的报错之后, debug 到怀疑人生之后, 才终于意识到这个问题的。
+ 结论就是,不要用unwrap, 除非你已经检查过了,能够完全确定这个unwrap 不会报错,然后让你的程序直接挂在这儿。
unwrap 是程序员对rust 程序的一种承诺,我已经检查过了,程序你就大胆的往下执行吧,出错了我也不会怪编译器,不会去问rust 不是号称现代,安全的编程语言吗,为啥会莫名奇妙挂掉了。
初学者往往会滥用unwrap, 在不知道自己已经做出了上述这些承诺的情况下。
函数中如果使用了unwrap, 会有一个标记trait, 标记此函数为非 Infallible, 这样在集成某些第三方库时,由于第三方库接口要求,而导致我们实现的函数不满足第三方trait 的要求,从而导致编译失败。
解决unwrap 滥用的一种常用办法是,使用watch, ? 或map_err 等方法,处理掉每一个 Result/Option 类型。使用match 处理 Result 和 Option 类型是常见的操作,但是问题在于这种处理多嵌套了一层,
一不小心就会陷入多重分类讨论,层层嵌套的问题,看不清代码逻辑主干。下面是我应对的一种办法:一种方式
1 | // algorithm4.rs |
另一种方式
1 | // algorithm4.rs |
久仰Rust大名, 听说有这么一门语言要撼动C语言的地位, 很难不让人感兴趣. 后面机缘巧合, 研究的课题也与Rust相关, 因此之前就”速成”过Rust. 不过, 毕竟是速成的, 根基不稳… 在公众号里发现了开源操作系统训练营这个活动, 火速加入🤩
原本以为, 之前学过Rust, 也用Rust写过一些玩具项目, 虽然不是啥大佬, 但做Rustlings还不是手到擒来. 然后一个下午就酱紫搭了进去🤡, 不过嘛, 还是收获满满的, 补漏了很多之前不熟悉的概念, 重要的知识点也巩固了许多.
想必没人看我的学习笔记, 所以不妨在此狠狠安利这个活动😏自从加了活动的群, 就不断地被热情的社牛群U震惊, 可以在凌晨两点欢快地讨论, 可以直接和负责上课的老师交流, 可以谈天说地, 还可以欣赏各位大佬编程时花里胡哨的二次元背景🥰当然最重要的是, 真的可以学到东西. 已经在狠狠期待第二阶段的内容了!
在2024年春夏季训练营中,我专注于通过Rustling项目来学习Rust编程语言,并在这个过程中获得了丰富的经验和成就:
深入理解Rust语法: 通过逐个完成Rustling项目中的练习,我深入理解了Rust语言的各种语法和特性。从简单的变量绑定到复杂的模式匹配,我对Rust的语法规则有了全面的了解。
掌握Rust生态系统: 在完成练习的过程中,我不仅熟悉了Rust的核心语言特性,还接触了一些常用的标准库和常见的编程模式。这让我对Rust的生态系统有了更清晰的认识,为将来的项目开发打下了坚实的基础。
解决问题的能力: 每个Rustling练习都是一个小问题,通过自己的努力和思考,我逐步解决了这些问题。在这个过程中,我培养了解决问题的能力,包括分析问题、查找文档、调试代码等技能。
持续学习的动力: 完成一个个Rustling练习的过程中,我不断获得成就感和满足感,这激发了我持续学习的动力。我意识到学习是一个持续的过程,而Rustling项目只是我学习之旅的起点。
扩展视野: 通过Rustling项目,我不仅学会了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,是个不错的学习资料,有时间的可以去看看