这个unwrap/pattern匹配码能不能多clear/idiomatic?
Can this unwrap/pattern matching code be made more clear/idiomatic?
作为一个学习项目,我正在探索在 Rust 中实现链表的不同方法。在一个特定的地方,我有一些代码可以正常工作,但它会多次调用 unwrap——我的印象是这通常被认为是 unsafe/poor 风格。我想让它变得更好。
这里是一些相关的定义,省略了一些不重要的细节。请注意,它是一个单向链表,拥有 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)
}
}
这工作正常,但由于我是作为一个学习实验来做的,我想看看我是否可以减少解包调用并使用更多的模式匹配。这部分实验不太顺利
这是我的尝试。不幸的是,这个版本比原来的版本更冗长(而且令人困惑!)。我特别不喜欢 "fall through out of this scope before you can do anything" 部分,但我还没有想出如何让它变得更好的想法。
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
}
这就是我现在的位置。主要问题是:是否有一种不那么疯狂的方式来编写模式匹配版本?是否有提高清晰度或惯用性的重要方法-两个版本的正确性?
由于 Box
,与框的模式匹配在稳定版上是混乱的。如果你愿意每晚使用直到盒子模式稳定,你可以重写你的 pop_back
函数(而不仅仅是 pop_last_node
函数):
pub fn pop_back(&mut self) -> Result<T, LinkedListError> {
fn pop_last_node<T>(node: &mut NodePtr<T>) -> Option<T> {
match node.take() {
None => None,
// is a leaf
Some(box Node { next: None, data }) => Some(data),
Some(mut this) => {
// recurse
let ret = pop_last_node(&mut this.next);
// put this subnode back, since it's not a leaf
*node = Some(this);
ret
}
}
}
pop_last_node(&mut self.head).ok_or(LinkedListError {
kind: LinkedListErrorKind::Empty
})
}
在 PlayPen
中试用
作为一个学习项目,我正在探索在 Rust 中实现链表的不同方法。在一个特定的地方,我有一些代码可以正常工作,但它会多次调用 unwrap——我的印象是这通常被认为是 unsafe/poor 风格。我想让它变得更好。
这里是一些相关的定义,省略了一些不重要的细节。请注意,它是一个单向链表,拥有 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)
}
}
这工作正常,但由于我是作为一个学习实验来做的,我想看看我是否可以减少解包调用并使用更多的模式匹配。这部分实验不太顺利
这是我的尝试。不幸的是,这个版本比原来的版本更冗长(而且令人困惑!)。我特别不喜欢 "fall through out of this scope before you can do anything" 部分,但我还没有想出如何让它变得更好的想法。
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
}
这就是我现在的位置。主要问题是:是否有一种不那么疯狂的方式来编写模式匹配版本?是否有提高清晰度或惯用性的重要方法-两个版本的正确性?
由于 Box
,与框的模式匹配在稳定版上是混乱的。如果你愿意每晚使用直到盒子模式稳定,你可以重写你的 pop_back
函数(而不仅仅是 pop_last_node
函数):
pub fn pop_back(&mut self) -> Result<T, LinkedListError> {
fn pop_last_node<T>(node: &mut NodePtr<T>) -> Option<T> {
match node.take() {
None => None,
// is a leaf
Some(box Node { next: None, data }) => Some(data),
Some(mut this) => {
// recurse
let ret = pop_last_node(&mut this.next);
// put this subnode back, since it's not a leaf
*node = Some(this);
ret
}
}
}
pop_last_node(&mut self.head).ok_or(LinkedListError {
kind: LinkedListErrorKind::Empty
})
}
在 PlayPen
中试用