Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

compound-types/enum #170

giscus[bot] bot announced in Book Comments
Mar 28, 2022 · 42 comments · 12 replies
Discussion options

compound-types/enum

Learning Rust By Practice, narrowing the gap between beginner and skilled-dev with challenging examples, exercises and projects.

https://zh.practice.rs/compound-types/enum.html

You must be logged in to vote

Replies: 42 comments 12 replies

Comment options

第6题这样也可以通过,跟参考答案有不少差距,不是很明白,可以解释一下吗?
1.self不加星可以通过,而 len 函数加了星反而报错,为什么
2.tail不获取引用也没有问题?

// 填空,让代码运行
use crate::List::*;
enum List {
 // Cons: 链表中包含有值的节点,节点是元组类型,第一个元素是节点的值,第二个元素是指向下一个节点的指针
 Cons(u32, Box<List>),
 // Nil: 链表中的最后一个节点,用于说明链表的结束
 Nil,
}
// 为枚举实现一些方法
impl List {
 // 创建空的链表
 fn new() -> List {
 // 因为没有节点,所以直接返回 Nil 节点
 // 枚举成员 Nil 的类型是 List
 Nil
 }
 // 在老的链表前面新增一个节点,并返回新的链表
 fn prepend(self, elem: u32) -> List {
 Cons(elem, Box::new(self))
 }
 // 返回链表的长度
 fn len(&self) -> u32 {
 match self {
 // 这里我们不能拿走 tail 的所有权,因此需要获取它的引用
 Cons(_, tail) => 1 + tail.len(),
 // 空链表的长度为 0
 Nil => 0
 }
 }
 // 返回链表的字符串表现形式,用于打印输出
 fn stringify(&self) -> String {
 match self {
 Cons(head, ref tail) => {
 // 递归生成字符串
 format!("{}, {}", head, tail.stringify())
 },
 Nil => {
 format!("Nil")
 },
 }
 }
}
fn main() {
 // 创建一个新的链表(也是空的)
 let mut list = List::new();
 // 添加一些元素
 list = list.prepend(1);
 list = list.prepend(2);
 list = list.prepend(3);
 // 打印列表的当前状态
 println!("链表的长度是: {}", list.len());
 println!("{}", list.stringify());
}
You must be logged in to vote
6 replies
Comment options

match 不加* ,self就是引用类型:&List, tial也是引用类型。
match 加 *, self就是List类型。tial是Box类型,并没有实现Copy特性,所以不能移动到tial

Comment options

这里我也一直很迷惑, 看了一片文章觉得有一定收获, 还需好好的实践一下才行. 推荐给大家看看 https://medium.com/@robertgrosse/ref-patterns-destructuring-and-invisible-borrows-2f8ae6902656

Comment options

&self即self:&Self的缩写,这样理解会好点

Comment options

fenglea的回复恍然大悟。我之前还在纠结为什么self的类型是&List(我在想那&self的类型是啥...现在看来&self的类型会是&&List)

Comment options

不过为什么当不加 * ,self 是 &List 类型时,解构的 tail 也是 &Box 类型呢,这个"&"会被自动传递下去吗?(可能是个很蠢的问题)

Comment options

Done, 废了些功夫总算是解决了这个问题。很多东西不是很理解,之后估计还有回来再理解一下

You must be logged in to vote
2 replies
Comment options

第3题的意图就是assert失败吗?

Comment options

要成功,利用模式匹配,获取到a b的值,然后判断2个相等

Comment options

链表就是函数式那一套.. 理解了其实很直观, 比用c写简单其实

You must be logged in to vote
0 replies
Comment options

// C语言风格的枚举定义
enum Number2 {
 Zero = 0,
 One = 1,
}
enum Number{
 Zero(u8),
 One(u8),
}

这种直接赋值和元祖申明类型,有什么区别。然后结构体那种不能直接赋值吗?

You must be logged in to vote
0 replies
Comment options

2022年11月15日 Done

You must be logged in to vote
0 replies
Comment options

done,6 not understand

You must be logged in to vote
1 reply
Comment options

done

Comment options

第6题还没打算看,等看完了基础部分再回来试试叭

You must be logged in to vote
0 replies
Comment options

第三题还能修改msg的定义是没想到的。。

You must be logged in to vote
1 reply
Comment options

明明写着仅填空,我以为他是要故意让我panic在那个位置,结果一看答案,他还改了数值🤣

Comment options

mark

You must be logged in to vote
0 replies
Comment options

第四题, 数组的概念还没学...
[Message;3] , 现在数组放最后一小节了

