• 记事本
    2021-09-25
    老师,你讲得太通透,太详细了,太负责任了,全网最好的教程了

    作者回复: 谢谢夸奖!

    
    27
  • c4f
    2021-09-24
    1. 不行。因为 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

    共 4 条评论
    16
  • noisyes
    2021-09-27
    刚才的小例子中要额外说明一下的是,如果你的代码出现 v.as_ref().clone() 这样的语句,也就是说你要对 v 进行引用转换,然后又得到了拥有所有权的值,那么你应该实现 From,然后做 v.into()。 这句话怎么理解呀

    作者回复: 就是如果你获得某个类型T 到其它类型的引用 U,然后又把这个引用 U clone 出一个 U 的带所有权的数据。那么为何不直接实现 From<T> for U 呢?

    
    10
  • 彭亚伦
    2021-11-17
    第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
    展开

    作者回复: 👍

    共 3 条评论
    4
  • 核桃
    2021-10-27
    老师,这里有一些概念没搞清晰。 1. * 这个符号,有时表示解引用,有时表示获取变量地址的值,对吗?有点搞混场景了。 2. trait继承这里,经常看到一句话,组合优于继承,怎么理解。同时对于实现和继承来说,可能基础不扎实,一直没理解好什么时候继承什么时候实现,学java的时候,那些抽象类和接口也迷糊的很。 另外这里隐藏了很多东西,看老师代码的时候经常用unwrap,其实生产环境代码是非常危险的。例如今天写hashmap替换里面内容时,最好用containkeys判断一下,如果没有则插入一个空的,再使用get_mut和unwarp,这样就保证安全不会panic了。

    作者回复: 1. * 代表解引用,&是获取变量引用(地址) 2. trait "继承" 是打引号的继承,只是 trait A "继承" trait B 的方法(和关联类型),是行为的继承,也是一种组合,和面向对象继承的概念是不一样的,它们之间并无数据的继承,也没有类别继承的关系。 3. unwrap() 在示例代码中会常常用到,我在之前的内容中介绍过这样会导致 panic,在生产环境中的代码除非你在上下文中确保它不会 panic,否则不宜使用。

    
    4
  • 周烨
    2021-10-08
    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(); } }```

    作者回复: 正确!

    共 3 条评论
    3
  • 夏洛克Moriaty
    2021-09-26
    let a = *list; let b = list.deref(); 老师请问下这两种方式有什么区别,为什么a和b的类型不同?

    作者回复: 区别是: let a = &*list; let b = list.deref(); // a == b 注意看 deref 的返回值。

    共 3 条评论
    2
  • Taozi
    2021-09-26
    第三题里面给List<T>实现DerefMut时,为什么不需要加type Target = LinkedList,那返回的Self::Target是什么。

    作者回复: DerefMut 依赖 Deref,也就是说要实现 DerefMut 必须县实现 Deref,所以 Target 就复用了。

    共 3 条评论
    1
  • 0@1
    2021-09-26
    老师,想提前问下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 不能一开始就调用。:)

    共 2 条评论
    1
  • GE
    2022-01-04
    1. 不能,但是这里和T关系无关,而是因为Vec本身已经实现了Drop trait,所以和Copy trait是冲突的 ``` // source code unsafe impl<#[may_dangle] T, A: Allocator> Drop for Vec<T, A> ```

    作者回复: 👍

    
    