-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Make two Paths unequal if they differ in trailing slash #87339
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
r? @yaahc (rust-highfive has picked a reviewer for you, use r? to override) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couldn't this break some use-cases? E.g. shells autocomplete directories with a trailing slash, if a CLI tool then tries to deduplicate paths or exclude paths by comparing them to another set of paths that's derived in some other way that doesn't append the trailing slash they might get a false negative compared to previous behavior.
At the very least it should be documented and have a release note.
library/std/src/path.rs
Outdated
@@ -2686,7 +2694,8 @@ impl fmt::Display for Display<'_> { | |||
impl cmp::PartialEq for Path { | |||
#[inline] | |||
fn eq(&self, other: &Path) -> bool { | |||
self.components().eq(other.components()) | |||
self.components() == other.components() | |||
&& self.ends_with_separator() == other.ends_with_separator() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be implemented on the Components
level instead of Path
? Otherwise they'll give different results.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Giving different results is correct. The path components are the same i.e. the Components
iterator would produce the identical sequence of Component
enum values, even if one path ends in separator and the other does not.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we say that a trailing slash is semantically different then shouldn't components preserve that semantic difference? E.g. the last component could include the trailing slash. Or there could be a final zero-length component. Ideally this would be expressed through a new enum variant but it's exhaustive so we can't extend it 🙁.
Is assert_eq!(foo, foo.components().as_path())
always true?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it follows that we need components to preserve that semantic difference.
It's often the case that code knows what kind of filesystem object it's expecting to manipulate, whether all files like a tests/ui/*.stderr
glob expansion in the trybuild crate, or all dirs like ~/.rustup/toolchains/*
. I don't see throwing an "empty" path component onto the end of the Components iterator in the dir case being helpful or desirable in the common case. The directoryness comes through based on what the code does with the paths after examining their components, such as using std::fs::remove_dir on it rather than remove_file.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it follows that we need components to preserve that semantic difference.
Well... the thing is that Components
implements AsRef<Path>
, so if you have a generic function taking it via AsRef
then it'll behave differently depending on whether you pass in a Components
or something else that implements AsRef<Path>
.
It's consistent with existing documentation, it's clearly spelled out that path.components()
will strip trailing slashes, but previously that wasn't considered a semantic difference (since Eq
considered them equal), now it is.
☔ The latest upstream changes (presumably #86898) made this pull request unmergeable. Please resolve the merge conflicts. |
merge conflicts |
e85912c
to
d628f76
Compare
@rustbot label -S-waiting-on-author +S-waiting-on-review |
Part of me feels like it would be nice to have eq work regardless of the trailing slash if the file being named is a directory, but I feel like we almost certainly don't want to be interacting with filesystem APIs to see if paths exist and are dirs when checking equality. So with all of that said this PR looks good to me. I'm going to FCP this change since it changes runtime behavior. |
@rfcbot merge |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
The final comment period, with a disposition to merge, as per the review above, is now complete. As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed. This will be merged soon. |
I do feel like we should resolve the two open comment threads before merging this |
Responded above. |
The ordering seems fine now. I still think the newly introduced semantic inconsistency between Maybe it just needs to be documented somewhere that |
Sounds good, lets do this in a follow up PR? @bors r+ |
📌 Commit b2e3862 has been approved by |
⌛ Testing commit b2e3862 with merge 44a95033fa2615ffab1182da151fa3ed531eced3... |
The job Click to see the possible cause of the failure (guessed by this bot)
|
💔 Test failed - checks-actions |
@bors r- (bors having hiccup and trying to retest failed prs) |
I think upon reflection I am going to retract this for being more disruptive than I would have wanted. An example of code that's negatively affected is this snippet from cargo where it's testing whether if let Ok(repo) = git2::Repository::discover(path) {
if repo.workdir().map_or(false, |workdir| workdir == path) { It turns out I guess code that wants the non-equal behavior should use a newtype with PartialEq and PartialOrd which mirror the syntactically_dir logic from this PR. |
It seems problematic to consider two paths equal if one of them can exist when the other does not exist.
In the previous behavior, the following asserts pass:
In this PR
path1 == path2
becomes false.