You must be logged in to vote
0 replies
Comment options

第二题的:
// 使用x = 1, y = 2 来初始化
改成:
// 使用x: 1, y: 2 来初始化
好一点?

You must be logged in to vote
0 replies
Comment options

Done。最后一题链表如果没有提供好框架让我们从头开始写的话确实是相当高难度的了。打印链表那里怕读者想不到怎么递归还提示了调用函数,他温我哭。

You must be logged in to vote
0 replies
Comment options

加了几个常用方法,最后pop函数那儿不得以还是引入了Copy特征,但是暂时也想不到别的好办法了,模式匹配可以用Clone特征取值吗?

use std::fmt::Debug;
use crate::List::*;
enum List<T> {
 Cons(T, Box<List<T>>),
 Nil,
}
impl<T> List<T> where T: Debug + Copy {
 fn new() -> Self {
 Nil
 }
 
 fn prepend(self, elem: T) -> Self {
 Cons(elem, Box::new(self)) 
 }
 
 fn append(&mut self, elem: T) {
 match *self {
 Cons(_, ref mut tail) => tail.append(elem),
 Nil => *self = Cons(elem, Box::new(Nil))
 }
 }
 fn is_empty(&self) -> bool {
 if let Nil = *self {
 return true
 }
 return false
 }
 fn pop(&mut self) -> Option<T> {
 if let Cons(elem, ref mut tail) = *self {
 if tail.is_empty() {
 *self = Nil;
 return Some(elem)
 } else {
 tail.pop()
 }
 } else {
 return None
 }
 }
 
 fn front_pop(self) -> (Self, Option<T>) {
 if let Cons(elem, tail) = self { 
 return (*tail, Some(elem))
 } else {
 return (self, None)
 }
 }
 
 fn len(&self) -> usize {
 match *self {
 Cons(_, ref tail) => 1 + tail.len(),
 Nil => 0
 }
 }
 
 fn stringify(&self) -> String {
 match *self {
 Cons(ref head, ref tail) => {
 format!("{:?}->{}", head, tail.stringify()) 
 },
 Nil => {
 format!("Nil")
 },
 }
 }
}
fn main() {
 let mut list: List<u8> = List::new();
 list.append(0_u8);
 list = list.prepend(1_u8);
 list = list.prepend(2_u8);
 list = list.prepend(3_u8);
 println!("{} Len: {}", list.stringify(), list.len());
 list.append(10_u8);
 println!("{} Len: {}", list.stringify(), list.len());
 println!("{} pop out!", list.pop().unwrap());
 println!("{} Len: {}", list.stringify(), list.len());
 
 let (list, opt) = list.front_pop();
 if let Some(elem) = opt {
 println!("{} pop out from front!", elem);
 }
 println!("{} Len: {}", list.stringify(), list.len());
}
You must be logged in to vote
1 reply
Comment options

傻了,忘了可以用引用做clone了。。。

 fn pop(&mut self) -> Option<T> {
 if let Cons(ref elem, ref mut tail) = *self {
 if tail.is_empty() {
 let elem_clone = elem.clone();
 *self = Nil;
 return Some(elem_clone)
 } else {
 tail.pop()
 }
 } else {
 return None
 }
 }
Comment options

3,5 题出现了第八节的 if let, 第 6 题出现 impl, 这些还没学过. 虽然去搜资料能够完成现在的题目, 但是整个过程很下头. 思绪被某些东西弄乱了.

You must be logged in to vote
0 replies
Comment options

虽然没有完全理解这个链表,但是跟着注释,竟然一次就全对了

You must be logged in to vote
0 replies
Comment options

第一题编译器是怎么知道Number::One是1呢?

You must be logged in to vote
0 replies
Comment options

done~~~

You must be logged in to vote
0 replies
Comment options

尝试写了写 append pop_front pop_back

