I took some time today to try to better understand the porting
abstractions in the current implementation, comparing today's
tree with arcnmx's last PR on the subject.
I was mostly trying to understand the existing sys abstractions
better, and looking at places in std that were doing
platform-specific stuff without going through sys.
My main goal is to identify concretely some simple incremental steps
we can begin to take to make porting std easier. We don't need to have
the whole porting story figured out right now to start making progress
since it's all details internal to std.
Overall organization
The std::sys module is intended to encapsulate platform-specific
implementations and features. The rest of std treats it as a platform
abstraction layer.
It's organization is difficult to understand because there are a lot
of tangled reexports.
- The
std::sysmodule is private, and defined conditionally based
on unix vs. windows. It corresponds to either "sys/unix/mod.rs" or
"sys/windows/mod.rs". - The private
std::sys_commonmodule andstd::syshave an unclear
relationship. In generalstd::sys_commonbuilds onstd::sysand
can be thought of as "the Rust runtime". Most modules in std that
need platform abstraction call intostd::sys_common, which
encapsulates the platform specific stuff. This is probably the
starting point for redefining the Rust platform abstraction layer. std::sys_commonlives on the file system insys/common.- The
std::osmodules. These export public platform-specific
features fromstd. So presumably there is no hope of putting them
in an abstraction layer because they break the abstractions. They
rely onstd::sys.
Review of platform-specific code in std, outside of sys.
Here I've just gone through std and looked for platform-specific code,
comparing today's branch with @arcnmx's refactorings. I've definitely
missed stuff, but this gives an idea of the state of things.
std::thread
- The
thread_local!macro contains cfgs. std::thread::localcontains platform-specificimplmod. @arcnmx
moves them tosys::thread_local.std::thread::scope_tlssimilarly contains animplmod.
std::rand
Looks fine today. Delegate's to sys::rand.
std::net
Looks fine to me. std::net doesn't contain platform-specific code.
@arcnmx's patch touches a lot of code here but it all looks incidental
to other refactoring.
std::io
There's a bit of cfg in stdio but it's just windows/not-windows.
std::process
The only cfgs are in tests.
std::fs
The only cfgs are in tests.
std::path
There are platform-specific cfgs but only of the unix-or-windows
variety. These should rightfully be sunk into an abstraction layer
because one of the porting painpoints is non-unix-or-windows platforms.
Concrete suggestions
The main reasons this RFC and its preceeding patches have been
hard to evaluate are that 1) the patches are massive, 2) it's
not clear all they do or why.
In order to move forward we need to agree on how we want std's
platform abstractions to be organized, and then refactor in that
direction in small increments.
My main immediate recommendations are focused on clarifying
two abstraction boundaries:
- The interface between std and the platform. Today this is
more-or-lessstd::sys_common, and I'm going to call it
std::pal(platform abstraction layer). - The interface between the 'pal' and its implementation. Today this
is more-or-lessstd::sys, and I'm going to keep calling it
std::sys
Separate std::sys_common from std::sys into std::pal
The way the sys modules don't reflect the file system organization,
nor make obvious what the abstraction boundaries are, is for me is a
source of major confusion.
If we consider sys_common to be the platform abstraction layer and
sys to be its implementation, we can start making a stronger
abstraction boundary between them. The first thing we might do to make
it clear is to not put them in the same sys module, for example move
std::sys_common to std::pal and make sure that std modules outside
of std::sys only call std::pal and never std::sys. Just by
doing this we will enforce that all platform-specific modifications to
std happen in std::sys only.
At this point std::pal defines the interface std expects from
the platform.
std::pal contains no platform-specific code. It's sole
responsibility is gluing together the platform-specific code from
std::sys.
In theory, since std::pal contains no platform-specific code it
could be merged into the rest of std, and std::sys itself could be
std's platform interface, but this body of code exists now, so it
makes sense to me to re-rationalize its responsibilities.
Clarify the interface between std::pal and platform-specifics
Once std is only calling into std::pal and std::pal is only
calling into std::sys then std::sys is the interface
that porters must implement.
It may not be a good interface, but everything will at least
corralled into std::sys.
We could probably at this point even write some lints that guarantee
that these two abstraction boundaries are never violated, for example
by grepping for inappropriate cfgs.
If we can just get that far, I think it would be a worthwhile
improvement on its own, and then we could take fresh stock of
the porting story and come up with further plans.