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.
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);
}
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();
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"]));
}
来源:https://stackoverflow.com/questions/40567020/what-is-a-macro-for-concatenating-an-arbitrary-number-of-components-to-build-a-p