Skip to content

Hash implementation for Path ignores path separators #127254

Closed
@zanieb

Description

@zanieb

I tried this code:

use std::path::PathBuf;
use std::hash::{DefaultHasher, Hasher, Hash};

fn main() {
    let x = PathBuf::from("hello/world");
    let y = PathBuf::from("helloworld");
    
    let mut hasher = DefaultHasher::new();
    x.hash(&mut hasher);
    println!("{}", hasher.finish());

    let mut hasher = DefaultHasher::new();
    y.hash(&mut hasher);
    println!("{}", hasher.finish());
}

I expected to see this happen: Different hashes since these represent different paths.

Instead, this happened: The hashes are equal.

This makes sense for redundant path separators (e.g. //), but not for all path separators.


I added a test case to the standard library — which fails.

diff --git a/library/std/src/path/tests.rs b/library/std/src/path/tests.rs
index 92702b395df..ed945a764c5 100644
--- a/library/std/src/path/tests.rs
+++ b/library/std/src/path/tests.rs
@@ -1566,6 +1566,13 @@ macro_rules! tc (
     relative_from: Some("bar")
     );
 
+    tc!("foo/bar", "foobar",
+    eq: false,
+    starts_with: false,
+    ends_with: false,
+    relative_from: None
+    );
+
     tc!("foo/bar/baz", "foo/bar",
     eq: false,
     starts_with: true,
---- path::tests::test_compare stdout ----
thread 'path::tests::test_compare' panicked at library/std/src/path/tests.rs:1569:5:
"foo/bar" == "foobar", expected false, got 4111386826608627770 and 4111386826608627770

The relevant code is probably around

rust/library/std/src/path.rs

Lines 3112 to 3114 in 6292b2a

// skip over separator and optionally a following CurDir item
// since components() would normalize these away.
component_start = i + 1;
which last changed in 45082b0. If I revert that commit, the error persists. It looks like the relevant change is a083dd6, if I revert that commit the test passes. This was the solution I opted for downstream (in astral-sh/ruff#12159) before performing this investigation.

Meta

I tested this on stable (1.79.0), nightly (1.81.0), and on master (6292b2a).

Discovered via astral-sh/ruff#12158

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-ioArea: `std::io`, `std::fs`, `std::net` and `std::path`C-enhancementCategory: An issue proposing an enhancement or a PR with one.T-libsRelevant to the library team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions