Conditionally derive based on feature flag [duplicate] - rust

This question already has an answer here:
Is it possible to conditionally enable an attribute like `derive`?
(1 answer)
Closed 2 years ago.
I want to add a feature to my crate that will optionally make certain structs serializable, and in particular, I want to use Serde's custom derive macros. The Serde dependencies are optional and extern crate declarations are conditionally included behind the feature. Consider the following:
#[derive(Eq, PartialEq, Serialize)]
struct MyStruct {
a: u8,
b: u8
}
With the feature flag enabled, it all works fine. With it disabled, I get this warning:
error: '#[derive]' for custom traits is not stable enough for use. It is deprecated and will be removed in v1.15 (see issue #29644)
Is there a way to conditionally include derived traits? I'm using Rust 1.15 stable.
Should I submit an issue for the error message? It seems misleading.

Like many other pieces of feature-based conditional compilation, use cfg_attr:
#[cfg_attr(feature = "example", derive(Debug))]
struct Foo;
fn main() {
println!("{:?}", Foo);
}
With this, cargo run will fail to compile as Debug is not implemented for Foo, but cargo run --features example will compile and run successfully.

Related

Where are clap macros coming from if they are not being imported?

In the following code snippet, I'm not importing the macros clap and arg but they're still available to the code. Where are they coming from?
use clap::Parser;
#[derive(Parser, Debug)]
struct Arguments {
#[clap(short, long)]
first_part: Option<String>,
#[arg(short, long)]
second_part: Option<String>,
}
fn main() {
let args = Arguments::parse();
println!("{:?}", args);
}
These are not macros, but attributes.
The confusion comes because derive is also an attribute, not a macro. Only that some derive implementations are implemented as macros. Remember that normal macros are recognized by the ! not by #, such as println!.
About where these attributes come from? Well, attributes are never imported, these derive macro helper attributes are parsed either directly by any macro that may be in effect. Yes, this means that these attributes are not scoped and could, in theory, conflict between several crates. It hasn't happened that I know of, yet.

Conditional compilation based on proc_macro

Is it possible to somehow detect in a crate that it's used in proc_macro and include some items based on that?
Let's say I have a token_parser crate with a Token enum:
pub struct Group;
pub struct Ident;
pub struct Punct;
pub struct Literal;
pub enum Token {
Group(Group),
Ident(Ident),
Punct(Punct),
Literal(Literal)
}
It's intended to be a wrapper for a proc_macro::TokenTree, so I want to be able to use it inside a proc_macro crates. I don't want to manualy implement a converting logic in every crate, so I want something like this:
#[cfg(proc_macro)]
impl From<proc_macro::TokenTree> for Token {
fn from(token: proc_macro::TokenTree) -> Self
{
todo!()
}
}
while still having an option to use token_parser crate in normal crates.
P.S I'm aware of proc_macro2 crate. I will probably use it (or even syn) in the end, for now I'm just trying to understand how to do something like that myself properly.
There are primarily two ways I would reach for based on how I want to enable such functionality.
feature for project-based enabling
If you want it only available when opted for, that's a fantastic case to use a cargo feature. Crates that want to use such functionality can then choose to enable this feature while other crates can choose to not enable the feature. Whether this feature should be default typically depends on how the library author believes it should/is used most of the time.
cfg for platform-based enabling
As you may have seen from reading the proc_macro2 source, they conditionally enable a cfg in their build script1. On the wasm32 platform target it is disabled, otherwise it is enabled. This way is very useful in cases where enabling functionality mostly depends on the platform.

Code and feature conditional on Rust version or specific compiler feature

Rust 1.63 stabilized const std::sync::Mutex::new(). Till date to have global flag preventing parallel execution would look like (example):
use once_cell::sync::Lazy;
static GLOBAL_FLAG: Lazy<Mutex<()>> = Lazy::new(Default::default);
while in since 1.63 it can look like this and the dependency isn't required anymore in Cargo.toml:
static GLOBAL_FLAG: Mutex<()> = Mutex::new(());
Now, the question is how to make use of both conditionally, depending on either:
which version of the compiler is used to build the crate
or
whether the feature is stable (I struggle to find it's correct name at the moment)?
I was looking at cfg_rust_features crate, but the part where dependency isn't required by Cargo.toml remains mystery. Any hints welcome.
This is typically accomplished by:
Detecting the Rust version (using rustversion/rustc_version) or the required feature (using autocfg),
Making the fallback dependency (once_cell) optional, and
Emitting an error if the required Rust feature is missing and the fallback dependency is also missing.
For example, using rustversion:
#[rustversion::since(1.63)]
mod private {
use std::sync::Mutex;
pub static GLOBAL_FLAG: Mutex<()> = Mutex::new(());
}
#[rustversion::before(1.63)]
#[cfg(feature = "once_cell")]
mod private {
use once_cell::sync::Lazy;
use std::sync::Mutex;
pub static GLOBAL_FLAG: Lazy<Mutex<()>> = Lazy::new(Default::default);
}
#[rustversion::before(1.63)]
#[cfg(not(feature = "once_cell"))]
compile_error!(
"The \"once_cell\" feature must be enabled for Rust compiler versions prior to 1.63."
);
When compiling on 1.62 without once_cell, this emits
error: The "once_cell" feature must be enabled for Rust compiler versions prior to 1.63.
--> src/main.rs:18:1
|
18 | / compile_error!(
19 | | "The \"once_cell\" feature must be enabled for Rust compiler versions prior to 1.63."
20 | | );
| |_^
Note: the use of mod private seen above is not strictly necessary, but it reduces the number of attributes and ensures that the relevant compile error is the only error emitted. You can pub use private::GLOBAL_FLAG to put it back in scope.

How to link to other fns/structs/enums/traits in rustdoc?

I'm building a Rust library and want to give it some polish. In the rustdoc, I'd sometimes like to link to other parts of the library within the docs, e.g. fns, traits or structs. What is the official syntax for this?
As of Rust 1.48, you can now rely on RFC 1946. This adds the concept of intra-documentation links. This allows using Rust paths as the URL of a link:
[Iterator](std::iter::Iterator)
[Iterator][iter], and somewhere else in the document: [iter]: std::iter::Iterator
[Iterator], and somewhere else in the document: [Iterator]: std::iter::Iterator
The RFC also introduces "Implied Shortcut Reference Links". This allows leaving out the link reference, which is then inferred automatically.
[std::iter::Iterator], without having a link reference definition for Iterator anywhere else in the document
[`std::iter::Iterator`], without having a link reference definition for Iterator anywhere else in the document (same as previous style but with back ticks to format link as inline code)
As a concrete example, this source code:
//! Check out [ExampleStruct], especially [this
//! method](ExampleStruct::foo), but [the trait method][trait] is also
//! cool. There is also [an enum variant you can
//! use](nested::ExampleEnum::Beta).
//!
//! [trait]: ExampleTrait::bar
pub struct ExampleStruct;
impl ExampleStruct {
pub fn foo(&self) {}
}
pub trait ExampleTrait {
fn bar();
}
pub mod nested {
pub enum ExampleEnum {
Alpha,
Beta,
}
}
Produces this documentation:
Specifically, this HTML is generated:
<p>Check out ExampleStruct, especially this method, but the trait method is also cool. There is also an enum variant you can use.</p>
As of Rust 1.48, Rustdoc now supports direct intra-doc links.
Pre Rust 1.48:
Rustdoc seems to generate mostly deterministic filenames for constituent elements of a crate. Therefore if you have an enum named Complex you can generally link to it using:
[Complex](enum.Complex.html)
Similarly a struct called Point would look like:
[Point](struct.Point.html)
This should carry over to most definitions (fn, trait, and so on).
For referencing elements of a crate at different nesting levels, you can use relative paths (where each module is its own folder):
[Point](../model/struct.Point.html)
or use absolute paths:
[Point](/crate_name/model/struct.Point.html)
More of these "conventions", including anchors for specific fields, etc., can be deduced if one builds docs (cargo doc --no-deps --open) and navigates to the field or item they want and takes note of the URL. Remember that only pub items are published to docs.
If one wants to link some specific part of a struct e.g., a method named foo in the same struct (using stable rust, not nightly)
[foo](#method.foo)
or if it is in another struct
[foo](struct.OtherStruct.html#method.foo)
In Rust 1.49 nightly it works (1.48 stable not released yet):
[super::structs::WebApiResponse]
[mycrate::structs::WebApiResponse]
etc.
Read here
Since the documentation is written in Markdown, just use the Markdown syntax for Hyperlinks; i.e.
[anchor text](URL)
Also, take a look at this: https://doc.rust-lang.org/book/documentation.html

How do I compile a multi-file crate in Rust?

I'm trying to figure out how to compile multi-file crates in Rust, but I keep getting a compile error.
I have the file I want to import into the crate thing.rs:
mod asdf {
pub enum stuff {
One,
Two,
Three
}
}
And my crate file test.rc:
mod thing;
use thing::asdf::*;
fn main(){
}
When I run rust build test.rc I get:
test.rc:3:0: 3:19 error: `use` and `extern mod` declarations must precede items
test.rc:3 use thing::asdf::*;
^~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
There's obviously something simple about how modules, crates and use work that I'm just not getting. My understanding was that mod something; for files in the same directory or extern mod something; for libraries on the library path caused the object file to be linked. Then use would allow you to import parts of the module into the current file, function or module. This seems to work for stuff in the core library.
This is with version 0.6 of the rust compiler.
You just need to put the use at the top of the file:
use thing::asdf::*;
mod thing;
fn main() {}
This looks very strange, but
It's what the error message says (anything that you can put at the top level that is not use or extern mod is an "item", including mods), and
It's how Rust name resolution works. use is always relative to the top of the crate, and the whole crate is loaded before name resolution happens, so use thing::asdf::*; makes rustc look for thing as a submodule of the crate (which it finds), and then asdf as a submodule of that, etc.
To illustrate this last point better (and demonstrate the two special names in use, super and self, which import directly from the parent and current module respectively):
// crate.rs
pub mod foo {
// use bar::baz; // (an error, there is no bar at the top level)
use foo::bar::baz; // (fine)
// use self::bar::baz; // (also fine)
pub mod bar {
use super::qux; // equivalent to
// use foo::qux;
pub mod baz {}
}
pub mod qux {}
}
fn main() {}
(Also, tangentially, the .rc file extension no longer has any special meaning to any Rust tools (including in 0.6), and is deprecated, e.g. all the .rc files in the compiler source tree were recently renamed to .rs.)

Resources