问题
In Python, a function called os.path.join()
allows concatenating multiple strings into one path using the path separator of the operating system. In Rust, there is only a function join()
that appends a string or a path to an existing path. This problem can't be solved with a normal function as a normal function needs to have a fixed number of arguments.
I'm looking for a macro that takes an arbitrary number of strings and paths and returns the joined path.
回答1:
Once you read past the macro syntax, it's not too bad. Basically, we take require at least two arguments, and the first one needs to be convertible to a PathBuf
via Into
. Each subsequent argument is push
ed on the end, which accepts anything that can be turned into a reference to a Path
.
macro_rules! build_from_paths {
($base:expr, $($segment:expr),+) => {{
let mut base: ::std::path::PathBuf = $base.into();
$(
base.push($segment);
)*
base
}}
}
fn main() {
use std::{
ffi::OsStr,
path::{Path, PathBuf},
};
let a = build_from_paths!("a", "b", "c");
println!("{:?}", a);
let b = build_from_paths!(PathBuf::from("z"), OsStr::new("x"), Path::new("y"));
println!("{:?}", b);
}
回答2:
There's a reasonably simple example in the documentation for PathBuf:
use std::path::PathBuf;
let path: PathBuf = [r"C:\", "windows", "system32.dll"].iter().collect();
回答3:
A normal function which takes an iterable (e.g. a slice) can solve the problem in many contexts:
use std::path::{Path, PathBuf};
fn join_all<P, Ps>(parts: Ps) -> PathBuf
where
Ps: IntoIterator<Item = P>,
P: AsRef<Path>,
{
parts.into_iter().fold(PathBuf::new(), |mut acc, p| {
acc.push(p);
acc
})
}
fn main() {
let parts = vec!["/usr", "bin", "man"];
println!("{:?}", join_all(&parts));
println!("{:?}", join_all(&["/etc", "passwd"]));
}
Playground
来源:https://stackoverflow.com/questions/40567020/what-is-a-macro-for-concatenating-an-arbitrary-number-of-components-to-build-a-p