18

I'm writing a Rust library (generated from cargo) with unit tests.

I'd like to use the extern crate maplit in my unit tests to be able to use JavaScript-like hashmap literals. I don't want to use maplit in my library code.

maplit provides a macro that apparently must be activated using #[macro_use]. The only way I've been able to get this all working is to place this at the top of lib.rs:

#[cfg(test)] #[macro_use] extern crate maplit;

// my crate-specific stuff

At this point I realized I don't know what exactly #[cfg(test)] does. I'm using it in my tests. These are included with library code, as per the convention, like so:

// some library code

#[cfg(test)]
mod test {
  use super::*;
  // tests here
}

I had thought that the line #[cfg(test)] was marking what follows until the end of the file (or block?) as only applicable to the test configuration.

If so, then putting this directive at the top of lib.rs seems like a problem. Won't my entire library be dropped when I compile a distribution?

I've tried to find documentation on what exactly #[cfg(test)] does, but to no avail.

Rich Apodaca
  • 28,316
  • 16
  • 103
  • 129
  • I'm sure sure but I think that mean this piece of code is only included for test. But I think there is maybe more, this kind of attribute are kind of magic feature from compiler – Stargateur Oct 31 '19 at 23:22

1 Answers1

30
#[....]

The above is a Rust Attribute which is like an annotation in other languages. For example; in Java we have @Annotation(....) for methods and classes. Unlike annotation the rust attribute can be an expression that follows the attribute syntax.

#[cfg(....)]

The above is a compiler configuration attribute. The cfg() is one of many built-in attributes.

#[cfg(test)]

The above tells the Rust compiler that the following code should only be compiled when the test configuration is active. You can have other configuration attributes like debug, windows or features.

#[cfg(test)] #[macro_use] extern crate maplit;

Is the same as

#[cfg(test)]
#[macro_use]
extern crate maplit;

Which tells the Rust compiler to only compile the next line if the test configuration is active, and the next line tells Rust to only use macros from the following crate.

If so, then putting this directive at the top of lib.rs seems like a problem. Won't my entire library be dropped when I compile a distribution?

The #[cfg(...)] attribute only applies the compiler condition upon the thing it is attached to.

When you place the attribute at the top of the file followed by a space. The attribute is attached to the current module or crate.

As shown here from the documentation example, the crate_type is applied to the entire file:

// General metadata applied to the enclosing module or crate.
#![crate_type = "lib"]

// A function marked as a unit test
#[test]
fn test_foo() {
    /* ... */
}

// A conditionally-compiled module
#[cfg(target_os = "linux")]
mod bar {
    /* ... */
}

// A lint attribute used to suppress a warning/error
#[allow(non_camel_case_types)]
type int8_t = i8;

// Inner attribute applies to the entire function.
fn some_unused_variables() {
  #![allow(unused_variables)]

  let x = ();
  let y = ();
  let z = ();
}
Reactgular
  • 52,335
  • 19
  • 158
  • 208
  • Surely you can: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=97a330ee55ece1d1ec7910b199a72a66 In this playground, the whole file including the `main` function is compiled only for tests, so you can test this code, but can't run it, since in non-test mode the file is treated as empty. – Cerberus Nov 01 '19 at 03:24
  • @Cerberus you've removed the main function. That's not an example of the whole file. If you placed something above main, then main would start working. Right? – Reactgular Nov 01 '19 at 13:29
  • It will stop to compile - https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e8c279f8dd8d626c2b2f6e2564aa08f8 - since the inner attributes (like the ones applied to the whole file) can't appear after other items. – Cerberus Nov 01 '19 at 13:44
  • 1
    ...sorry, I misunderstood your comment - here it is: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=d3bbbd06d5eba679b4f88f7d415569db It is still erased. – Cerberus Nov 01 '19 at 13:44
  • 1
    This answer conflicts with the documentation it links to, there is no mention of spaces being important in said documentation, instead according to the documentation whether an attribute is "inner" (applied to the thing it is inside) or "outer" (applied to the thing that comes next) depends on whether there is an exclamation mark after the hash. – plugwash Jan 23 '22 at 17:31