-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Implement Display
for Path
and PathBuf
#49868
Conversation
In [RFC rust-lang#474][rfc474], the issue of how to handle Displaying a Path was left as an open question. The problem is that a Path may contain non-UTF-8 data on most platforms. In the implementation of the RFC, a `display` method was added, which returns an adapter that implements `Display` by replacing non-UTF8 data with a unicode replacement character. Though I can't find a record of the discussion around this issue, I believe there were two primary reasons not to just implement this behavior as the `Display` impl of `Path`: 1. The adapter allocated in the non-UTF8 case, and Rust as a rule tries to avoid allocations that are not explicit in code. 2. The user may prefer an alternative solution than using the unicode replacement character for handling non-UTF8 data. In my view, the choice to provide an adapter rather than implement Display has had a high cost in terms of user experience: * I almost never remember that I need an adapter, forcing me to go back and edit my code after compiling it and getting an error. * It makes my code more noisy to have the display adapter; this detail is rarely important. * It is extremely uncommon to actually do something other than call the display adapter when trying to display a path (I have never wanted anything else myself). * For new users, it is Yet Another Compiler Error that they have to figure out how to solve, contributing to the sense that Rust nags nags and obstructs rather than assists & guides. Therefore, I think time has shown that this has been a detriment to user experience, rather than a helpful reminder. That leaves only the first reason not to implement this: implicit allocations. That problem was happily resolved in June 2017: rust-lang#42613 provided an alternative implementation which efficiently avoids allocations. Given that, I think it is time that we implement `Display` for both `Path` and `PathBuf` and deprecate the `Path::display` method. r? @alexcrichton cc @rust-lang/libs [rfc474]: https://github.com/rust-lang/rfcs/blob/master/text/0474-path-reform.md)
Probably checkboxes are justified for this: @rfcbot fcp merge |
Team member @withoutboats has proposed to merge this. The next step is review by the rest of the tagged teams: Concerns:
Once a majority of reviewers approve (and none object), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
Your PR failed on Travis (raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem. Click to expand the log.
I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact |
So what I've learned from the logs is that deprecating this is going to cause a lot of warnings. Possibly we should just provide the new API without deprecation? |
Strong 👍 from me on this. I work with paths a lot, and the number of times I've benefited from the fact that I feel like deprecating the |
In general, we shouldn’t emit deprecation warnings in Nightly before the recommended replacement is available at least in the Stable channel. But I’m not convinced we should mako this specific change. @rfcbot concern correctness I believe that the reason not to implement |
It seems overwhelmingly more common to want to display paths for user consumptions than to try to use the display impl to "round trip" them. I also suspect that these issues would not be hard to debug - they seem very likely to appear as some invalid path which contains the unicode replacement character (I'd agree with the contention that they likely would not be caught in testing unless you're doing fuzz testing with randomly generated valid paths.) And I'm not convinced that forcing users to go |
Your PR failed on Travis (raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem. Click to expand the log.
I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact |
@SimonSapin An interesting case where this comes up easily is error messages. Namely, I'm pretty sure I almost always use My point here is that, even with the adapter method, I'm already doing the technically incorrect thing, largely because it seems border line impossible to do the right thing. Namely, how am I supposed to print an error message that might not be valid UTF-8? Our error messages are irrevocably tied to valid UTF-8 because our formatting infrastructure is tied to valid UTF-8. I would need to seriously go out of my way to get this right. Now, I don't think this is a huge deal in and of itself, but the On the other hand, there are cases where a program wants to print a file path for non-error reasons, e.g., an #[cfg(unix)]
fn write_path<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
use std::os::unix::ffi::OsStrExt;
let path = path.as_ref().as_os_str().as_bytes();
self.wtr.write_all(path)
}
#[cfg(not(unix))]
fn write_path<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
let path = path.as_ref().to_string_lossy();
self.wtr.write_all(path.as_bytes())
} In other words, I not only need to abandon our nice formatting infrastructure, but I need to write platform dependent code to do it. I'm with @withoutboats on this one. While I appreciate the |
I definitely sympathize with the problems mentioned here, I run into them all the time myself as well. I feel, though, that we've got a lot of messaging to undo if we start down this road (which I'm not sure we want to do). For example the documentation for
which directly implies that Along those lines, though, I would have a question of why stop here? If our goal is to make Is there a strong rationale for stopping here and not adding further conveniences? |
To me this seems like a very strange way to frame the question! Every decision is a trade off & should be judged on its own merits. Why allow the display adapter at all - I believe most users use it thoughtlessly without question & don't actually consider whether its correct; why shouldn't we make them jump through more hoops to lossily display their Paths and potentially introduce errors? Why should Option, which parent returns, have an unwrap convenience when it can be the source of runtime panics? Etc etc, you can ask infinite questions in either direction on the line between Coq and JavaScript. We can't make some sort of sweeping philosophical argument in a great "convenience vs correctness" debate. For each API decision we have to ask: is this choice improving users' lives & work? I think this is a case where our historical decision was a clear net negative for users. |
@alexcrichton Yeah I'm not sure if there is a super strong rationale separating these things. It's definitely a little blurry, so we'd probably need to employ reasoning like, "paths are frequently used and printed, and the specific display adapter we have for it isn't carrying its weight." It's not an especially generic argument that can be applied to other things I think. The docs on |
Does deprecating a stable interface not require a RFC? Edit: If we need a RFC to deprecate a stable interface, I think introducing a replacement of the current interface without deprecating also needs a RFC:
|
Path::display
, implement Display
for Path
Display
for Path
and PathBuf
The problem with implementing Strong opposition from me on this change. |
@withoutboats I agree in general yes but I'd personally find the decision to only add Put another way I feel like there's a lot of "papercuts" when working with I think there's perhaps an argument to be made, however, that the "eat your vegetables" approach we have today ends up benefitting nearly no one. Those who are aware of the issues don't necessarily need the constant ergonomic overhead and those who aren't aware of the issues are likely to do the (subtly) wrong thing anyway. Even if this provides a strong motivation for this change though I think we have more issues to work through than just adding a |
Yes, this is my opinion. |
I think I agree with @retep998 that the biggest footgun this opens up is enabling Rust already has way to many conversion mechanisms that we try hard to keep somewhat sane, with beginners often just blindly trying stuff until it works. And most languages don't bother to keep strings and paths as separated as Rust does, so people often don't expect to look out for it. But its also true that there already many ways to misuse path-string conversions in other ways, so I also understand seeing this as a needless hurdle in thew API. |
I agree that the "biggest footgun" is When this had a deprecation warning I immediately found lots of places checked into this repo that were doing exactly this |
I don't think I personally agree with |
Another way to think about this perhaps is that if you do want to "do the right thing" then it's nearly impossible to audit with |
One way to approach this problem which I would personally be more comfortable with is to have |
@alexcrichton That's tempting, but the problem there is when formatting error messages. If you do |
@BurntSushi true yeah, but I feel like we have to give on something here. We've explicitly documented that we cannot do what this PR currently proposes, so something's gotta give. I would personally prefer to stick to the "Display is lossless" guarantee moreso than not panicking, and our messaging would be that if you want to make sure you're compatible with non-utf8 paths then you should have a test case (as that's what you'd really need anyway). It's true that panics will come up from time to time, but it's sort of just another "here's a reminder you need to handle a bug here". It's not as obvious as |
@alexcrichton Yeah I guess what I'm trying to say is that if our choice is between "status quo" and "impl Display, but panic on invalid UTF-8," then I'd probably pick the status quo. My opinion has weakened on this over time. I dunno what to do. The |
I don't think people use or understand display as lossless - I think they use and understand it as pretty printing intended for the end user. For example, the Display impl of the Context type in failure prints only the context and not the underlying error - two different Contexts with the same context but different errors will Display the same (and this wasn't my design, it was @aturon's). This is based on this much looser definition of Display which is how I think I'm unclear how the existing situation upholds that requirement either; why is the Display impl for the |
This does seem like a case where people aren't clear on what the alternative is. If you really, really want to emit a non-UTF-8 path, presumably you'd have to treat the |
This has not had a comment in many weeks, so ping from triage. |
Which of course doesn't work work on Windows. |
@aturon had an alternative proposal |
Ping from triage @rust-lang/libs! What's the consensus on this? |
Ping from triage @rust-lang/libs, what is the status of this? |
@TimNN I think the status is that there is no consensus so far. @smmalis37 Disposition only means that one team member proposed FCP in that direction. #49868 (comment) lists a concern that blocks the start of FCP. |
I think we don't have time to hash this out right now, will revisit in the future. |
In RFC #474, the issue of how to handle Displaying a Path was left as an open question. The problem is that a Path may contain non-UTF-8 data on most platforms. In the implementation of the RFC, a
display
method was added, which returns an adapter that implementsDisplay
by replacing non-UTF8 data with a unicode replacement character.Though I can't find a record of the discussion around this issue, I believe there were two primary reasons not to just implement this behavior as the
Display
impl ofPath
:In my view, the choice to provide an adapter rather than implement Display has had a high cost in terms of user experience:
Therefore, I think time has shown that this has been a detriment to user experience, rather than a helpful reminder. That leaves only the first reason not to implement this: implicit allocations. That problem was happily resolved in June 2017: #42613 provided an alternative implementation which efficiently avoids allocations.
Given that, I think it is time that we implement
Display
for bothPath
andPathBuf
and deprecate thePath::display
method.r? @alexcrichton
cc @rust-lang/libs