14|类型系统:有哪些必须掌握的trait?
该思维导图由 AI 生成,仅供参考
trait
- 深入了解
- 翻译
- 解释
- 总结
Rust语言中的trait是非常强大的特性,能够让代码结构具有很好的扩展性,使系统变得非常灵活。本文深入介绍了一些必须掌握的trait,包括Clone、Copy、Read、Write、Iterator、Debug、Default、From和TryFrom等。通过深入研究这些trait的定义和使用场景,读者可以更好地理解Rust语言的设计理念,并写出更加优雅的Rust代码。文章还探讨了Drop trait在内存管理中的应用,以及标记traitSized、Send、Sync和Unpin的作用。通过本文的阅读,读者可以深入了解Rust语言中trait的重要性和灵活性,为编写高效、安全的Rust代码提供了重要的指导和启发。同时,文章还提到了Send/Sync trait在并发安全中的基础作用,以及类型转换相关的traitFrom\<T> / Into\<T>/AsRef\<T> / AsMut\<T>的使用方法。通过合理地使用这些trait,读者可以让代码变得简洁,符合Rust可读性强的风格,更符合开闭原则。
《陈天 · Rust 编程第一课》,新⼈⾸单¥68
全部留言(34)
- 最新
- 精选
- 记事本老师,你讲得太通透,太详细了,太负责任了,全网最好的教程了
作者回复: 谢谢夸奖!
2021-09-2531 - c4f1. 不行。因为 Vec 和 Copy 都不是用户自己 crate 中定义的,所以根据孤儿原则无法为 Vec 实现 Copy trait 2. 因为 Arc 实现了 Deref 和 DerefMut trait,解应用可以直接访问内部的 Mutex 3. 实现的时候遇到了一个问题:对于非法的 index (比如测试用例中的 128)该如何返回,没找到解决方法于是只针对 List<u32> 实现了 Index trait,这样在遇到非法 index 时返回 &0 即可。 针对 Vec 测试了一下非法 index 的情形,发现会直接终止进程。具体代码如下 ```rust fn index(&self, index: isize) -> &Self::Output { // todo!(); if let Some(idx_abs) = if index >= 0 { Some(index as usize) } else { self.len().checked_sub(index.abs() as usize) } { let mut iter = self.iter(); for _i in 0..idx_abs { iter.next(); } iter.next().unwrap_or(&0) } else { &0 } } ```
作者回复: 很棒! 对于 3,你可以看我的参考代码: ```Rust impl<T> Index<isize> for List<T> { type Output = T; fn index(&self, index: isize) -> &Self::Output { let len = self.len() as isize; // 我们需要先对负数,以及 index 超出范围的数字进行处理 // 使其落在 0..len 之间 let n = (len + index % len) % len; let iter = self.iter(); // 由于 n 一定小于 len 所以这里可以 skip n 取 next // 此时一定有值,所以可以放心 unwrap iter.skip(n as usize).next().unwrap() } } ``` playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9f354aaed64c4b97f3b80c3be9c4b59a
2021-09-24416 - noisyes刚才的小例子中要额外说明一下的是,如果你的代码出现 v.as_ref().clone() 这样的语句,也就是说你要对 v 进行引用转换,然后又得到了拥有所有权的值,那么你应该实现 From,然后做 v.into()。 这句话怎么理解呀
作者回复: 就是如果你获得某个类型T 到其它类型的引用 U,然后又把这个引用 U clone 出一个 U 的带所有权的数据。那么为何不直接实现 From<T> for U 呢?
2021-09-2711 - 彭亚伦第3题, 同样使用标准库的2个方法, `checked_rem_euclid`取得合理索引值, `iter().nth()`获得实际值 ```rust use std::{ collections::LinkedList, ops::{Deref, DerefMut, Index}, }; struct List<T>(LinkedList<T>); impl<T> Deref for List<T> { type Target = LinkedList<T>; fn deref(&self) -> &Self::Target { &self.0 } } impl<T> DerefMut for List<T> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } impl<T> Default for List<T> { fn default() -> Self { Self(Default::default()) } } impl<T> Index<isize> for List<T> { type Output = T; fn index(&self, index: isize) -> &Self::Output { let len = self.0.len(); //标准库的checked_rem_euclid方法, 如果len=0 则返回None //这里直接把i进行unwrap, 如果链表长度不为0, 则i一定在0..len范围内, 可以放心使用, //如果长度为零, 意味这对一个空链表进行索引, 那么我panic应该也是合情合理的吧 let i = (index as usize).checked_rem_euclid(len).unwrap(); &self.0.iter().nth(i).unwrap() } } #[test] fn it_works() { let mut list: List<u32> = List::default(); for i in 0..16 { list.push_back(i); } assert_eq!(list[0], 0); assert_eq!(list[5], 5); assert_eq!(list[15], 15); assert_eq!(list[16], 0); assert_eq!(list[-1], 15); assert_eq!(list[128], 0); assert_eq!(list[-128], 0); } ``` playground 链接: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=73b4129f1a6608892691da92d501ba15
作者回复: 👍
2021-11-1734 - 核桃老师,这里有一些概念没搞清晰。 1. * 这个符号,有时表示解引用,有时表示获取变量地址的值,对吗?有点搞混场景了。 2. trait继承这里,经常看到一句话,组合优于继承,怎么理解。同时对于实现和继承来说,可能基础不扎实,一直没理解好什么时候继承什么时候实现,学java的时候,那些抽象类和接口也迷糊的很。 另外这里隐藏了很多东西,看老师代码的时候经常用unwrap,其实生产环境代码是非常危险的。例如今天写hashmap替换里面内容时,最好用containkeys判断一下,如果没有则插入一个空的,再使用get_mut和unwarp,这样就保证安全不会panic了。
作者回复: 1. * 代表解引用,&是获取变量引用(地址) 2. trait "继承" 是打引号的继承,只是 trait A "继承" trait B 的方法(和关联类型),是行为的继承,也是一种组合,和面向对象继承的概念是不一样的,它们之间并无数据的继承,也没有类别继承的关系。 3. unwrap() 在示例代码中会常常用到,我在之前的内容中介绍过这样会导致 panic,在生产环境中的代码除非你在上下文中确保它不会 panic,否则不宜使用。
2021-10-274 - 周烨1. 不能,因为不能确定T是否实现了Copy trait。 2. 因为Arc实现了Deref trait 3. ```impl<T> Index<isize> for List<T> { type Output = T; fn index(&self, index: isize) -> &Self::Output { let len = self.len() as isize; let i = if index % len >= 0 { index % len } else { len + index % len } as usize; let it = self.iter(); return it.skip(i).next().unwrap(); } }```
作者回复: 正确!
2021-10-0834 - 夏洛克Moriatylet a = *list; let b = list.deref(); 老师请问下这两种方式有什么区别,为什么a和b的类型不同?
作者回复: 区别是: let a = &*list; let b = list.deref(); // a == b 注意看 deref 的返回值。
2021-09-2632 - GE1. 不能,但是这里和T关系无关,而是因为Vec本身已经实现了Drop trait,所以和Copy trait是冲突的 ``` // source code unsafe impl<#[may_dangle] T, A: Allocator> Drop for Vec<T, A> ```
作者回复: 👍
2022-01-041 - Taozi第三题里面给List<T>实现DerefMut时,为什么不需要加type Target = LinkedList,那返回的Self::Target是什么。
作者回复: DerefMut 依赖 Deref,也就是说要实现 DerefMut 必须县实现 Deref,所以 Target 就复用了。
2021-09-2631 - 0@1老师,想提前问下unsafe相关的问题,这个目前比较困扰我进一步学习Rust. 比如这个 std::mem::forget(t), 看了下源码,是直接调用 ManuallyDrop::new(t), 看文档,好像这2个又不是直接等价的。forget的源码如下,多了些属性宏修饰,编译器是不是多加了处理,从而跟直接调用 ManuallyDrop::new(t)起到的效果不一样? 如果是的话,这些宏的文档在哪里可以看到,类似的这些编译器处理的宏有哪些,他们的文档在哪里,谢谢。 Note: 我学rust陆续2年了,看源码时对这些需要编译器额外处理的东西比较困惑,不知道如何去进一步的理解他们, rust中很多隐含规则貌似都有他们的影子。 #[inline] #[rustc_const_stable(feature = "const_forget", since = "1.46.0")] #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "mem_forget")] pub const fn forget<T>(t: T) { let _ = ManuallyDrop::new(t); }
作者回复: 这里没有额外的 secret,forget 和 ManuallyDrop::new() 不同的地方是一个有返回值,一个没有返回值。文档中的例子也是使用这一点的不同来表示它们不同的使用场景的。你可以再仔细看看两个例子调用 forget 和 ManuallyDrop::new() 的位置的不同,想想为什么使用 forget 不能一开始就调用。:)
2021-09-2621