From Is working with Paths always this painful? on Reddit:

The difference between Path and PathBuf is roughly the same as the one between &str and String or &[] and Vec, ie. Path only holds a reference to the path string data but doesn’t own this data, while PathBuf owns the string data itself. This means that a Path is immutable and can’t be used longer than the actual data (held somewhere else) is available.

The reason why both types exists is to avoid allocations where possible. As most functions take both Path and PathBuf as arguments (by using AsRef<Path> for example), this usually doesn’t have a big impact on your code.

A very rough guide for when to use Path vs. PathBuf:

  • For return types:
    • If your function gets passed a Path[Buf] and returns a subpath of it, you can just return a Path (like Path[Buf].parent())
    • If you create a new path, or combine paths or anything like that, you need to return a PathBuf.
  • For arguments:
    • Take a PathBuf if you need to store it somewhere.
    • Use a Path otherwise.
    • In public interfaces, you usually don’t want to use Path or PathBuf directly, but rather a generic P: AsRef<Path> or P: Into<PathBuf>. That way the caller can pass in Path, PathBuf, &str or String.