How do I print all attributes? - rust

I want to print the compile-time attributes to the console, ("conditional predicates").
Is there built-in function or macro that does this?
I'm imagining code like:
#[allow(non_snake_case)]
fn main() {
eprintln!(cfg_as_str!());
}
that might print
allow.non_snake_case=true
allow.dead_code=false
...
cfg.debug_attributes=true
cfg.test=false
cfg.bench=false
...
target_arch="x86_64"
...
I want to better understand the state of the rust compiler at different lines of code. However, it's tedious to do so by trial-and-error.
Of course, I could write this on my own. But I'd guess someone else already has.

I don't think you'll get the lint attributes but for the cfg options you could add a build.rs with:
use std::env;
fn main() {
for (key, value) in env::vars() {
if key.starts_with("CARGO") {
eprintln!("{}: {}", key, value);
}
}
}
Then building with cargo build -vv will output cfgs.
See: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts

Related

Return Result with unquoted string

If I have a file like this:
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
Err("May
June")?
}
I get this result:
Error: "May\nJune"
Is it possible to get the unquoted string on output? Like this:
Error: May
June
I tried this:
Err("May
June").map_err(|e|
format!("{:?}", e)
)?
but it just made it worse:
Error: "\"May\\nJune\""
You have to print the error yourself instead of relying on the default fallback implementation.
main() -> Result<…> prints Debug version of the error (where strings are escaped). It's intended as a quick'n'dirty solution for examples or playpens, and not for real programs where anyone would care about presentation of the output.
Use:
fn main() {
if let Err(e) = run() {
eprintln!("{}", e);
std::process::exit(1);
}
}
fn run() -> Result<(), Box<dyn Error>> {
// etc
}
It will print the error using Display formatting.
There's nothing special about main()'s built-in error handling, so you're not losing anything by printing the error yourself.
There's also an alternative solution of implementing a custom Debug implementation on errors to make the Debug implementation print a non-debug one, but IMHO that's a hack, and needs more code than just doing the straightforward print. If you want that hack, have a look at the anyhow crate.
It might be overkill to pull in an extra dependency for this, but you can use the terminator crate, which offers a new error type intended to be returned from main that delegates to the Display implementation when printed with Debug. Then your example would look like this...
use terminator::Terminator;
fn main() -> Result<(), Terminator> {
Err("May
June")?
}
...and the output would be this:
Error: May
June

Can I create my own conditional compilation attributes?

There are several ways of doing something in my crate, some result in fast execution, some in low binary size, some have other advantages, so I provide the user interfaces to all of them. Unused functions will be optimized away by the compiler. Internal functions in my crate have to use these interfaces as well, and I would like them to respect the user choice at compile time.
There are conditional compilation attributes like target_os, which store a value like linux or windows. How can I create such an attribute, for example prefer_method, so I and the user can use it somewhat like in the following code snippets?
My crate:
#[cfg(not(any(
not(prefer_method),
prefer_method = "fast",
prefer_method = "small"
)))]
compile_error("invalid `prefer_method` value");
pub fn bla() {
#[cfg(prefer_method = "fast")]
foo_fast();
#[cfg(prefer_method = "small")]
foo_small();
#[cfg(not(prefer_method))]
foo_default();
}
pub fn foo_fast() {
// Fast execution.
}
pub fn foo_small() {
// Small binary file.
}
pub fn foo_default() {
// Medium size, medium fast.
}
The user crate:
#[prefer_method = "small"]
extern crate my_crate;
fn f() {
// Uses the `foo_small` function, the other `foo_*` functions will not end up in the binary.
my_crate::bla();
// But the user can also call any function, which of course will also end up in the binary.
my_crate::foo_default();
}
I know there are --cfg attributes, but AFAIK these only represent boolean flags, not enumeration values, which allow setting multiple flags when only one enumeration value is valid.
Firstly, the --cfg flag supports key-value pairs using the syntax --cfg 'prefer_method="fast"'. This will allow you to write code like:
#[cfg(prefer_method = "fast")]
fn foo_fast() { }
You can also set these cfg options from a build script. For example:
// build.rs
fn main() {
println!("cargo:rustc-cfg=prefer_method=\"method_a\"");
}
// src/main.rs
#[cfg(prefer_method = "method_a")]
fn main() {
println!("It's A");
}
#[cfg(prefer_method = "method_b")]
fn main() {
println!("It's B");
}
#[cfg(not(any(prefer_method = "method_a", prefer_method = "method_b")))]
fn main() {
println!("No preferred method");
}
The above code will result in an executable that prints "It's A".
There's no syntax like the one you suggest to specify cfg settings. The best thing to expose these options to your crates' users is through Cargo features.
For example:
# Library Cargo.toml
# ...
[features]
method_a = []
method_b = []
// build.rs
fn main() {
// prefer method A if both method A and B are selected
if cfg!(feature = "method_a") {
println!("cargo:rustc-cfg=prefer_method=\"method_a\"");
} else if cfg!(feature = "method_b") {
println!("cargo:rustc-cfg=prefer_method=\"method_b\"");
}
}
# User Cargo.toml
# ...
[dependencies.my_crate]
version = "..."
features = ["method_a"]
However, in this case, I'd recommend just using the Cargo features directly in your code (i.e. #[cfg(feature = "fast")]) rather than adding the build script since there's a one-to-one correspondence between the cargo feature and the rustc-cfg being added.

Is there a way to make expect() output a more user-friendly message?

