A Tour of Rust - Binary Serach Tree
A Tour of Rust - Binary Search Tree
学习Rust已经有一段时间了,最近开始尝试用 Rust 进行一些简单的demo开发进行练手。今天尝试了一下,实现了第一个简单的 Binary Search Tree。必须承认,Rust 的入门曲线确实相当陡峭。本来我的预计是花费2~3小时实现,结果最后花了整整6个小时,比预计的时间超出一倍多。这里记录一下自己的学习心得以及体会。
代码实现
在实现代码的过程中,我使用了两种不同的开发思路:
- 基于传统的命令式编程语言,使用语言提供的各种控制流语句(如
if
,while
)来控制逻辑; - 基于Rust强大额模式匹配控制逻辑;
传统模式
在传统模式下,我们很常规的定义了一个二叉树,和大部分语言不同的是,Rust
并不提倡使用 NULL
指针,而提供了强大的 Option
来为我们实现流程控制。
1 | pub struct Node<T> |
在这种情况下,我们的 add 方法实现也并不复杂,代码如下:
1 | pub fn new(root: T) -> Node<T> { |
Rust模式
Rust模式相当特别,他并不直接显式的使用结构体来表示我们的二叉树节点,而是用了
enum
来代替我们的节点。而在 enum 中,我们嵌套了我们所需要的
struct
以及一个用于替代 NULL
指针的枚举类型
Empty
,这种方式也是在Rust中处理 Cons
list 的一种技巧:
1 | pub enum RsNode<T: Ord> |
实际的插入过程也相当简单:
1 | pub fn new() -> RsNode<T> { |
为什么我会觉得使用Rust模式更加的简单
两种风格的代码实现,其实都相当简单,他们最大的区别也不仅仅在于后面的模式匹配,而是在于一个非常重要的思想:
使用
enum
将空值
作为对象的一部分。
例如,在第一个实现中,如果我们不使用一个工具函数,那么代码将会是这样:
1 | // `self` must be `mut` rather than `&mut` or `&` due to : |
我们内部持有的对象,在每次使用时都必须进行判断。简单来说就是:
如果
空值
是对象的一部分,那么在对象内部的变量,我们就可以直接持有 对象本身 而不是持有Option<T>
,这样我们可以把空值的判断合并到一个流,或者说合并到一个方法,而不是通过Option<T>
对象去间接的访问:下图很好的说明了这点:
- 在传统模式下,
Option<Box<Node>>
并不能直接调用方法add()
,而是要通过Box<Node>
;- 在Rust模式下,
Box<Node>
可以直接调用add()
方法。
--- title: Rust vs. Traditional --- flowchart TD subgraph Rust direction LR subgraph RsNode direction LR subgraph RustNode["Node"] Box2["Box[Node]"]:::front add2["add()"]:::front end Empty end end subgraph Traditional subgraph Node Option1["Option[Box[Node]]"]:::op add["add()"]:::front end Box1["Box[Node]"]:::front end Option1 --> Box1 --> Node classDef front 1,fill:#FFCCCC,stroke:#333; classDef back fill:#969,stroke:#333; classDef op fill:#bbf,stroke:#f66,stroke-width:2px,color:#fff,stroke-dasharray: 5 5 classDef header fill: #696,color: #fff,font-weight: bold,padding: 10px;