#Path
Status: standard library specification draft.
This chapter defines std.path, which lib/std writes in ordinary hd
over the language tier:
- the
Pathtype that the file system traits take; - the
Parseimplementation that reads aPathfrom text; - the text operations
join,parent,file_name,extension,file_stem, andwith_extension.
#Paths
A Path is a newtype over the text of a file system path:
use std.path.Path
fn notes() -> Path:
Path("notes/today.txt")- std-path.decl
std.pathdeclarespub type Path(string). Code imports it, as inuse std.path.Path. - std-path.textA
Pathholds its text as written. Constructing one neither checks nor normalizes it. - std-path.traits
PathimplementsEq, which compares the text,Display, which shows the text, andParse, which wraps it. - std-path.parse
PathimplementsParsewithErrorset tonever.parsereturns.Ok(Path(text))for every text, so"a/b".parse::[Path]()is.Ok(Path("a/b")). - std-path.parse.textThe implementation neither checks nor normalizes the text, as
Path(text)does, and adds no public name. So aPathis a typed command-line field, anenv_as::[Path]read, or a regex group.
Why. Go's
path/filepathworks on plain strings. A newtype keeps that cost and still tells a path from other text in a signature.
See also: Fs.
#Path Operations
A Path splits and joins its text at /, the only separator:
use std.path.Path
fn config_file(home: Path) -> Path:
home.join(".config").join("hd.toml")
fn sibling(file: Path, name: string) -> Path:
match file.parent():
.Some(dir) => dir.join(name)
.None => Path(name)
fn is_markdown(file: Path) -> bool:
match file.extension():
.Some(text) => text == "md"
.None => false| Rule | Method | Result |
|---|---|---|
std-path.joinjoin |
pub fn join(self, part: string) -> Path |
the path followed by part |
std-path.parentparent |
pub fn parent(self) -> Path? |
the path without its last component, or .None when it has none |
std-path.file-namefile_name |
pub fn file_name(self) -> string? |
the last component, when it names a file or directory |
std-path.extensionextension |
pub fn extension(self) -> string? |
the text after the last . of the file name |
std-path.file-stemfile_stem |
pub fn file_stem(self) -> string? |
the file name without its extension |
std-path.with-extensionwith_extension |
pub fn with_extension(self, extension: string) -> Path |
the path with the extension of its file name replaced |
-
std-path.ops.methods
std.pathgivesPaththe six methods above. Calling one needs only thePathimport. -
std-path.ops.separatorThe separator is
/on every host. -
std-path.ops.componentsThe components of a path are the pieces of its text between separators. Empty pieces and
.pieces are left out, except a.that comes first in a path that does not start with/. -
std-path.ops.text-onlyThe methods read text only. None touches a file system, resolves
.., or changes a path beyond what a rule below says. -
std-path.join.absoluteWhen
partstarts with/,joinreturnsPath(part). -
std-path.join.separatorOtherwise
joinreturns the text of the path, then/unless that text is empty or ends in/, thenpart. SoPath("a").join("")isPath("a/"). -
std-path.parent.value
parentreturns, in.Some, the path of every component but the last, separated by/, with a leading/when the path starts with/. -
std-path.parent.noneA path with no component,
""or/, has no parent, soparentreturns.None. -
std-path.parent.singleSo a path of one component has the parent
Path(""), orPath("/")when it starts with/. -
std-path.file-name.value
file_namereturns the last component in.Some. It returns.Nonewhen the path has no component or the last one is.or... -
std-path.extension.value
extensionreturns, in.Some, the text after the last.of the file name, without the.. -
std-path.extension.noneIt returns
.Nonewhen there is no file name, when the name has no., or when its only.is its first character. -
std-path.extension.emptyA file name that ends in
.gives.Some(""). -
std-path.file-stem.value
file_stemreturns, in.Some, the file name without the.and extension thatextensionreturns. Whenextensionreturns.None, the stem is the whole file name, so.bashrchas the stem.bashrc. -
std-path.file-stem.none
file_stemreturns.Noneexactly whenfile_namedoes. -
std-path.file-stem.dotsOnly the last
.splits the name, soa.tar.gzhas the stema.tar. A name that ends in.loses it, soa.has the stema. -
std-path.with-extension.value
with_extensionreturns the path text up to the end of the file stem, followed by.andextension. The file name is the last component, as infile_name. -
std-path.with-extension.emptyWhen
extensionis"",with_extensionadds no., so it removes the extension. A name without one is kept. -
std-path.with-extension.noneA path with no file name,
"",/,., or a path ending in.., is returned unchanged. -
std-path.with-extension.trailingSeparators and
.components after the file name are dropped, soPath("a/b/").with_extension("x")isPath("a/b.x"). Separators before the name stay as written. -
std-path.with-extension.verbatim
extensionis added as written. It is not checked, and a leading.is not removed, sowith_extension(".x")adds..x. -
std-path.with-extension.dotfileA dotfile has no extension, so
with_extensionappends:Path(".bashrc").with_extension("bak")isPath(".bashrc.bak"). -
std-path.ops.cost
file_stemandwith_extensionslice the path text, andwith_extensionbuilds one new text.
| Text | parent |
file_name |
extension |
file_stem |
|---|---|---|---|---|
"/a/b.txt" |
.Some(Path("/a")) |
.Some("b.txt") |
.Some("txt") |
.Some("b") |
"a/b/" |
.Some(Path("a")) |
.Some("b") |
.None |
.Some("b") |
"a" |
.Some(Path("")) |
.Some("a") |
.None |
.Some("a") |
"/a" |
.Some(Path("/")) |
.Some("a") |
.None |
.Some("a") |
"/", "" |
.None |
.None |
.None |
.None |
".bashrc" |
.Some(Path("")) |
.Some(".bashrc") |
.None |
.Some(".bashrc") |
"a.tar.gz" |
.Some(Path("")) |
.Some("a.tar.gz") |
.Some("gz") |
.Some("a.tar") |
"a." |
.Some(Path("")) |
.Some("a.") |
.Some("") |
.Some("a") |
"a/.." |
.Some(Path("a")) |
.None |
.None |
.None |
"./a" |
.Some(Path(".")) |
.Some("a") |
.None |
.Some("a") |
with_extension of |
With | Result |
|---|---|---|
Path("a/b.txt") |
"md" |
Path("a/b.md") |
Path("a.tar.gz") |
"zip" |
Path("a.tar.zip") |
Path("a") |
"md" |
Path("a.md") |
Path("a.txt") |
"" |
Path("a") |
Path(".bashrc") |
"bak" |
Path(".bashrc.bak") |
Path("a.") |
"md" |
Path("a.md") |
Path("a/b/") |
"x" |
Path("a/b.x") |
Path("/") |
"x" |
Path("/") |
join of |
Result |
|---|---|
Path("a") and "b" |
Path("a/b") |
Path("a/") and "b" |
Path("a/b") |
Path("a") and "/b" |
Path("/b") |
Path("") and "b" |
Path("b") |
Why. The methods and their edge cases are Rust's
Path::join,parent,file_name,extension,file_stem, andwith_extensionon Unix, so the dotfile, trailing-slash, and root rows match there. Go'sfilepath.Joinalso cleans the result, which Rust and this chapter do not.partis astring, since hd has noAsRef; aPathargument is writtenstring(path).