Proposal: make UnixStream *type* available on Windows

TL;DR: make UnixStream type available as dummy API on Windows, so using this type on Windows will be runtime error on Windows instead of compile-type error on Windows.

I have a couple of libraries I maintain.

These libraries work on Windows, more or less. I do not test them explicitly (I don't have an access to Windows machine and configuring Windows CI for a small project is painful), but since TCP sockets work similarly on all OS, these libraries work on Windows too.

On Unix (Linux, macOS) these libraries also support Unix sockets. Unix sockets do not work on Windows, and that's fine.

However, I often encounter a problem, when I break compilation of my libraries on Windows simply because I have to guard all Unix/non-Unix specific code with cfg(unix) and cfg(not(unix)).

I change for example unix-specific function signature and forget to change windows-specific signature (and if I correctly change Windows-specific signature, I don't know how I could test it).

So my proposal.

Let's make UnixSocket type available on Windows (and all other environments). connect and bind operations would return io::Error "not supported".

So all library code will be typechecked for operating systems in single cargo check run.

And people can continue using cfg(unix) or cfg(not(unix)) if they wish to hide some functions from their projects in specific environment.

What others do

Java

Some other projects do it. For example, Java many years resisted introducing OS-specific functions into the JDK, but now Java provides basic access to OS-specific API. The example is, now in Java it is possible to read POSIX file attributes (like owner and group).

Function getFileAttributeView accepts PosixFileAttributeView object (which is available on all operating systems), but returns null if POSIX file attributes are not supported.

Node.js and libuv

Node.js and libuv actually implement unix-socket-like API on Windows using pipes (so the path-socket code actually works across different operating systems except that paths on Windows must be not filesystem paths, but something like \\?\some\string).

Edit: typo in type name

I don't think this is a good way forward. It pushes these errors further back and makes it harder to find. Now instead of breaking builds (which you could detect with CI that just checks if something builds on a specific platform), you won't know that you've broken something until much later, once someone tries to use the function, at which point it becomes much harder to work around. Maybe you could run cargo check --target some-windows-target in CI, that way you don't need to configure a Windows CI?

Worse still, with this change, you won't know that you are writing platform specific code until you are well underway, and it may be hard to rewrite your code after the fact to make it platform agnostic. Right now you have to write #[cfg(..)] to write platform specific code in a crate that's meant to be multi-platform,

If you don't care about a specific platform, then you can always stick a #[cfg(not(any(/* supported platforms */)))] compiler_error!("platform not supported").

Java and Javascript don't have great support for conditional compilation (if any at all), so they must fallback on these sorts of techniques to get around it. But we can and do better with, conditional compilation, by pushing errors as early as possible.

6 Likes

I assume you mean UnixStream? There's another recent thread, though no real conclusion...

1 Like

If you don't care about a specific platform, then you can always stick a #[cfg(not(any(/* supported platforms */)))] compiler_error!("platform not supported") .

No, I can't. The last straw before I wrote this post was: I changed edition to 2018, and code broke on Windows because I used ::error in a function which was guarded by cfg(not(unix)) and it no longer worked in edition 2018, because I needed to write crate::error now.

And I learned about this mistake only when someone reported a bug. I committed a fix, and I did not even check it because I don't have access to a Windows host.

(Previously I had Windows CI, but I killed it because it did more harm than good: it randomly crashed, it was very slow, and I could not find how to configure it to install certain system environment dependencies like prebuilt OpenSSL.)

Maybe you could run cargo check --target some-windows-target in CI

It would be great if it was possible to do it, but AFAIU it's practically very hard (for ordinary joe working on pet project in free time) to make it work on mac or linux, for example.

It pushes these errors further back and makes it harder to find.

My experience of writing production code in Java for many years tells me otherwise: having environment-specific stubs (OS-specific code, deployment-specific code, database-specific code, IPv4/IPv6-specific code etc etc etc) chosen at runtime was quite convenient and rarely caused troubles. But I can be biased of course.

1 Like

Ok, I didn't realize that, it's been relatively easy for me to use github's new built-in CI (Github Actions on beta) with the Rust plugin to get cross-platform build-checks (and test runs), but other CI's may make this harder (never had much experience building/testing Rust on other CIs).

Ok, I may have jumped the gun a bit. :sweat_smile:

OK, maybe it's time for me to learn how to use github actions. Thanks!

2 Likes

If you need a decent starting place, I have what I believe to be a decent CI setup for testing on multiple operating systems and targets, in addition to feature gates, formatting, and clippy. The config is present in the time-rs/time repo.

Maybe you could implement your own type that has this interface?

#[cfg(windows)]
pub use crate::my_fake_stream::UnixStream;

#[cfg(not(windows))]
pub use std::os::unix::net::UnixStream;

Such fake stream could be published as crates-io crate for others who have the same problem.

1 Like

I'm not sure exactly what CI you're using, but I believe this can be as simple as adding the following to commands to your CI script (even if your CI is running only on linux):

- rustup target install x86_64-pc-windows-gnu
- cargo check --target=x86_64-pc-windows-gnu
3 Likes

Own UnixStream type won’t help type checking with mio or tokio for example.

I’m afraid this won’t work easily when dep crates have native dependencies like OpenSSL.