【问题标题】:How to assert/introspect nested recursive structures/enums?如何断言/内省嵌套的递归结构/枚举?
【发布时间】:2020-11-15 18:16:36
【问题描述】:

我有一个有向图数据结构,我发现自己在与模式匹配作斗争以进行简单的内省。有没有更好的办法?

我的结构除了多个孩子之外还允许多个父母:

pub enum NodePtr {
    Terminal(i32),
    Node(Rc<RefCell<Node>>),
}

pub struct Node {
    children: Vec<NodePtr>,
    parents: Vec<Weak<RefCell<Node>>>,
}

这是我可以设计的最简单的代码来断言单个父子关系已按预期建立:

fn example(parent: NodePtr, child: NodePtr) {
    if let NodePtr::Node(parent_rc) = &parent {
        if let NodePtr::Node(child_rc) = &child {
            assert_eq!(parent_rc.borrow().children.len(), 1);
            if let Option::Some(node_ptr) = parent_rc.borrow().children.get(0) {
                if let NodePtr::Node(rc) = node_ptr {
                    assert!(Rc::ptr_eq(rc, child_rc));
                } else {
                    assert!(false);
                }
            } else {
                assert!(false);
            }

            assert_eq!(child_rc.borrow().parents.len(), 1);
            if let Option::Some(weak) = child_rc.borrow().parents.get(0) {
                if let Some(rc) = weak.upgrade() {
                    assert!(Rc::ptr_eq(&rc, parent_rc));
                } else {
                    assert!(false);
                }
            } else {
                assert!(false);
            }
        } else {
            assert!(false);
        }
    } else {
        assert!(false)
    };
}

Full working example

感觉就像我必须经历如此多的匹配才能得到我想要的。我是否错过了更好的方法或不同的思维方式?

【问题讨论】:

  • 大部分代码由右大括号和assert!(false) 组成。你能举出另一个你认为缺乏优雅/人体工程学的例子吗?另请注意,您可以将 Option::Some 拼写为 Some
  • 如果您使用Option::unwrap 并创建一个unwrapNode,所有这些都将大大减少嵌套:play.rust-lang.org/…
  • @loganfsmyth 这正是我正在寻找的——谢谢——当你知道会发生什么时创建这些类型的辅助函数是典型的吗?
  • @user4815162342 你的大部分代码都由右大括号和断言组成!(false) 这有点意思——我在问如何简化代码
  • @prior 很公平。我想说的是,简化该特定函数的方法可能适用于处理图形的其他代码。或者您是说对图表进行任何事情都需要类似的大量assert!(false) 检查?

标签: data-structures rust enums nested


【解决方案1】:

感觉就像我必须经历如此多的匹配才能得到我想要的。我是否错过了更好的方法或者可能是不同的思维方式?

您可以在实用函数后面抽象出琐碎的模式匹配,例如:

impl NodePtr {
    /// Returns Some if NodePtr is a Node, None otherwise.
    fn as_node(&self) -> Option<&Rc<RefCell<Node>>> {
        match self {
            NodePtr::Node(rc) => Some(rc),
            _ => None,
        }
    }
}

这允许您将if let NodePtr::Node(foo) = foo { ... } else { assert!(false); } 替换为let foo = foo.as_node().unwrap()。您也可以使用 foo.unwrap() 代替整个 if let Some(foo) = foo { ... } else { assert!(false); } 块。最后,您不需要对x.get(n) 进行模式匹配,您可以使用x[n]&amp;x[n]

应用所有这些会将您的代码压缩为以下内容:

// prove parent & child relationship
let parent_rc = parent.as_node().unwrap();
let child_rc = child.as_node().unwrap();
assert_eq!(parent_rc.borrow().children.len(), 1);
let node_ptr = &parent_rc.borrow().children[0];
assert!(Rc::ptr_eq(node_ptr.as_node().unwrap(), child_rc));
assert_eq!(child_rc.borrow().parents.len(), 1);
let weak = &child_rc.borrow().parents[0];
assert!(Rc::ptr_eq(&weak.upgrade().unwrap(), parent_rc));

Playground

还有一个单独的问题,即是否可以对类型进行结构化以避免如此多的运行时检查和出现恐慌的可能性。我建议查看现有的板条箱以构建 DAG。

【讨论】:

  • 同意——这比 @loganfsmyth 在 cmets 中的回答要好一些,因为它避免了恐慌,直到最后一刻让外部呼叫者选择使用 .unwrap() 或不按他们的意愿.
猜你喜欢
  • 2015-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-25
  • 1970-01-01
  • 1970-01-01
  • 2021-09-18
  • 2016-07-12
相关资源
最近更新 更多