Consider adding a contains function to std::path::Path

Hi

I was experimenting with filtering directories when iterating over them and it surprised me that std::path::Path has starts_with and ends_with methods but no contains. contains in this context would check if a path contains a component like /my/test/dir contains test.

Now you could use the component iterator to accomplish this. However to me it seems like a simple utility which is a natural complement to starts_with and ends_with. Is there a reason for not having a contains method? If not should we consider implementing it?

When would you use it? After all, the meaning of a path depends on what comes before it. The only thing I can think of is checking for .., but in situations where you’re doing that you usually want to check other things about the path anyway (like whether it has a symlink in the middle).

One case I can think of is enforcing naming rules on other platforms. For example, /path/with/NUL/in/it is bad because Windows cannot represent this path given the reserved name NUL anywhere. However, the rules are complex enough that .contains() doesn't make sense even as such a solution IMO.

If all you want to do is check if a path contains some component, you can do that with path.iter().any(...): Rust Playground

But I would expect the actual use case for contains to span path components, like starts_with and ends_with do. It is possible to do a contains check as long as the path is representable as a string, even though there are some hoops to jump through.

This isn't saying anything about whether Path::contains() is justified. But if it is, it would be better than hoping that the path is representable as a string.

1 Like

Without .as_str(), matching componentwise: [playground]

pub fn path_contains(haystack: &Path, needle: &Path) -> bool {
    let mut iter = haystack.iter();
    loop {
        if iter.as_path().starts_with(needle) {
            break true;
        }
        let Some(_) = iter.next() else {
            break false;
        };
    }
}

or defined recursively: [playground]

pub fn path_contains(haystack: &Path, needle: &Path) -> bool {
    haystack.starts_with(needle) || {
        let mut iter = haystack.iter();
        _ = iter.next();
        let rest = iter.as_path();
        !rest.as_os_str().is_empty() && path_contains(rest, needle)
    }
}

I don't think std can do much better internally than this external impl, basically only by swapping the terminal condition from haystack.len() == 0 to haystack.len() < needle.len()

1 Like

My initial example was too simplistic. @parasyte's suggestion is more a long the lines of what I was thinking. Where Path::contains checks for a span of components similar to the example in @CAD97 's response.

@jrose My case was to check for commonly used directory names like .git, target, node_modules etc.

I was thinking about this incorrectly. It felt like a gap in the methods implemented for std::path::Path but I can see how this is probably not a common use case.

Thanks for the feedback :slight_smile:

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.