Is it possible to conditionally compile a code block inside a function? - rust

I'm wondering if something like this is possible
fn main() {
#[cfg(foo)] {
println!("using foo config");
}
}
The context is some code that cannot adequately be tested with just unit tests. I'll often have to run a "demo" cfg which displays information. I'm looking for alternatives to manually commenting in/out some portions of code.

As of at least Rust 1.21.1, it's possible to do this as exactly as you said:
fn main() {
#[cfg(foo)]
{
println!("using foo config");
}
}
Before this, it isn't possible to do this completely conditionally (i.e. avoiding the block being type checked entirely), doing that is covered by RFC #16. However, you can use the cfg macro which evaluates to either true or false based on the --cfg flags:
fn main() {
if cfg!(foo) { // either `if true { ... }` or `if false { ... }`
println!("using foo config");
}
}
The body of the if always has name-resolution and type checking run, so may not always work.

You may interested in the crate cfg-if, or a simple macro will do:
macro_rules! conditional_compile {
($(#[$ATTR:meta])* { $CODE: tt }) => {
{
match 0 {
$(#[$ATTR])*
0 => $CODE,
// suppress clippy warnning `single_match`.
1 => (),
_ => (),
}
}
}
}
fn main() {
conditional_compile{
#[cfg(foo)]
{{
println!("using foo config");
// Or something only exists in cfg foo, like a featured mod.
}}
}
}

Related

Construct ident from string/int in declarative macro

I have a dozen crates all with two functions part1() and part2(). For each of those, I want to print the return value of both functions. To not repeat myself, I wrote a little macro, so my main crate's main() looks like this:
fn main() {
macro_rules! print_day {
($day:ident) => {
let day = stringify!($day).replace('d', "");
println!("Day {day} Part 1: {}", $day::part1());
println!("Day {day} Part 2: {}", $day::part2());
};
}
print_day!(d01);
print_day!(d02);
print_day!(d03);
// list goes on..
}
Is it possible to construct an ident from a string or int in a declarative macro, so I could instead do like below? All I've found is either for proc macros, using additional packages or the other way round.
fn main() {
macro_rules! print_day {
($day:expr) => {
let daystr = format!("{:02}", $day);
let dayident = ???;
println!("Day {daystr} Part 1: {}", $dayident::part1());
println!("Day {daystr} Part 2: {}", $dayident::part2());
};
}
for day in 0..=num_days {
print_day!(day);
}
}
Using concat_idents you can glue identifiers together, but unfortunately it does not (yet?) allow non-identifiers such as numbers.
#![feature(concat_idents)]
fn m_1() {
println!("Hi");
}
macro_rules! m {
($n:ident) => {
concat_idents!(m, $n)();
};
}
fn main() {
// underscore is needed to make it into an identifier
m!(_1);
}
So we need to list the crates manually, just like we list the functions manually.
I think something like below should work, but it does not.
macro_rules! call_all {
(#funs $crat:ident $($fun:ident)+) => {{
$($crat::$fun();)*
}};
($($crat:ident)+ # $($fun:ident)+) => {{
// error: attempted to repeat an expression containing no syntax variables matched as repeating at this depth
$(call_all!(#funs $crat $($fun)*);)*
// ^^^^^^
}};
}
fn main() {
call_all!(tokio rand # m_1 m_2)
}
You can use the seq-macro crate to simplify that:
fn main() {
seq_macro::seq!(day in 1..=25 {
println!("Day {} Part 1: {}", day, d~day::part1());
println!("Day {} Part 2: {}", day, d~day::part2());
});
}
Sadly, it does not support formatting with leading zeroes (01, 02, ...) or using a variable for the number of days. The first could be fixed with a custom proc macro, the second could not.

How do I exclude a code block depending on the target arch? [duplicate]

I'm wondering if something like this is possible
fn main() {
#[cfg(foo)] {
println!("using foo config");
}
}
The context is some code that cannot adequately be tested with just unit tests. I'll often have to run a "demo" cfg which displays information. I'm looking for alternatives to manually commenting in/out some portions of code.
As of at least Rust 1.21.1, it's possible to do this as exactly as you said:
fn main() {
#[cfg(foo)]
{
println!("using foo config");
}
}
Before this, it isn't possible to do this completely conditionally (i.e. avoiding the block being type checked entirely), doing that is covered by RFC #16. However, you can use the cfg macro which evaluates to either true or false based on the --cfg flags:
fn main() {
if cfg!(foo) { // either `if true { ... }` or `if false { ... }`
println!("using foo config");
}
}
The body of the if always has name-resolution and type checking run, so may not always work.
You may interested in the crate cfg-if, or a simple macro will do:
macro_rules! conditional_compile {
($(#[$ATTR:meta])* { $CODE: tt }) => {
{
match 0 {
$(#[$ATTR])*
0 => $CODE,
// suppress clippy warnning `single_match`.
1 => (),
_ => (),
}
}
}
}
fn main() {
conditional_compile{
#[cfg(foo)]
{{
println!("using foo config");
// Or something only exists in cfg foo, like a featured mod.
}}
}
}

Rust macro accept call to member function

I'm trying to replace a bunch of snippets like
if let Some(thing) = some_t {
thing.doSomething();
}
with a macro, resulting in something like
if_some(some_t, doSomething());
so basically replacing
if_some(/* option */, /* member function call(s) */);
with
if let Some(thing) = /* option */ {
thing./* member function call(s) */
}
The attempt below does however not seem to work (the compiler reports "error: unexpected token: doSomething()", which does not help me in understanding what is wrong here. Anyone has an idea on how to handle this?
#[macro_export]
macro_rules! if_some {
( $option:ident, $function:expr ) => {{
if let Some(thing) = $option {
thing.$function
}
}};
}
struct Thing {}
impl Thing {
fn doSomething(&self) {}
}
fn main() {
let some_t = Some(Thing {});
if let Some(thing) = some_t {
thing.doSomething();
}
if_some!(some_t, doSomething());
}
Once doSomething() is captured as expr, it will always stay so. The compiler does not see thing.doSomething(), it sees thing.<expr> and this is invalid Rust. You cannot write an expression after the dot, it needs to be an identifier.
The easiest way to fix that is to instead capture it as list of tts. tts can be inspected after they are captured:
#[macro_export]
macro_rules! if_some {
( $option:ident, $($function:tt)* ) => {{
if let Some(thing) = $option {
thing.$($function)*
}
}};
}

rust extend built-in enum Result?

tl;dr Is it possible to extend std::result::Result to add my own variant that signals "things are Okay but also..." and keep impl Result methods like is_ok()?
I want to extend Result to signal additional states that a function caller can use for special cases.
use std::result::Result
use std::io::Error;
/// Extend Result to also signal "things are okay but check on things"
enum ResultExt<T, E> {
Result<T, E>,
OkButCheckThings(T),
}
pub fn do_stuff() -> ResultExt<u64, Error> {
// ...
}
pub fn main() -> {
let var = match do_stuff() {
Ok(val) => { val },
Err(err) => { 0 },
OkButCheckThings(val) => { check_things(); val },
}
dbg!(var);
}
It's possible to plainly extend an Enum. But I would also like to use the underlying Result<T, E> functions like is_ok.
let var2 = do_stuff();
if var2.is_ok() {
println!("It is totally Ok, nothing to check!");
}
I created a rust playground example that successfully extends Result<T, E> but the extended enum cannot use functions like is_ok().
The real-world use-case is a function that calls std::io::Read may need to "modify" the returned Result to signal additional states beyond Ok and Err. But I want these various "meta states" to be captured by one enum, as opposed to returning various other bool flags (I want to avoid return signature with (Result<T>, bool, bool). This would allow one clean match statement of all possible states; Ok, Err, "Okay but...", "Err but ...", etc..
There is no current way of "extending" and enum perse.
But it could be simply solved by embedding your own enum type into the result itself.
Simple example, similar to yours:
use std::fmt::Display;
enum StuffToCheck<T> {
Ok(T),
CheckThis(T),
}
impl<T> StuffToCheck<T>
where
T: Display + Copy,
{
pub fn check_things(&self) -> T {
match self {
Self::Ok(val) => {
*val
}
Self::CheckThis(val) => {
println!("Checking stuff for {}", val);
*val
}
}
}
}
fn do_stuff() -> ResultExt<u64> {
Ok(StuffToCheck::CheckThis(10))
}
type ResultExt<T> = Result<StuffToCheck<T>, std::io::Error>;
fn main() {
let var = match do_stuff() {
Ok(result) => result.check_things(),
Err(_err) => 0,
};
dbg!(var);
}
Playground
You could even use nested pattern matching:
...
match do_stuff() {
Err(e) => {//handle error}
Ok(StuffToCheck::Ok(value)) => { value },
Ok(StuffToCheck::CheckThis(value)) => {
check_things(value);
value
}
}
...
I think this is an instance of the X-Y problem. You can use the built-in result, you just need a different error type, that returns an option: Some(partial_result) or None.
For example you have function parse, that can attempt to adjust for a malformed input, but report the error.
pub fn parse(b: &str) -> Result<&str, CustomParseError> {
// Do something that might fail,
if failed(){
return CustomParseError::new(None)
} else if partially_failed() {
return CustomParseError::new(Some(partial_result))
} else {
return completeResult
}
}
This way you have a clean code path where nothing failed, and all of your assumptions are correct, and if it's not => instead of unwrapping, you match and check which case you have. This is vastly superior, because the error often contains enough information for you to reconstruct both what went wrong, and what could be done to fix it.

How do I assert an enum is a specific variant if I don't care about its fields?

I'd like to check enums with fields in tests while ignoring the actual value of the fields for now.
Consider the following example:
enum MyEnum {
WithoutFields,
WithFields { field: String },
}
fn return_with_fields() -> MyEnum {
MyEnum::WithFields {
field: "some string".into(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn example() {
assert_eq!(return_with_fields(), MyEnum::WithFields {..});
}
}
playground
I'd like to use assert_eq! here, but the compiler tells me:
error: expected expression, found `}`
--> src/lib.rs:18:64
|
18 | assert_eq!(return_with_fields(), MyEnum::WithFields {..});
| ^ expected expression
This is similar to Why do I get an error when pattern matching a struct-like enum variant with fields?, but the solution does not apply in my case.
Of course, I can use match and do it myself, but being able to use assert_eq! would be less work.
Rust 1.42
You can use std::matches:
assert!(matches!(return_with_fields(), MyEnum::WithFields { .. }));
Previous versions
Your original code can be made to work with a new macro:
macro_rules! is_enum_variant {
($v:expr, $p:pat) => (
if let $p = $v { true } else { false }
);
}
#[test]
fn example() {
assert!(is_enum_variant!(return_with_fields(), MyEnum::WithoutFields {..}));
}
Personally, I tend to add methods to my enums:
fn is_with_fields(&self) -> bool {
match self {
MyEnum::WithFields { .. } => true,
_ => false,
}
}
I also tend to avoid struct-like enums and instead put in extra work:
enum MyEnum {
WithoutFields,
WithFields(WithFields),
}
struct WithFields { field: String }
impl MyEnum {
fn is_with_fields(&self) -> bool {
match self {
MyEnum::WithFields(_) => true,
_ => false,
}
}
fn as_with_fields(&self) -> Option<&WithFields> {
match self {
MyEnum::WithFields(x) => Some(x),
_ => None,
}
}
fn into_with_fields(self) -> Option<WithFields> {
match self {
MyEnum::WithFields(x) => Some(x),
_ => None,
}
}
}
I hope that some day, enum variants can be made into their own type to avoid this extra struct.
If you are using Rust 1.42 and later, see Shepmaster's answer below.
A simple solution here would be to do the opposite assertion:
assert!(return_with_fields() != MyEnum::WithoutFields);
or even more simply:
assert_ne!(return_with_fields(), MyEnum::WithoutFields);
Of course if you have more members in your enum, you'll have to add more asserts to cover all possible cases.
Alternatively, and this what OP probably had in mind, since assert! just panics in case of failure, the test can use pattern matching and call panic! directly in case something is wrong:
match return_with_fields() {
MyEnum::WithFields {..} => {},
MyEnum::WithoutFields => panic!("expected WithFields, got WithoutFields"),
}
I'd use a macro like #Shepmaster proposed, but with more error reporting (like the existing assert! and assert_eq! macros:
macro_rules! assert_variant {
($value:expr, $pattern:pat) => ({
let value = &$value;
if let $pattern = value {} else {
panic!(r#"assertion failed (value doesn't match pattern):
value: `{:?}`,
pattern: `{}`"#, value, stringify!($pattern))
}
})
// TODO: Additional patterns for trailing args, like assert and assert_eq
}
Rust playground demonstrating this example

Resources