`PathBuf::join` does not fix slashes

Why does this happen? Isn't PathBuf supposed to abstract away platform-specific details like these? I fixed it by doing first.extend(second.components()), which isn't too terrible, but isn't immediately clear without a comment.

PathBuf is a way to contruct paths. It depends on where you run the code. Under Linux it will treat the path like a UNIX string. Even .\\a\\b\\c\\ + d\\ will result in .\\a\\b\\c\\/d\\
I also changed your code to this, to make it a bit easier to read, note that Path is akin to str, not String like PathBuf.

use std::path::Path;

fn main() {
    let first = Path::new("./a/b/c");
    let joined = first.join("d\\");
    
    dbg!(joined);
}

More information about PathBuf can be found here: PathBuf in std::path - Rust

1 Like

Can you specify what behaviour did you expect?

The way that Rust's path functionality works is that it generally attempts to preserve the path as provided until you do path manipulation to it. When you do a path manipulation operation, any path modifications will use the primary path separator for the current platform.

One interesting little detail is that the path parsing is platform dependent, which has caught me out some. Notably, on non-Windows platforms, \ isn't parsed as a separator and path prefixes aren't parsed either.

This is usually what you want/need. Linux paths can have arbitrary non-nul bytes separated by /, which includes \ or things that look like Windows prefixes [1], so trying to parse them would be incorrect. But on Windows, both \ and / are accepted as being separators in non-verbatim paths, so you want the API to work like users expect. BUT, when you have a verbatim path (starting with \\?\, often called UNC paths even though neither style superset the other), / is actually allowed as part of a path component.

“Properly” manipulating paths in full generality is just a very subtly complicated problem. Rust's std does a quite amazing job of working “as expected” without being wrong in the real-world necessary edge cases.


  1. The most blursed system I have ever seen some evidence of was a *nix system that instead of /mnt/ was exposing drives as pseudo-Windows /C:/. IIRC everyone who had to deal with that machine hated it. ↩︎

4 Likes

For the last separator to get "normalized" (either be removed entirely or replaced with the separator first used)

I see. However, doesn't .join (or .push, which results in the same outcome in this scenario) constitute a manipulation?

Which "last separator"? If you mean the \\ in second that is not a separator on Linux (which is where the playground runs), and hence it is correctly preserved.

1 Like

Here's an example of creating that path as a directory and successfully opening it.