Platform and Feature Specific Code

It's quite common if you're shipping a binary to run into a situation in which you need to do something differently on different platforms. For example, you might want to use a different library on Windows than you do on Linux. You might want to use a different file path separator. You might want to use a different system call.

Use Library Code

A lot of platform abstraction is done for you by the standard library. For example, prefer using File over the nix crate to obtain a file handle. For memory-mapping, the memmap2 crate handles many of the platform issues for you.

Use Config Directives

You can make blocks of code conditionally compile based on feature flags or platform. For example:

#![allow(unused)]
fn main() {
#[cfg(all(not(feature = "opengl"), feature = "cross_term"))]
}

Will only compile if the opengl feature is disabled, and the cross_term feature is enabled. You'll often need blobs combining feature combinations to determine what to compile. It gets messy fast.

To minimize the mess, define a common interface. It could be a trait, or it could be a structure that will always offer the same methods (the trait is cleaner). Put each platform/feature implementation in a separate module and make the compilation decision at module inclusion time. For example:

#![allow(unused)]
fn main() {
#[cfg(all(feature = "opengl", not(target_arch = "wasm32")))]
mod native;

#[cfg(all(feature = "opengl", not(target_arch = "wasm32")))]
pub use native::*;

#[cfg(all(feature = "opengl", target_arch = "wasm32"))]
mod wasm;

#[cfg(all(feature = "opengl", target_arch = "wasm32"))]
pub use wasm::*;
}

Now when you compile, it only includes the appropriate module and shares the common type defined in each of the modules. That's a great way to share functionality between platform-specific implementations (which can be managed by different teams, even) without resorting to dynamic dispatch.