@@ -11,4 +11,170 @@ tags:
11
11
- rust
12
12
---
13
13
14
- Awaiting translation.
14
+ 自我上一篇關於 Rust 的文章已有一段時日了,而且它沒有談到 Rust 的理念與原理,因此這篇文章會是一個對於已有 C 系列語言經驗的工程師而言的 Rust 簡介,再者,
15
+ 如果你不知道二元搜尋樹(BST)的話,我強烈建議先去查查。
16
+
17
+ # 所有權
18
+
19
+ 在實作 BST 前,我們必須解釋所有權,在 Rust 中,物件必須由某個東西擁有,而且只能是那個東西擁有,舉例來說,對於無法複製型別` A ` ,下列程式碼會產生錯誤。
20
+
21
+ ``` rust
22
+ let x : A = ... ; // ...由x擁有
23
+ let y = x ; // 轉移到y
24
+ x ... ; // 錯誤
25
+ ```
26
+
27
+ 因此要使用` x ` 而且不讓` x ` 失去擁有權會用到「借用」,分成兩類:分享參考(&)與獨有參考(可變參考、&mut),前者是一個指向常數不可變記憶的指標,
28
+ 後者指向可變記憶指標。對於` x ` ,在同個變數範圍,最多只能有一個獨有參考,而且如果有獨有參考就不能有分享參考。
29
+
30
+ # 節點與葉子
31
+
32
+ 在 C,我們可以將節點定義為:
33
+
34
+ ``` c
35
+ struct node {
36
+ int key;
37
+ struct node * left, * right;
38
+ };
39
+ ```
40
+
41
+ 一個` node ` 有兩個指標指向其他` node ` ,注意這些其他節點的記憶分配沒有被具體地定義出來,如果左或右節點是空的,那其值會被設為` (struct node*)NULL ` ,
42
+ 這方法有些缺點,最主要是你所分配的堆記憶(heap memory)需要手動清理,不然會造成記憶外洩。
43
+
44
+ 在 Rust,由於一個節點只能由父母存取,而且知道它的父母無關上下文,我們可以定義為此:
45
+
46
+ ``` rust
47
+ struct BSTNode <T > {
48
+ left : BST <T >,
49
+ right : BST <T >,
50
+ pub key : i32 ,
51
+ pub val : T ,
52
+ }
53
+
54
+ pub struct BST <T > {
55
+ node : Option <Box <BSTNode <T >>>,
56
+ }
57
+ ```
58
+
59
+ 我們顯然定義了一個間接架構` BST<T> ` ,用來裝在可有可無的` Box<node> ` 。一個` Box ` 是一個指向特定堆記憶的獨特指標,如果沒有人指向、擁有` Box ` ,
60
+ 則此記憶會被清理,如果` node ` 這個性質是` None ` ,那 BST 就是單純的空樹,除此之外,有些人會這樣寫:
61
+
62
+ ``` rust
63
+ pub struct BST <T > {
64
+ left : Option <Box <BST <T >>>,
65
+ right : Option <Box <BST <T >>>,
66
+ pub key : i32 ,
67
+ pub val : T ,
68
+ }
69
+ ```
70
+
71
+ ,精簡化程式碼,它是可行的,不過你需要在頂層使用` Option ` 來表示空樹,因此我們在此文章會使用第一個實作。接下來,我們來定義幾個簡單的功能。
72
+
73
+ ``` rust
74
+ impl <T > BST <T > {
75
+ // an empty BST
76
+ pub fn new_empty () -> Self {
77
+ Self { node : None }
78
+ }
79
+ // a BST with one node
80
+ pub fn new (key : i32 , val : T ) -> Self {
81
+ Self {
82
+ node : Some (Box :: new (BSTNode :: new (key , val ))),
83
+ }
84
+ }
85
+ pub fn is_empty (& self ) -> bool {
86
+ self . node. is_none ()
87
+ }
88
+ pub fn insert (& mut self , key : i32 , val : T ) {
89
+ if let Some (root ) = & mut self . node {
90
+ // match is a way to split and pattern match results
91
+ match root . key. cmp (& key ) {
92
+ Ordering :: Equal => {
93
+ panic! (" BST should not have same id!" );
94
+ // bad practice ^
95
+ }
96
+ Ordering :: Greater => { // root.key > key
97
+ root . left. insert (key , val );
98
+ }
99
+ Ordering :: Less => {
100
+ root . right. insert (key , val );
101
+ }
102
+ }
103
+ } else {
104
+ self . node = Some (Box :: new (BSTNode :: new (key , val )));
105
+ }
106
+ }
107
+ }
108
+ ```
109
+
110
+ 此處,` Box::new ` 是分配堆記憶的方法。
111
+
112
+ # 二元搜尋樹刪除
113
+
114
+ 接下來,我們來定義 BST 刪除節點的方法。要刪除節點` n ` ,我們會分開討論:
115
+
116
+ 1 . ` n ` 是葉子:使` n ` 成為空樹。
117
+ 2 . ` n ` 只有一個孩子:使孩子取代` n ` 。
118
+ 3 . ` n ` 有兩個孩子:使` n ` 的中敘式相鄰後者取代` n ` ,並不改變原先` n ` 與孩子的連結。
119
+
120
+ 接下來,把想法寫出來。
121
+
122
+ ``` rust
123
+ impl <T : Clone > BST <T > {
124
+ fn extract_min (& mut self ) -> Option <Box <BSTNode <T >>> {
125
+ let Some (root ) = & mut self . node {
126
+ return None ;
127
+ };
128
+
129
+ if ! root . left. is_empty () {
130
+ return root . left. extract_min ();
131
+ }
132
+
133
+ // 找到中敘式相鄰後者,並轉移記憶
134
+ return self . node. take ();
135
+ }
136
+
137
+ pub fn remove (& mut self , key : i32 ) {
138
+ let Some (root ) = & mut self . node else {
139
+ return ;
140
+ };
141
+ match root . key. cmp (& key ) {
142
+ Ordering :: Equal => match (root . left. node. as_ref (), root . right. node. as_ref ()) {
143
+ (None , None ) => {
144
+ self . node. take ();
145
+ // 轉移自己記憶,丟掉自己
146
+ }
147
+ (Some (_ ), None ) => {
148
+ self . node = root . left. node. take ();
149
+ // 轉移左節點至自己
150
+ // 使左節點變為空樹,沒有擁有者,丟掉
151
+ }
152
+ (None , Some (_ )) => {
153
+ self . node = root . right. node. take ();
154
+ // 轉移右節點至自己
155
+ // 使右節點變為空樹,沒有擁有者,丟掉
156
+ }
157
+ (Some (_ ), Some (_ )) => {
158
+ if let Some (x ) = root . right. extract_min () {
159
+ root . key = x . key;
160
+ root . val = x . val. clone ();
161
+ }
162
+ }
163
+ },
164
+ Ordering :: Greater => {
165
+ root . left. remove (key );
166
+ }
167
+ Ordering :: Less => {
168
+ root . right. remove (key );
169
+ }
170
+ }
171
+ }
172
+ }
173
+ ```
174
+
175
+ ` Option::take ` 用來將` Some(x) ` 中的記憶換成` None ` ,並將` Some(x) ` 複製到輸出,如果` Option<Box<T>> ` 被` take ` ,原先的` Box<T> ` 會被丟掉,
176
+ 而全新的` Box ` 會產生,雖然這些最後通常會被優化掉。被拿走的節點造成 BST 成為空樹。
177
+
178
+ # 一小步
179
+
180
+ 在這篇文章中,我們對於 Rust 的所有權系統有些見解,不過我們還沒看到 Rust 另一個重要的觀念——生命時長!下篇文章見。
0 commit comments