这种解包/模式匹配代码可以更清晰/惯用吗?

时间:2015-06-22 03:39:20

标签: pattern-matching rust unwrap

我正在探索在Rust中实现链表的不同方法作为学习项目。在一个特定的地方,我有一些工作正常的代码,但它会打开多次调用 - 我认为这通常被认为是不安全/糟糕的风格。我想让它变得更好。

以下是一些相关的定义,省略了一些不重要的细节。请注意,它是一个单链表,拥有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
}

这就是我现在的位置。主要问题是: 是否有一种不太疯狂的方式来编写模式匹配版本? 是否有重要方法可以提高任一版本的清晰度或惯用性?< / p>

1 个答案:

答案 0 :(得分:1)

由于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

中试用