A vision for portability in Rust


I have to agree with @comex here, this sort of feature sounds awesome.


Doesn’t the portability lint do basically the same thing, though?


Obviously this is unstructured speculation, but you can model the various sub-modules of std as APIs (think std::thread which has spawn(), current() etc.) with associated types (Thread, JoinHandle etc.).

A trait based model would have a std::thread “trait” that specifies that API with some number of impls for various platforms. So far, so obvious, I think this is what everyone is picturing in the last few comments.

If we allow some std-only magic (or a new language feature) we could implement the std::thread module’s API through the trait statically (so the code says std::thread::spawn() but that is compiled down to <std::linux::x86_64::gnu::thread::Impl as std::thread::ModTrait>::spawn() (i.e. the existing API stays usable, but is backed by a statically-dispatched, selected-at-compile-time implementation, that depends on platform and/or, as @matthieum suggests mocking or scope attributes).

Shouldn’t slow down compilation speeds, since all the translations are fixed for a single compile, and won’t slow down the generated code, since the function calls are all static-dispatch.

Wild speculation begins here Expose all (not just std) module APIs through a trait rather than unbound functions so any module can be swapped out for specialisation or testing purposes. End wild speculation


I’d love to participate in the discussions. (Though that may be overloading myself a bit too much)


Huh, you might even be able to do that in stable Rust with #[cfg] and default type args:

// `std`
#[cfg(os = "linux", ...)]
pub use std::linux::...::Impl;

#[cfg(os = "osx", ...)]
pub use std::osx::...::Impl;

pub mod thread {
    pub trait ModTrait {
        fn spawn();
    pub fn spawn<ThreadImpl=std::Impl>() where ThreadImpl: ModTrait {

Not sure if there’s a way to make the trait bound ‘invisible’ to the caller until they want to provide their own impl type, or if that matters.

I’m torn between how this would let you use two different versions of the standard library in the same codebase (“let me just quickly use my little DebugStdImpl here…”), but it would also let you use two different versions of the standard library in the same codebase (“Ah no see that file got opened by our NFS overlay on top of std::fs, but this file…”).


I would never suggest run-time dispatch :slight_smile:

@pierzchalski: I was more thinking of something akin:

#[cfg(os = "linux", ...)]
use std::linux::thread::Impl;

pub mod thread {
    pub fn spawn() { Impl::spawn(); }

Without the trait impl nor the trait usage being visible.

Also, I would prefer “magic” to bring the trait implementation in, so that the trait can be implemented by a crate outside of std. This would make supporting alternate platforms much easier.

Want to play on an embedded ARM target? In the target description file, specify your own crate as providing the low-level std trait implementations, and hop you’re up and running without patching the official std crate, all statically dispatched.


Thanks, all who have responded! There are already some very interesting issues being raised in this thread, and next week I’ll try to summarize them in another post.

In the meantime, those who want to be part of the working group, can you please fill out a scheduling poll and make sure to include your email address? We’ll have a video meeting at a time that works for all, and go from there.


One of the questions i suppose that i had about the portability linter, as described in the RFC uses a SAT solver, and whether this should aim to leverage the solver built into chalk in some fashion, or not.


Another thing that might be good to address in the portability story is the situation with path prefixes. Apparently Redox has its own style of prefixes that can’t be accounted for without breaking backwards compatibility.


I’ve sent meeting invites for those of you who responded to the doodle poll. I’ll try to write up some summary notes before then.

See you all next week!


I am currently working on porting rustc to the GPU. The project is called rlsl. See the latest blog post for more infos.

The portability initiative seems amazing. Although so far my experience with no_std has been pretty good, I only re-export the things that I need and the unsupported functions never get collected because they are not exposed.

I am not sure how reasonable the cfg approach would be. For me, I think there would have to be a cfg attribute on almost every item.


I think we need an ergonomic way to define crate-level target configuration (os, feature, arch, etc.) which will be translated to all items inside it and perhaps module-level as well. From user perspective Cargo.toml is ideal place for it, but some raised concerns that such coupling could be undesirable.