学習プロジェクトとして、Rust でリンク リストを実装するさまざまな方法を検討しています。ある特定の場所で、適切に動作するコードがいくつかありますが、それは unwrap を複数回呼び出します。これは一般的に、安全でない/貧弱なスタイルと見なされているという印象を受けています。もっと良くしたいです。
以下に関連する定義をいくつか示しますが、重要でない詳細は省略しています。next
これは、所有ポインタを持つ、単独でリンクされたリストであることに注意してください。これらの定義はすべて簡単で、ざっと目を通せるものでなければなりません。読みやすくするために、興味深い部分を切り離します。
type NodePtr<T> = Option<Box<Node<T>>>;
struct Node<T> {
data: T,
next: NodePtr<T>,
}
pub struct LinkedList<T> {
head: NodePtr<T>,
}
impl<T> LinkedList<T> {
pub fn pop_back(&mut self) -> Result<T, LinkedListError> {
if self.head.is_none() {
Err(LinkedListError { kind: LinkedListErrorKind::Empty })
} else {
Ok(LinkedList::pop_last_node(&mut self.head))
}
}
// definition for pop_last_node coming up shortly...
}
この特定の実装では、再帰関数を試しています。これが私の作業バージョンのpop_last_node
.
fn pop_last_node(node_ref: &mut NodePtr<T>) -> T {
match node_ref.as_ref().unwrap().next {
None => {
let old_tail = node_ref.take();
old_tail.unwrap().data
}
_ => LinkedList::pop_last_node(&mut node_ref.as_mut().unwrap().next)
}
}
これは正しく機能していますが、これは学習実験として行っているため、アンラップ呼び出しを削減し、パターン マッチングをさらに使用できるかどうかを確認したいと考えました。実験のこの部分はうまくいきませんでした。
これが私の試みです。残念ながら、このバージョンは元のバージョンよりもはるかに冗長です (そして紛らわしいです!)。私は特に「何かをする前にこの範囲から抜け出す」部分が好きではありませんが、それを改善する方法についてのアイデアを思いつくことができませんでした.
fn pop_last_node(node_ref: &mut NodePtr<T>) -> T {
{
let next_node = match node_ref.as_mut() {
None => panic!("Error handling will go here..."),
Some(node_box) => &mut node_box.next,
};
match *next_node {
None => {
// fall through to code below
},
_ => {
return LinkedList::pop_last_node(next_node)
},
}
}
// no sense converting this to a match--the "None" case was already checked above
node_ref.take().unwrap().data
}
そして、それが私が今いるところです。主な質問は次のとおりです。パターン マッチング バージョンを作成する、よりクレイジーでない方法はありますか? どちらかのバージョンの明瞭さや慣用性を改善するための重要な方法はありますか?