gpt4 book ai didi

rust - 我怎样才能让这个 Rust 代码更地道

转载 作者:行者123 更新时间:2023-12-03 11:36:16 25 4
gpt4 key购买 nike

最近我开始学习 Rust,我的主要困难之一是将多年的面向对象思维转化为过程代码。
我正在尝试解析具有由特定处理程序处理的标记的 XML,该处理程序可以处理从子项获取的数据。
此外,我还有一些它们之间共有的字段成员,我不希望将相同的字段写入所有处理程序。
我试过了,我的代码是这样的:

use roxmltree::Node; // roxmltree = "0.14.0"

fn get_data_from(node: &Node) -> String {
let tag_name = get_node_name(node);
let tag_handler: dyn XMLTagHandler = match tag_name {
"name" => NameHandler::new(),
"phone" => PhoneHandler::new(),
_ => DefaultHandler::new()
}
if tag_handler.is_recursive() {
for child in node.children() {
let child_value = get_data_from(&child);
// do something with child value
}
}
let value: String = tag_handler.value()

value
}

// consider that handlers are on my project and can be adapted to my needs, and that XMLTagHandler is the trait that they share in common.
我的主要问题是:
  • 这感觉像是一种面向对象的方法。
  • is_recursive 需要重新实现到每个结构,因为它们的特征不能有字段成员,我以后必须添加更多字段,这意味着每个新字段都有更多样板;
  • 我可以为 Handler 使用一种类型并将函数指针传递给它,但这种方法似乎很脏。例如:=> Handler::new(my_other_params, phone_handler_func)
  • 最佳答案

    • This feels like a Object oriented approach to it

    事实上,我不这么认为。此代码明显违反 Tell-Don't-Ask principle ,这脱离了面向对象编程的中心思想:将数据和相关行为封装到对象中。对象( NameHandlerPhoneHandler 等)没有足够的知识来了解它们自己要做什么,所以 get_data_from必须向他们查询信息并决定做什么,而不是简单地发送消息并让对象弄清楚如何处理它。
    因此,让我们首先将有关如何处理每种标签的知识转移到处理程序本身:
    trait XmlTagHandler {
    fn foreach_child<F: FnMut(&Node)>(&self, node: &Node, callback: F);
    }

    impl XmlTagHandler for NameHandler {
    fn foreach_child<F: FnMut(&Node)>(&self, _node: &Node, _callback: F) {
    // "name" is not a recursive tag, so do nothing
    }
    }

    impl XmlTagHandler for DefaultHandler {
    fn foreach_child<F: FnMut(&Node)>(&self, node: &Node, callback: F) {
    // all other tags may be recursive
    for child in node.children() {
    callback(child);
    }
    }
    }
    这样你就调用 foreach_child各种 Handler ,并让处理程序自己决定正确的操作是否是递归。毕竟,这就是他们有不同类型的原因——对吧?
    摆脱 dyn部分,这是不必要的,让我们编写一个使用 XmlTagHandler 的通用辅助函数处理一种特定类型的标签,并修改 get_data_from所以它只是调度到它的正确参数化版本。 (我假设 XmlTagHandler 也有一个 new 函数,这样你就可以通用地创建一个。)
    fn handle_tag<H: XmlTagHandler>(node: &Node) -> String {
    let handler = H::new();
    handler.foreach_child(node, |child| {
    // do something with child value
    });
    handler.value()
    }

    fn get_data_from(node: &Node) -> String {
    let tag_name = get_node_name(node);
    match tag_name {
    "name" => handle_tag::<NameHandler>(node),
    "phone" => handle_tag::<PhoneHandler>(node),
    _ => handle_tag::<DefaultHandler>(node),
    }
    }
    如果你不喜欢 handle_tag::<SomeHandler>(node) ,也可以考虑制作 handle_tag提供的方法 XmlTagHandler , 所以你可以改写 SomeHandler::handle(node) .
    请注意,我并没有真正改变任何数据结构。您对 XmlTagHandler 的假设特质和各种 Handler实现者是组织代码的一种非常正常的方式。但是,在这种情况下,与仅编写三个单独的函数相比,它并没有提供任何真正的改进:
    fn get_data_from(node: &Node) -> String {
    let tag_name = get_node_name(node);
    match tag_name {
    "name" => get_name_from(node),
    "phone" => get_phone_from(node),
    _ => get_other_from(node),
    }
    }
    在某些语言中,例如 Java,所有代码都必须是某个类的一部分——因此您会发现自己编写的类由于任何其他原因不存在,而不是将相关事物组合在一起。在 Rust 中,您不需要这样做,因此请确保任何添加的复杂性,例如 XmlTagHandler实际上是在拉它的重量。
    • is_recursive needs to be reimplemented to each struct because they traits cannot have field members, and I will have to add more fields later, which means more boilerplate for each new field

    如果没有有关这些领域的更多信息,就不可能真正理解您在这里面临的问题;但是,一般来说,如果有 struct 的家庭s 有一些共同的数据,你可能想要做一个通用的 struct而不是一个特质。查看 How to reuse codes for Binary Search Tree, Red-Black Tree, and AVL Tree? 的答案更多建议。
    • I could use one type for a Handler and pass to it a function pointer, but this approach seems dirty

    优雅有时是有用的东西,但它是主观的。我会推荐闭包而不是函数指针,但这个建议对我来说似乎并不“脏”。制作闭包并将它们放入数据结构中是编写 Rust 代码的一种非常正常的方式。如果您可以详细说明您不喜欢它的哪些方面,也许有人可以指出改进它的方法。

    关于rust - 我怎样才能让这个 Rust 代码更地道,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65871837/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com