Skip to content

ACP: PathBuf::has_dir_suffix #335

Open
@clarfonthey

Description

@clarfonthey

Proposal

Problem statement

Rust's Path types require this slash to treat paths as a directory. While methods like push will always treat the current path as a directory, other methods like set_file_name and set_extension won't.

Motivating examples or use cases

It's common for users to write paths while omitting the trailing slash, like so:

windows_path = "C:\\Users\\Me"
unix_path = "/home/me"

When converting these strings to paths via methods like Path::new, it could be desirable to normalise these into the trailing-slash form to ensure that methods work correctly, like set_file_name. For example, a method to check for a configuration file in a directory might look like this:

let mut buf = PathBuf::from(some_path_string);
for filename in &[/* ... */] {
    buf.set_file_name(filename);
    match File::open(&buf) {
        Err(err) if err.kind() == io::ErrorKind::NotFound => continue,
        res => return res,
    }
}

In this case, the set_file_name call will only work as expected if the initial path is a directory. If it's not, the last component of the directory will be overwritten.

Solution sketch

impl Path {
    // whether the path ends in a directory separator
    fn has_dir_suffix(&self) -> bool;

    // makes this path have a dir suffix, which may involve copying it to a new buffer
    fn with_dir_suffix(&self) -> Cow<'_, Path>;
}
impl PathBuf {
    // adds or removes a directory separator such that `has_dir_suffix` has the given value
    fn set_dir_suffix(&mut self, suffix: bool);
}

Alternatives

As mentioned above, push("") will be equivalent to set_dir_suffix(true), since adding an "empty" component will simply add a trailing slash. However, it's not clear that this will always work as expected, and that's why this method is proposed.

In particular, set_dir_suffix has some obvious properties that aren't clear from push(""):

  • The result will always have file_name() == None
  • The method is idempotent (calling it multiple times is equivalent to calling it once)
  • You can also remove the dir suffix without having to verify these invariants

Links and related work

N/A

What happens now?

This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.

Possible responses

The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):

  • We think this problem seems worth solving, and the standard library might be the right place to solve it.
  • We think that this probably doesn't belong in the standard library.

Second, if there's a concrete solution:

  • We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
  • We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ACP-acceptedAPI Change Proposal is accepted (seconded with no objections)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