Skip to content

ACP: Method for joining multiple paths in one go #209

@GoldsteinE

Description

@GoldsteinE

Proposal

Problem statement

There’s a popular pattern for working with Rust paths: doing path.join(first).join(second) to create a PathBuf from multiple components. Unfortunately, that could result in extra unneeded allocations (which may or may not be optimized out).

More optimal version looks like this:

let mut path_buf = path.to_path_buf();
path_buf.push(first);
path_buf.push(second);

which is not pretty.

Motivation, use-cases

Extremely common. Spotted in rust-lang/rust:
https://github.com/rust-lang/rust/blob/edcbb295c9827ce38cbef4093e2c3d184923f362/src/bootstrap/doc.rs#L205
https://github.com/rust-lang/rust/blob/edcbb295c9827ce38cbef4093e2c3d184923f362/src/bootstrap/dist.rs#L2251

Deno:
https://github.com/denoland/deno/blob/cb2ca234bb39d8e02b08d2866860e8d3a00b5887/core/extensions.rs#L647

Starship:
https://github.com/starship/starship/blob/ce7f984932a97b4ad3cd6e6ece8e1c3b6022ba99/src/context.rs#L418

It’s easy to find more examples:
https://github.com/search?q=language%3Arust%20%2F%5C.join%5C(.*%3F%5C)%5C.join%2F&type=code

I’m not sure that ever happens on a hot path though.

Notably, there is a one-line way to do it, but it’s hard to discover, so one possible alternative would be to document it better.

[path, first, second].into_iter().collect()

It also doesn’t handle the case where base path and other parts have different types well, which seems to be quite common.

Solution sketches

impl Path {
    // name bikesheddable
    pub fn join_multi<P: AsRef<Path>>(&self, paths: impl IntoIterator<Item = P>) -> PathBuf;
}

impl PathBuf {
    // name extremely bikesheddable
    pub fn with_multi<P: AsRef<Path>>(self, paths: impl IntoIterator<Item = P>) -> Self;    
}

Used like

path.join_multi([first, second])

Another alternative design could be a builder-like interface to PathBuf:

impl PathBuf {
    fn with(self, path: impl AsRef<Path>) -> Self;
}

used like

path.to_path_buf().with(first).with(second)

or even

path.join(first).with(second)

It could be a bit more wordy, but has a benefit of not requiring first and second to be the same type.

Links and related work

Zig has a join() function for concatenating a list of paths: https://ziglang.org/documentation/0.6.0/std/#std;fs.path.join

I don’t think C++ has such a method.

This issue is much less relevant to GCed languages, so I don’t think it’s very useful to look at their APIs, but e.g. Python has os.path.join() / PurePath.joinpath() and Go has path.Join().

What happens now?

This issue is part of the libs-api team API change proposal process. Once this issue is filed the libs-api team will review open proposals in its weekly meeting. You should receive feedback within a week or two.

Metadata

Metadata

Assignees

No one assigned

    Labels

    T-libs-apiapi-change-proposalA proposal to add or alter unstable APIs in the standard libraries

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions