How to make a test guarantee that a crate was built with no_std

git clone -b broken-nostd

Here is a crate whose #[no_std] support is clearly broken. frunk_core (located under core/) uses std all over the place and has un-cfg-ed usage of Vec and HashMap.

Yet, cargo test --no-default-features succeeds! How can this be? The answer is because --no-default-features does not actually disable the std feature, due to a circular dependency in dev-dependencies that accidentally reenables it.

I’ve fixed this by pruning the internal dependencies and drenching them in default-features = false, but it feels fragile. How can I write a failing test case for the above branch?


Maybe have a compile-fail test that includes use std;? (I’m just guessing here; I’m not sure if that would work)

// in core/

#[cfg(feature = "__test_nostd")]
/// ```compile_fail
/// use std;
/// ```
pub const _UNUSED: () = ();

Unfortunately, this doctest compiles (i.e. fails) even when the crate is correctly built with #[no_std].

// in core/

#[cfg(feature = "__test_nostd")]
/// ```compile_fail
/// use frunk_core::_NO_STD_PROOF;
/// ```
pub const _UNUSED: () = ();

#[cfg(not(feature = "std"))]
pub const _NO_STD_PROOF: () = ();

This is better, but it’d be nice to not have that extraneous feature that gets in the way of using --all-features


Or, you could just check if the crate builds in the absence of libstd.

rustup component remove rust-std-x86_64-pc-windows-gnu
cargo build --target x86_64-pc-windows-gnu --no-default-features


Why not something more like this (Not tested)

#[cfg(not(feature = "std"))]
struct UNUSED;
fn no_std_test(_: UNUSED){}

My rationale is that the compiler will see: UNUSED doesn’t exist because there is no std feature, so no_std_test() will try to take an argument whose type doesn’t exist.


Mind, the crate itself needs to compile when the feature is enabled. (else why would the feature exist?). I just need a way to initiate the the test that doesn’t clutter our list of feature flags with internal garbage.

Hmmm. Maybe this could work on CI…


Since edition2018 you can use std; in #[no_std] the same way that you can extern crate std; in edition2015. (This makes writing #[no_std] simpler in some cases as you can use core everywhere unless they type only comes from std.)

So a doc-test with edition2015,compile-fail and use std; should require building it in #[no_std], but I’m unsure and haven’t tested it. A manual solution with [cfg(feature = "std")] is more self-evident and resilient anyway.


You could add a CI test for a target that doesn’t have std, like thumbv6m-none-eabi. I think if you just cargo check that target, you won’t even need any cross compile linkers or such.


Perfect! Here’s our new .travis.yml.