Description
tl;dr Should nix be split up into multiple crates named nix-*
and what should the API style be?
The situation
Nix originated as a posix binding dumping ground for various crates that I had been working on. I tended to need the same bindings over and over again, so I just moved them into Nix. I thought this would be useful for other people, so I accepted PRs for various system APIs as they have been submitted.
Everything has been fine so far, but a few issues have arisen.
As the API coverage has increased, whenever a breaking change is made, the semver must be incremented. So, if the ioctl
module changes an API as it figures out the best way to expose functionality, the version must be bumped and downstream crates must change their dependencies. For example, Mio depends on nix, but not on any ioctl
features, so bumping the dependency is pretty annoying.
Secondly, the API style has been somewhat ad hoc, especially with more complicated system APIs like fcntl
.
Crate structure
There is value in having a single place (nix) to find system APIs that have a coherent API style and a single documentation site. I think that each specific API silo, like sockets, ioctl, mqueue, signals, etc... could be in their own crate named nix_*
. Common features like errors, NixPath
, etc... would live in nix_core
. The nix
crate would be a facade depending on all the sub crates with liberal dependencies (probably "*"
) which would allow the consumer to optionally be able to pin each sub crate to whatever.
Libraries like Mio could then depend only on the features that are needed.
Also, it would allow delegating responsibility better. I, for one, know nothing about ioctl
. I would like to stop being the blocker.
API style
This is still something that I haven't figured out. I do know that the goal of nix is *comprehensive, safe, zero cost bindings to system APIs on nix OSes.
So, what does that mean for nix? As of now, there are currently a variety of styles. For example, most APIs are 1-1 bindings to the equivalent OS api. This leads to APIs like fcntl
which uses a convoluted FcntlArg
strategy. ioctl
on the other hand uses a (fairly impressive) macro...
The question is, would it be better to expand beyond the 1-1 rule. For example, in the fnctl
case, the API would most likely be much cleaner and more "rusty" if each "fcntl arg" was moved to an individual function. There could, for example, be a public fcntl
mod with a dupfd
function. Using it would be:
let res = fcntl::dup_fd(fd);
Then, should it go further? Should there be an Fd
type that wraps std::os::unix::RawFd
and implement functions like dup
directly on that type?
These are all open questions that I pose to the users of nix
.
cc @posborne @cmr @geofft @utkarshkukreti @MarkusJais and whoever else :)