rust の std::path::Path のつまづきポイント

rust の std::path::Path はパスを扱うための標準ライブラリです。 std::path::Path を使う上でつまづきそうなポイントをまとめました。

動作確認は rustc 1.43.0 (4fb7144ed 2020-04-20) で行いました。

/ or \

Unix では / のみ、 Windows では \/ の両方が separator として認識されます。

参考:

This type supports a number of operations for inspecting a path, including breaking the path into its components (separated by / on Unix and by either / or \ on Windows), extracting the file name, determining whether the path is absolute, and so on.

relative or absolute

Path が相対パス絶対パスかをチェックするために、 is_relative, is_absolute というメソッドが提供されています。

is_relative の実装 は下記のとおりです。 is_relative は単に is_absolute の逆の結果を返すように実装されています。

!self.is_absolute()

is_absolute の実装 は下記のとおりです。 is_absoluteUnixWindows によって判定の仕方が変わります。 (redox の場合も判定の仕方が違いますが、今回は省略。)

Unix の場合、 is_absolutehas_root の結果は同じで、パスが / から始まれば絶対パスと判定されます。 Windows の場合、 prefixC: など)から始まるかつ prefix 以降が \/ から始まれば絶対パスと判定されます。

self.has_root() && (cfg!(unix) || self.prefix().is_some())

Pathうしの比較

rust では 2つの struct がイコールであるかどうかの判定を PartialEq trait で実装します。

PathPartialEq の実装 は下記のとおりです。 components() の内容を比較していることが分かります。

impl cmp::PartialEq for Path {
    fn eq(&self, other: &Path) -> bool {
        self.components().eq(other.components())
    }
}

components() はパスを separator で区切ってできた Component への iterator を返します。 ただし、区切る際に、連続する / を無視する、始まり以外の . を無視する、末尾の / を無視するなどの処理が行われます。

したがって、以下の assertion はすべて true になります。

assert!(Path::new("a//b") == Path::new("a/b"));
assert!(Path::new("a/./b") == Path::new("a/b"));
assert!(Path::new("a/b/") == Path::new("a/b"));

一方で a/b/..a はイコールとして扱われません。 これは、 a/b が symlink である可能性があるからです。

assert!(Path::new("a/b/..") != Path::new("a"));

~ の扱い

~ は home ディレクトリとして展開されることはなく、 ~ という名前のパスとして扱われます。 例えば、 Path::new("~").is_dir()false となります(current dir に ~ という名前のディレクトリがなければ)。 これは、 ~/home/user などではなく、 $PWD/~ を指していることになるためです。

assert!(!Path::new("~").is_dir());

~ を home ディレクトリとして展開するには、 shellexpand などのライブラリを使うと良いです。