RustでLinkedListを実装してみた
TL;DR
最近、データ構造とアルゴリズムをRustで始めてみようかなと思っていて、双方向連結リスト(DLList)を実装していたのですが、難しかったのでメモ。基本的には公式のLinkedListを見ながら実装しました。
要件
| method名 | 挙動 |
|---|---|
get(i) | i番目の要素を見る |
set(i, x) | i番目の要素をxにする |
add(i, x) | i番目にxを加える |
remove(i) | i番目の要素を削除する |
の4つのメソッドを実装します。
実装
構造体の定義
公式はNonNullを使っていたので真似しました。unsafe使いまくる必要があるので怖いんですけど、公式がこうしていたのでunsafeを使うことにしました。RefCell, Rc, Weakを使うのも良いというのを何件か見かけました。unsafe使わない実装もやってみたいですね。あとPhantomDataなにってなってるので勉強します。
getとsetの定義
最初に実装したいのはi番目のNonNull<Node<T>>を持ってくる処理です。公式をよく見ると、Cursor的な構造体を定義しています。現在のindexとその時点のポインタを持っていて、nextとprevを移動できる感じのやつです。公式だとCursorMutも定義していてそっちのほうがRustっぽい気もしたんですが、なくても今回の要件的には問題なさそうだったので省略しています。
Cursorを使ってi番目のノードのポインタを持ってくる処理を書きます。headとtailの近いほうから見に行きます。getとsetはこのポインタから値を取り出すか変更する処理なんですぐ書けます。
addの定義
addを行うためには、入れたいノード(e)の前に新しいノードを入れればいいです。つまり
のような感じです。この処理の公式の実装はsplice_nodesというメソッドで実装されているようです。この実装はもうちょっと複雑で、他のリストがあったとして、そのheadとtailのポインタを使うことで
のような処理ができます。ただ、今回は一個入れればいいので、もう少し簡略化して書きます。引数にe.prevとe、addedをとって挿入するsplice_nodeを実装しました。addに関しては入れたいところのポインタをget_nodeで持ってきて、splice_nodeすればいいです。
removeの実装
消したいノードをn_iとすると、以下のようにすればいいことがわかります。
この処理はunlink_nodeという関数で実装されています。やってる処理は上記そのままです。removeはaddのときと同じく、i番目のノードのポインタを持ってきて、unlink_nodeに渡すだけですね。
感想
公式のコードはやっぱり勉強になります。