enum MyList {
 Node(i32, Box<MyList>),
 Null,
}
impl MyList {
 fn new() -> MyList {
 MyList::Null
 }
 fn size(&self) -> u32 {
 match self {
 Self::Null => 0,
 Self::Node(_, ref next) => next.size() + 1,
 }
 }
 fn stringify(&self) -> String {
 match self {
 Self::Null => String::from("Null"),
 Self::Node(n, next) => format!("{}->{}", n, next.stringify()),
 }
 }
 fn print(&self) {
 match self {
 Self::Null => println!("len={}, list={}", self.size(), self.stringify()),
 Self::Node(_, _) => println!(
 "len={}, front={}, back={}, list={}",
 self.size(),
 self.front().unwrap(),
 self.back().unwrap(),
 self.stringify()
 ),
 }
 }
 fn front(&self) -> Option<i32> {
 match self {
 Self::Null => None,
 Self::Node(num, _) => Some(*num),
 }
 }
 fn back(&self) -> Option<i32> {
 match self {
 Self::Null => None,
 Self::Node(num, next) => match **next {
 Self::Null => Some(*num),
 Self::Node(_, _) => next.back(),
 },
 }
 }
 fn preppend(self, num: i32) -> MyList {
 Self::Node(num, Box::new(self))
 }
 fn pop_front(self) -> MyList {
 match self {
 Self::Null => self,
 Self::Node(_, next) => *next,
 }
 }
 fn append(&mut self, num: i32) {
 match self {
 Self::Null => *self = Self::Node(num, Box::new(Self::Null)),
 Self::Node(_, next) => next.append(num),
 }
 }
 fn pop_back(&mut self) {
 if let Self::Node(_, next) = self {
 match **next {
 Self::Null => *self = Self::Null,
 Self::Node(_, _) => next.pop_back(),
 }
 }
 }
}
fn main() {
 let mut list = MyList::new();
 list.print();
 list = list.preppend(1);
 list = list.preppend(2);
 list = list.preppend(3);
 list.print();
 list.append(4);
 list.append(5);
 list.append(6);
 list.print();
 list = list.pop_front();
 list = list.pop_front();
 list.print();
 list.pop_back();
 list.pop_back();
 list.print();
}
You must be logged in to vote
0 replies
Comment options

mark finished

You must be logged in to vote
0 replies
Comment options

done

You must be logged in to vote
0 replies
Comment options

if let Message::Quit = msg {
 assert_eq!(1, 1);
}

第三题这段if条件判断的是msg 是否是 Message::Quit 变体。如果是,它会执行大括号内的代码块。
和assert_eq是否成功无关

You must be logged in to vote
0 replies
Comment options

#6 第六题得消化一下

You must be logged in to vote
0 replies
Comment options

好难,看不懂了

You must be logged in to vote
0 replies
Comment options

done

You must be logged in to vote
0 replies
Comment options

done day-7

You must be logged in to vote
0 replies
Comment options

Mark: 第一题怪怪的

You must be logged in to vote
0 replies
Comment options

第六题是不是超纲了 🤯

You must be logged in to vote
1 reply
Comment options

没有。只是看起来很复杂,认真读一下代码其实很简单(前提是会其他语言)。

Comment options

照猫画虎实现了一个expand方法

 // expand 的一个比较低效的实现
 // 老的链表被整体拷贝一份并在末尾添加新节点
 // fn expend(self, elem: u32) -> List {
 // match self {
 // Cons(data, head) => {
 // Cons(data, Box::new(head.expend(elem)))
 // }
 // Nil => Cons(elem, Box::new(self))
 // }
 // }
 // expand似乎高效一些的实现,不再需要对整个链表的节点进行拷贝
 fn expend(&mut self, elem: u32) {
 match self {
 Cons(data, next) => {
 next.expend(elem)
 },
 Nil => {
 *self = Cons(elem, Box::new(Nil))
 }
 }
 }

以下是对self、&self以及自己摸索的 &mut self的一点理解,掺杂了一些C++学习中的感受
fn prepend(self, elem: u32)
第一个参数写作 self,其实可以理解为
在任何List变量调用prepend方法时,都用自身作为第一个参数

{
 let x: List = List::new();
 x = prepend(x, 1);
}

这时自身的所有权已经交出,则当前变量在函数返回后即失效
因此 prepend() -> List 返回新的List值
需要将新的值绑定到变量上.
fn len(&self) -> u32
第一个参数写作 &self,可以理解为

{
 let x: List = List::new();
 let length = len(&x);
}

这意味着调用len方法时,取变量自身的不可变引用做参数
不交出所有权,且不改变原始变量
fn expend(&mut self, elem: u32)
第一个参数写作&mut self,意味着使用变量本身的可变引用作为参数
且不返回任何值

{
 let x: List = List::new();
 expand(&mut x, 1);
}

这里获取变量自身的可变引用做参数,仍然不获取所有权
但是当最后遇到链表结尾时,可以改变结尾处的List变量插入新节点
这个方法是我照猫画虎实现的,不太拿的准,
希望以后能想起来再回来看看大家的见解

You must be logged in to vote
0 replies
Comment options

done

You must be logged in to vote
0 replies
Comment options

Done

You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

AltStyle によって変換されたページ (->オリジナル) /