RFC: A portability lint by aturon · Pull Request #1868 · rust-lang/rfcs (original) (raw)
Thanks for writing this up @aturon! I know it's been a long haul getting here but I'm quite excited to see this RFC as I think it strikes a great balance with all the thoughts we've heard so far.
One thing I always find useful is to game out common usage patterns and see what they would look like. First I figured it'd be good to take a look at the approach the standard library takes in the definition of std::fs
. I've seen the same pattern across the ecosystem, such as the mio crate, as well.
In std::fs
we've got (roughly):
pub struct File { inner: imp::File, }
impl File { pub fn cross_platform_method(&self) { self.inner.cross_platform_method() } }
The actual definition of imp::File
is platform-specific and so will have a #[cfg]
annotation leading up to it. For the cross-platform File
to actually use the inner version it then needs to say that it's abstracting over platforms. That leads me to two questions:
- For the type definition, I don't think this RFC provides a solution other than allowing the lint, right?
- For the method we could use
match_cfg!
, but I feel like that'd get burdensome. Each arm of the match would be the same (self.inner.cross_platform_method()
) and this'd be repeated for all methods (quite a few here!).
I think that the most ergonomic solution for this would be to disable the lint inside these modules, right? Basically std::fs
would have #![allow(nonportability)]
at the top?
In terms of other concrete cases I like the ergonomics of writing a platform-specific API. Once you're in a #[cfg(unix)]
you've unlocked the whole Unix world of APIs for free, which semantically feels right.
I personally feel that match_cfg!
may not wish to be built into the compiler/standard library from the get go. If we like the ergonomics of it then I think we should add it on that basis, but not as a fundamental piece of the puzzle for the platform compatibility lint.
Ideally I think we could add a definition like https://is.gd/qvrAm0 which basically just expands to a bunch of cfg expressions on statements. The crucial piece that match_cfg!
does in this RFC though which a crate implementation wouldn't do is propagate the any
business. That is, it wouldn't propagate that the expression is the any
of the possible arms.
While writing this comment though @Kimundi brought up an interesting point that it might be pretty awesome if we can auto-detect something like this. I would imagine it's very hard to do (running resolution multiple times... kinda), but the benefits would be that my example above wouldn't need any modifications either.
In terms of subsets, I feel like that's where the RFC gets very hand-wavy. Splicing out threads, compiling for emscripten, or trying to compile out floats I feel will need more thought than just applying the #[cfg]
lint we're concocting here. For example, some questions I might have are:
- How do I tag a crate as emscripten-compatible? If the default set of enabled cfg is the standard set, then any API I export will require the threads cfg, right? Put another way, I think the goal here is to compile for Linux but get warnings about emscripten incompatibility? How would that work?
- What does the API evolution story look like for excising a portion of the standard library for a platform? Even if it's a lint it'd be good to have in mind what the warnings would look like.