学习一门编程语言,主要关注几个方面:
- 基本语法与数据类型:学习如何实现一些简单的功能,此时一般会关注内存模型
- IO类库:学习如何进行字节、字符操作,磁盘操作,网络操作等
- 线程类库:学习如何编写多线程相关的功能
这些功能是一门语言的核心功能,学完这些,就可以去看一些框架了。
所以最开始,先用Rust来写一个链表吧。
啃了大概几十页的《Rust权威指南》之后,依照我多年的java开发经验,我写的链表目前是这样子:
struct Node {
value: u32,
next: Node,
}
struct LinkedList {
head: Node,
size: u32,
}
这是一个异常简单的链表结构,简单到没用泛型,甚至只包含结构体,连方法都没有实现。但是编译器上来就给了我一闷棍:
error[E0072]: recursive type `Node` has infinite size
--> src\main.rs:5:1
|
5 | struct Node {
| ^^^^^^^^^^^ recursive type has infinite size
6 | value: u32,
7 | next: Node,
| ---- recursive without indirection
|
help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to make `Node` representable
|
7 | next: Box<Node>,
| ++++ +
这里是说Node类型具有无限的大小,而 Rust必须在编译时知道每一种类型占据的空间大小 ,对于递归的类型而言,无法在编译时被确定具体大小。
同时,编译器也给出了解决方案,即使用Box来存储指向下一个对象的指针,而不是直接存储对象,由于Box是一种智能指针,具有确定的大小,便能够通过编译。
Box使我们可以将数据存储在堆上,并在栈中保留一个指向堆数据的指针。-- 《Rust权威指南》
修改后的代码如下:
struct Node {
value: u32,
next: Box<Node>,
}
struct LinkedList {
head: Box<Node>,
size: u32,
}
这样修改之后,cargo check能够顺利通过。但在接下来添加初始化方法的过程中出现了问题——由于Rust没有类似于Java语言中的null概念,所以上面的数据结构是无法初始化的,编译器会一直要求你输入下一个Node……
这样的代码在编译器那里是过不去的:
impl LinkedList {
fn new() -> LinkedList {
return LinkedList { head: Nil, size: 0 };
}
}
所以此处还需要用到Option。
Option是Rust标准库中的枚举,用来标识一个值无效或缺失。在标准库中的源码如下:
enum Option<T> (
Some(T),
None,
)
Some 和 None 都仅仅是 Option 这个枚举的变体,并没有什么特殊的意义。但在编码过程中我们可以将None视为类似于空值之类的东西,从而显式地去处理它。
引入Option后,链表结构体长这样:
struct Node {
value: u32,
next: Option<Box<Node>>,
}
struct LinkedList {
head: Option<Box<Node>>,
size: u32,
}
impl LinkedList {
fn new() -> LinkedList {
return LinkedList {
head: None,
size: 0,
};
}
}
cargo check通过,至此,链表的基本结构体算是基本确定下来,但真正的麻烦才刚刚开始,因为接下来要学习编写各种方法。