macro_rules! path {
($(&)? ident / $(ident_or_literal_or_expr) / * ) => { ... };
(literal / $(ident_or_literal_or_expr) / * ) => { ... };
($(&)? ( path_expression ) / $(ident_or_literal_or_expr) / *) => { ... };
}Expand description
Efficient Path-join macro
The macro rules above is only for illustration purpose, see source code for implementation
§Usage
The macro efficiently creates joined paths from either a owned PathBuf
or a borrowed Path reference (impl AsRef<Path>), and one or more path segments reference
to join. The OS separator is used (i.e. \ on Windows).
The format of the macro in pseudocode is:
ⓘ
cu::path!( FIRST_SEG $( / NEXT_SEG )* )FIRST_SEG can be:
- A owned
PathBufident- e.g.
cu::path!(my_path_buf / ...)
- e.g.
- A borrowed
&Pathident:- e.g.
cu::path!(&my_path / ...) - Here
&is the macro rule to indicate you don’t want to borrow the path, so you need it even whenmy_pathis already a borrowed path
- e.g.
- A literal string, which you can use without
&- e.g.
cu::path!("my_path" / ...)
- e.g.
- An expression that evaluates to a owned
PathBuf- e.g.
cu::path!( (get_path()) / ...) - Expression needs to be parenthesized because
/cannot follow an expression in macro rules.{ }also works
- e.g.
- An expression that evaludates to a borrowed
&Path- e.g.
cu::path!( &(my.path) / ... ) - Expression needs to be parenthesized because
/cannot follow an expression in macro rules.{ }also works - Here
&is the macro rule to indicate you don’t want to borrow the path, so you need it even whenmy_pathis already a borrowed path
- e.g.
Each NEXT_SEG can be:
- A literal string
- An ident (the macro will not take ownership of the variable)
- An expression wrapped with either
( )or{ }. The last expression doesn’t need to be wrapped
§Examples
use std::path::{Path, PathBuf};
// From a literal string
let p1 = cu::path!("home" / "user");
let p2 = cu::path!("home" / "user" / "docs");
assert_eq!(p1, PathBuf::from("home").join("user"));
assert_eq!(p2, PathBuf::from("home").join("user").join("docs"));
// From an owned PathBuf ident (base is moved)
let base = PathBuf::from("usr").join("local");
let p = cu::path!(base / "bin" / "tool");
assert_eq!(p, PathBuf::from("usr").join("local").join("bin").join("tool"));
// From a borrowed &Path ident (use `&` even if already a reference)
let base = PathBuf::from("etc");
let base_ref: &Path = base.as_path();
let p = cu::path!(&base_ref / "nginx" / "nginx.conf");
assert_eq!(p, PathBuf::from("etc").join("nginx").join("nginx.conf"));
// From an expression returning PathBuf (must be parenthesized)
let p = cu::path!((PathBuf::from("usr").join("local")) / "bin");
assert_eq!(p, PathBuf::from("usr").join("local").join("bin"));
// From an expression returning &Path (must be parenthesized, and needs `&`)
let owned = PathBuf::from("var");
let p = cu::path!(&(owned.as_path()) / "log");
assert_eq!(p, PathBuf::from("var").join("log"));
// NEXT_SEG can be an ident — not moved, still usable after
let dir = "subdir";
let file = "file.txt";
let p = cu::path!("root" / dir / file);
assert_eq!(p, PathBuf::from("root").join(dir).join(file));
let _ = (dir, file); // still accessible
// NEXT_SEG can be an expression (must be parenthesized)
let sub = String::from("sub");
let p = cu::path!("root" / (sub.as_str()) / "output.log");
assert_eq!(p, PathBuf::from("root").join("sub").join("output.log"));§Implementation
Currently this uses the same implementation as the standard library (as of 1.95.0)
that does not do any probing to pre-allocate the path based on the input iterator.
Each segment is .push()-ed onto the initial buffer in a loop.