I find the message produced by expect() to be very unfriendly for users. Consider the following short example...
use std::env;
fn main() {
let imagefn = env::args().skip(1).next().expect("Filename not provided.");
println!("{}", imagefn);
}
That errors with:
thread 'main' panicked at 'Filename not provided.', libcore/option.rs:960:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.
I find expect() very useful for writing quick code, but wish I could output something more like this:
Filename not provided.
With all the other information hidden unless I actually provide the environment variable, which I, as a developer, should know about. I guess my questions are:
Is there a way I can override expect() to do this?
Why does expect() output its unfriendly message even in release builds?
You can use set_hook to change the panic message. Example:
use std::panic::set_hook;
fn main() {
set_hook(Box::new(|info| {
if let Some(s) = info.payload().downcast_ref::<String>() {
println!("{}", s);
}
}));
// Displays: "My error message":
Option::None::<i32>.expect("My error message");
}
You can also use message() that is simpler, but unstable (for now):
#![feature(panic_info_message)]
use std::panic::set_hook;
fn main() {
set_hook(Box::new(|info| {
println!("{:?}", info.message().unwrap());
}));
Option::None::<i32>.expect("My error message");
}
Note that you can create your own extension method that panics with a custom type. In the panic hook, if you can downcast to your custom type, you are certain of the origin of the panic.
expect() is just a convenient conditional call to panic!():
pub fn expect(self, msg: &str) -> T {
match self {
Some(val) => val,
None => expect_failed(msg) // expect_failed calls panic!()
}
}
Ideally you should probably handle this with the ? operator inside a function returning an Option or Result in order to be able to handle this sort of issue in a more graceful manner.
If you would just like to return a more friendly-looking message and quit, you could implement your own function printing the message and terminating with process::exit.

How do I disable an entire example based on features?

My Rust project has examples that are only relevant to certain features.
I can ignore the main function with:
#[cfg(feature = "foo")]
fn main() {
But other statements that depend on the feature cause errors when I run cargo test. So I have to use a number of cfg attribute statements on functions and use statements to disable code that depends on the feature.
Is there a way to just ignore an entire example file based on the feature configuration?
Also, because main is hidden without the feature, cargo test has this error:
error: main function not found
So this isn't a good solution.
Make more specific use of the #[cfg] directive, providing both a main() when foo is enabled, and a main() when foo is not:
extern crate blah;
// other code which will still compile even without "foo" feature
#[cfg(feature = "foo")]
fn main() {
use blah::OnlyExistsWithFoo;
// code which requires "foo" feature
}
#[cfg(not(feature = "foo"))]
fn main() {
// empty main function for when "foo" is disabled
}

How can I statically register structures at compile time?

I'm looking for the right method to statically register structures at compile time.
The origin of this requirement is to have a bunch of applets with dedicated tasks so that if I run myprog foo, it will call the foo applet.
So I started by defining an Applet structure:
struct Applet {
name: &str,
call: fn(),
}
Then I can define my foo applet this way:
fn foo_call() {
println!("Foo");
}
let foo_applet = Applet { name: "foo", call: foo_call };
Now I would like to register this applet so that my main function can call it if available:
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
match AppletRegistry.get(args[1]) {
Some(x) => x.call(),
_ => (),
}
}
The whole deal is about how AppletRegistry should be implemented so that I can list all the available applets preferably at compile time.
You can't.
One of the conscious design choices with Rust is "no code before main", thus there is no support for this sort of thing. Fundamentally, you need to have code somewhere that you explicitly call that registers the applets.
Rust programs that need to do something like this will just list all the possible implementations explicitly and construct a single, static array of them. Something like this:
pub const APPLETS: &'static [Applet] = [
Applet { name: "foo", call: ::applets::foo::foo_call },
Applet { name: "bar", call: ::applets::bar::bar_call },
];
(Sometimes, the repetitive elements can be simplified with macros, i.e. in this example, you could change it so that the name is only mentioned once.)
Theoretically, you could do it by doing what languages like D do behind the scenes, but would be platform-specific and probably require messing with linker scripts and/or modifying the compiler.
Aside: What about #[test]? #[test] is magic and handled by the compiler. The short version is: it does the job of finding all the tests in a crate and building said giant list, which is then used by the test runner which effectively replaces your main function. No, there's no way you can do anything similar.
You can.
You need to use the inventory crate. This is limited to Linux, macOS, iOS, FreeBSD, Android, and Windows at the moment.
You need to use inventory::submit to add it to a global registry, inventory::collect to build the registry, and inventory::iter to iterate over the registry:
use inventory; // 0.1.9
use std::{collections::BTreeMap, env};
struct Applet {
name: &'static str,
call: fn(),
}
// Add something to the registry
fn foo_call() {
println!("Foo");
}
inventory::submit!(Applet {
name: "foo",
call: foo_call
});
// Build the registry
inventory::collect!(Applet);
fn main() {
let args: Vec<String> = env::args().collect();
let mut registry = BTreeMap::new();
// Access the registry
for applet in inventory::iter::<Applet> {
registry.insert(applet.name, applet);
}
if let Some(applet) = registry.get(&args[1].as_ref()) {
(applet.call)();
}
}
Running it shows how it works:
$ cargo run foo
Foo
$ cargo run bar
This is not a direct answer to your question but just FYI... For ELF binaries, I could achieve something similar to GCC's __attribute__((constructor)) with
fn init() { ... }
#[link_section = ".init_array"]
static INIT: fn() = init;
This is obviously a deviation from Rust's design philosophies. (Portability and what #DK calls "no code before main" principle.)
INIT can also be an Rust array. You might need to pay more attention to alignments.

Resources