Skip to content
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

std::path::Component makes platform specific assumptions; cannot be extended #18

Open
ids1024 opened this issue Apr 18, 2018 · 9 comments
Labels

Comments

@ids1024
Copy link
Contributor

ids1024 commented Apr 18, 2018

The Component enum includes a Prefix variant that only appears on Windows, which is not ideal. Optimally, OS specific functionality should not be exposed on other OSs. Worse, operating systems with paths that are noticeably different from Unix and Windows cannot be represented by this enum.

This has been an issue for Redox OS, which includes "schemes" in paths, like file:/path/to/file and sys:uname. We haven't been able to add a variant to the enum, since this would be a breaking change (by breaking exhaustive match statements).

Resolving this without breaking backwards compatibility is problematic. Making the enum non-exhaustive and adding new variants presumably would not possible for a future edition of Rust (it would break the ABI of the enum, and there is no sane way to automatically fix exhaustive matches).

I guess that just leaves deprecating Component, and adding a better alternative alongside it? This does seem like a genuine portability issue, and I'm sure Redox is not the only OS that would run into difficulty here, though I can't immediately think of any others.

@ids1024 ids1024 changed the title std::path::Component makes platform specific assumptions; cannot be extende std::path::Component makes platform specific assumptions; cannot be extended Apr 18, 2018
@Ericson2314
Copy link
Collaborator

Excellent concrete case to bring up. This enum is already a bit silly cause some variants only come at the beginning of the path and some only in the middle. This means any use of the components iterator will probably blow up in absurd cases anyways, so exhaustiveness of the enum is not that important. I'd deprecate it and make something wholly new and better.

Relatedly, I think the portability lint should effect enum exhaustiveness like so:

enum Foo {
  #[cfg(x)] Bar,
  #[cfg(or(x,y))] Baz,
}

#[cfg(x)] 
fn ok_for_x(a: Foo) {
  let Bar(b) = a;
}

#[cfg(or(x,y))] 
fn ok_for_either(a: Foo) {
  match a {
    Bar(_) => (),
    Baz(_) => (),
  }
}

#[cfg(x)] 
fn bad_for_either(a: Foo) {
  let Bar(b) = a;
}

of course, this is only sound if the portability lint is an error rather than warning, so it will be a while before we can do such things.

@jethrogb jethrogb added the std label Apr 18, 2018
@jethrogb
Copy link
Collaborator

Another issue: what if I'm on Linux (or whatever) and I want to work with Windows-specific paths? (Or the other way around)

@Ericson2314
Copy link
Collaborator

Ericson2314 commented Apr 18, 2018

@jethrogb That's valid too, though to be clear this already fails for that because the Normal(&'a OsStr) variant would be Linux-specific.

@FenrirWolf
Copy link

I'm sure Redox is not the only OS that would run into difficulty here, though I can't immediately think of any others.

Ran into this with the Nintendo 3DS of all things. The homebrew lib I wrap over for out-of-tree std support uses path prefixes similar to what Redox does.

@jackpot51
Copy link

Related issue: rust-lang/rust#52331

@ids1024
Copy link
Contributor Author

ids1024 commented Jan 18, 2019

I think there are a few ways to fix this.

  1. If possible, find a backwards compatible way to add new platform specific functionality to the existing enums. But I can't think of any, and this could get ugly.
  2. Deprecate components() without a replacement. I don't think it's used very often at all, except in std for implementing methods on Path, but perhaps I would be surprised. Code that needs functionality would be encouraged to find suitable crates for path handing.
  3. Deprecate components(), and replace with OS specific extension traits, called something like std::os::unix::path::PathExt, std::os::windows::path::PathExt, std::os::redox::path::PathExt, etc. with some replacement. This would eliminate portability issues, at the cost of requiring code to implement separate behavior per platform, and arguably cluttering std a bit. The lack of a non-platform-specific API would be annoying, but is to some extent unavoidable.
  4. Deprecate components(), and create a new cross platform version. There are several possible designs, but I think any is likely to involve non-exhastive enums to potentially support different kinds of paths another platform might support.
  5. I suppose another option is to add an iterator over plain OsStr, instead of a type that provides specific information. This should be entirely portable, but provides less functionality.
  6. Provide the functionality of option 5 but with freedom for future extension. Replace with an iterator over a new type that is opaque, and only has the as_os_str() method. More functionality could be added. This does commit std to a pure iterator version though, while it could be considered better to have the prefix separate from the rest of the path.

I'm thinking perhaps the best choice here is to deprecate without replacement, while leaving the option to add a new API in the future if anyone can think of a sane one and there seems to be enough demand to include it in std.

The cost of deprecating probably isn't major, because I don't think it's used much. Is there a good way to determine/estimate what percentage/number of crates.io crates use this API?

If it is used more commonly than I expect but users only need strings for the components, then I might change my preference to option 5.

Edit: On the other hand, accessing path components seems like a pretty fundamental feature of a path type. But the most common cases are handled by other methods, and I expect very few crates (if any) individually handle each variant of Component and Prefix. So option 5 might handle it, but I don't know what uses of this API in the wild are like.

Edit: Added option 6.

@roblabla
Copy link

deprecate components without a replacement

Please no. Components is a perfectly reasonable API, and at least I personally have used them multiple times to, for instance, inspect if a path starts with a Root or a Prefix, and if so pop said prefix in an idiomatic/functional way. It's a really nice API whose only problem is that it makes overeager assumptions about what a Path should look like with no path for extensibility.

It's worth noting that, for the specific case of Redox laid out in rust-lang/rust#52331 (which I have a specific interest in since I'm maintaining an std port with a platform that has a similar path system), kennytm proposed a design that is not a breaking change and works OK, though it does slightly overload what a Prefix is. It's what I ended up doing in my STD fork.

@ids1024
Copy link
Contributor Author

ids1024 commented Jan 18, 2019

Please no. Components is a perfectly reasonable API, and at least I personally have used them multiple times

Fair enough. I'm probably underestimated the utility of the function, and I suppose it is necessary to have some kind of API for this.

The fundamental issue here is that kind() method and the Prefix type it returns. So deprecating and replacing just that is an option.

kennytm proposed a design that is not a breaking change and works OK, though it does slightly overload what a Prefix is.

An issue I have with @kennytm's suggestion is the deprecation only on Redox. It also affects other potential platforms, and it is also desirable to warn developers about the portability issue even when they're not compiling for such a platform.

And is deprecation warnings for just one target something Rust currently does? It seems like an odd thing to do generally, even if it's technically possible.

@MggMuggins
Copy link

I would understand that a deprecation warning done like that to be an indication that something needs to be done better in terms of organization, rather than just a stopgap telling people not to use a feature because it isn't applicable on a certain platform. However, it would certainly work in the short term.

For a more permanent fix, given that the design is not suitable for parsing paths for other OS's, I'd recommend modifying Prefix so that it is relevant to the current platform. Eg, the current symbols only being visible when compiling for Windows, and different symbols for unix, redox, etc.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants