【发布时间】:2020-06-14 04:23:54
【问题描述】:
我的输入是一个文件系统路径的平面列表,这些路径是单个顶级目录的所有子目录(或其中的文件)。
我的最终输出应该是:
- 路径的文本分层显示,类似于 unix tree 命令。
- 具有与 (1) 匹配的逻辑结构的路径的分层 JSON 序列化
我创建了一个中间数据结构,它是一个自引用 struct Dir,它的名称和向量为 Box'ed child struct Dir。
我可以成功地使用Dir 来表示任意目录树,如下面的输出所示。
我正在考虑使用堆栈来处理列表,为每个子目录添加一个 Dir 并在上升时弹出,但我似乎无法像使用 C 或其他那样使用 Rust语言。无论我尝试什么,都会遇到编译器错误。
如何将平面列表转换为 Dir 并让编译器满意?或者,如何以不同的方式实现(1)和(2)?
代码:
// A type to represent a path, split into its component parts
#[derive(Debug)]
struct Path {
parts: Vec<String>,
}
impl Path {
pub fn new(path: &str) -> Path {
Path {
parts: path.to_string().split("/").map(|s| s.to_string()).collect(),
}
}
}
// A recursive type to represent a directory tree.
// Simplification: If it has children, it is considered
// a directory, else considered a file.
#[derive(Debug)]
struct Dir {
name: String,
children: Vec<Box<Dir>>,
}
impl Dir {
fn new(name: &str) -> Dir {
Dir {
name: name.to_string(),
children: Vec::<Box<Dir>>::new(),
}
}
fn has_child(&self, name: &str) -> bool {
for c in self.children.iter() {
if c.name == name {
return true;
}
}
false
}
fn add_child<T>(mut self, leaf: T) -> Self
where
T: Into<Dir>,
{
self.children.push(Box::new(leaf.into()));
self
}
}
fn dir(val: &str) -> Dir {
Dir::new(val)
}
fn main() {
// Form our INPUT: a list of paths.
let paths = vec![
Path::new("root/child1/grandchild1.txt"),
Path::new("root/child1/grandchild2.json"),
Path::new("root/child2/grandchild3.pdf"),
Path::new("root/child3"),
];
println!("Input Paths:\n{:#?}\n", paths);
// Transformation:
// I need an algorithm here that converts the list of paths
// above to a recursive struct (tree) below.
// ie: paths --> dir
let top = dir("root");
let mut cwd = ⊤
for p in paths.iter() {
for part in p.parts.iter() {
if !cwd.has_child(part) {
// cwd.add_child(dir(part));
// cwd = &cwd.children[cwd.children.len() - 1];
}
}
}
// Intermediate Representation:
// The above transformation should result in the following
// hierarchical structure.
let top = dir("root")
.add_child(
dir("child1")
.add_child(dir("grandchild1.txt"))
.add_child(dir("grandchild2.json")),
)
.add_child(dir("child2").add_child(dir("grandchild3.pdf")))
.add_child(dir("child3"));
println!("Intermediate Representation of Dirs:\n{:#?}\n\nOutput Tree Format:\n", top);
// Output: textual `tree` format
print_dir(&top, 0);
}
// A function to print a Dir in format similar to unix `tree` command.
fn print_dir(dir: &Dir, depth: u32) {
if depth == 0 {
println!("{}", dir.name);
} else {
println!(
"{:indent$}{} {}",
"",
"└──",
dir.name,
indent = ((depth as usize) - 1) * 4
);
}
for child in dir.children.iter() {
print_dir(child, depth + 1)
}
}
输出:
$ ./target/debug/rust-tree
Input Paths:
[
Path {
parts: [
"root",
"child1",
"grandchild1.txt",
],
},
Path {
parts: [
"root",
"child1",
"grandchild2.json",
],
},
Path {
parts: [
"root",
"child2",
"grandchild3.pdf",
],
},
Path {
parts: [
"root",
"child3",
],
},
]
Intermediate Representation of Dirs:
Dir {
name: "root",
children: [
Dir {
name: "child1",
children: [
Dir {
name: "grandchild1.txt",
children: [],
},
Dir {
name: "grandchild2.json",
children: [],
},
],
},
Dir {
name: "child2",
children: [
Dir {
name: "grandchild3.pdf",
children: [],
},
],
},
Dir {
name: "child3",
children: [],
},
],
}
Output Tree Format:
root
└── child1
└── grandchild1.txt
└── grandchild2.json
└── child2
└── grandchild3.pdf
└── child3
【问题讨论】:
-
看来Cannot move out of borrowed content / cannot move out of behind a shared reference 的答案可能会回答您的问题。如果没有,请edit您的问题解释差异。否则,我们可以将此问题标记为已回答。
-
值得指出的是,字符串不是表示路径的好方法。这就是 Rust 有
Path类型和朋友的原因。 -
thx 但代表路径不是我要问的。将路径列表转换为层次结构是对象。
-
我阅读了您提供的链接,但它并没有解决问题。我已经用 &self 尝试了各种方法,但都无济于事——我认为使用 clone() 毫无意义。因此,如果您将此问题标记为已回答,那么我将不会比我提出问题时更接近解决方案。我到处搜索,我没有找到任何人在做我想做的事情的例子,这一定是可能的,但对于像我这样学习语言的人来说并不明显。如果该链接是相关的,也许您可以修改我提供的代码并演示它是如何解决它的。
标签: recursion rust tree hierarchy hierarchical