Misunderstanding of PathBuf:push

The situation:

let mut p = PathBuf::from("/var");
p.push("/");
println!("{}", p.to_string_lossy()); // ?

Now, when I converting this path to the string type, it has / path (not /var as expected)

This is called out explicitly in the method's documentation:

If path is absolute, it replaces the current path.

Since / begins with a /, it is an absolute path.

5 Likes

Sure, but I'm thinking in the Vector context, if I push, why does it overwrites previous parts. I spent a lot of time until I understood it.

My guess is that it is mimicking the way that the shell cd command works. If you cd to a relative path, it appends to the current directory. If you cd to an absolute path, it replaces the current directory. So perhaps the author did that to make it intuitive, in this sense at least. In any case, that's how I would try to remember the behavior.

5 Likes

Interesting point of view, thanks.

What should it mean to append an absolute path to another path? It's not really a meaningful operation. But the existing semantics make at least one use case easy: Consider a program that takes a path as input (from user, from config file, whatever). The input should be appended to a "default path" (for example, the program's working directory) if it's relative, or used as-is if it's absolute. push does exactly that for you.

1 Like

On the other hand, it makes a different scenario harder: working on mounting a set of absolute paths into a folder. This could be for operating on a non-booed system (Linux distro installer, working on container file systems, etc) as well as for working on archive files (want to figure our where a file from a tar archive should be extracted? Yes they can use absolute paths) or DESTDIR style installs into a staging directory before creating archives.

In my experience this scenario comes up far far more often than the config file scenario you described, though I guess it depends on what sort of software you work on.

Definitely does :grinning_face_with_smiling_eyes: I can't say I've ever encountered your use case, while many, many programs take user-provided paths and must absolutize them.

rust's Path type has its own semantic model, it's more than the lower level representation of a Vec, for example, it differentiate the root directory, the special entries . and .., Windows UNC path, etc. see std::path::Componenet.

if your use case needs the semantics of a Vec of bytes, PathBuf::into_os_string() gives you the underlying OsString of the PathBuf, which is just a Vec of bytes (in platform specific encoding).

if you think about it, the semantics of this operation actually should first convert the absolute path into a relative path (using the given prefix as base), which can be done with Path::strip_prefix().

I would consider absolute paths in archive files a security risk, so the code should already have checked for this special case anyway. depending on the use case, it may reject such archives, or it may translate absolute paths into relative ones, or it may need an explicit user interaction or configuration to allow absolute paths. in any case, PathBuf::push() alone is not the enough to handle this.

2 Likes