#[cfg(none)] for disabling code

I used to be a heavy user of Dlang which has a version(..) construct akin to Rust's #[cfg(..)]. Dlang also has an explicitly specified as never defined version(none) to disable blocks of code. I was surprised to find, with the recent change of warning on unknown cfgs, that Rust did not have a similar cfg specified. I had thought #[cfg(none)] was idiomatic in fact!

I'd like to ask for opinions on adding such a cfg. I realize currently one can #[cfg(any())] but an explicit version has the potential to read more clearly. Perhaps #[cfg(disabled)] would be a better name towards that end?

6 Likes

Any identifier can be specified to be enabled, although it'd be strange to enable the cfg for none or disabled. But cfg(false) is currently an error and I think an obvious choice for a deliberate version of cfg(any()).

11 Likes

Why would you disable an entire code block under any circumstances? Wouldn't be the obvious thing to do be to remove the code block from the codebase?

Probably as an alternative to commenting out the function while fixing something else that is broken (or because that function has a compile error due to other changes you're actively working on).

The biggest benefit to using this over commenting the function out is that (a) the compiler still checks if the function can be parsed as a function, (b) syntax highlighting still works and (c) one line instead of two.

For example: When you are refactoring a larger piece of code but want to keep the old code as a reference without loosing syntax highlighting (to be removed once refactoring is done). (Granted, I've always used a /**/ for that.

17 Likes

I don't know how other people work and I will not state that my way is the "correct" way. But having de-facto dead code (unused, commented-out or otherwise) in my program, is what I consider a code smell that needs to be avoided.

Certainly even the strictest of standards have no problem with disabled code if it’s a temporary un-committed thing in your working tree, or a personal WIP branch, or…

10 Likes

Generally speaking you shouldn't commit that code, but it's still useful to be able to temporarily disable a chunk of code while hacking on something.

10 Likes

I'd love to see false and true officially added as always-available identifiers for cfg.

(true is useful not just for symmetry but for cases where you have cfg(any(long, list, here)) and want to quickly force-enable it locally; you can add true at the start or end without deleting the rest.)

This would be a small and simple RFC, and easy to review. The main issue would be considering compatibility with existing code. I would expect that nobody is using true or false as existing cfg terms with any meaning other than "always true" or "always false", but we'd need to confirm that with a crater run.

17 Likes

I did use #[cfg(skip)] a lot, a lot, and after rust started to warn about it I migrated to #[cfg(any())], but it is ugly. Additionally, in C/C++ #if 0 is quite useful, especially with else branch.

11 Likes

cfg(false) happens to work, so it hasn't even crossed my mind that it was not officially supported. I've never used cfg(true), so I haven't noticed it.

cfg(false) doesn't work today. cfg(FALSE) does (by accident, and now with an "unexpected cfg condition name" warning).

If you use cfg(false) today you get:

error: `cfg` predicate key cannot be a literal
 --> src/main.rs:1:7
  |
1 | #[cfg(false)]
  |       ^^^^^

error[E0565]: unsupported literal
 --> src/main.rs:6:11
  |
6 | #[cfg(not(false))]
  |           ^^^^^

That pretty strongly suggests that it'd be completely backwards-compatible to make that work.

11 Likes

FTR, you can also do #[cfg(any())] as an always-false predicate

1 Like

the same is true of #[cfg(true)], neither of these forms are syntactically valid in current rust, so giving them special meaning would be entirly backwards compatible.

6 Likes

Probably as an alternative to commenting out the function while fixing something else that is broken (or because that function has a compile error due to other changes you're actively working on).

Doesn't that mean that it is possible to tolerate this warning until refactoring is finished?

1 Like

Currently, false could be evaluated by something like #[cfg(all(unix, not(unix)))]

or #[cfg(any())], as already meantioned.

3 Likes

Same. :slight_smile: In fact I've used these (or apparently a variant like FALSE, since false errors) in the past and thought they worked, and was confused until I realized they did not. (check-cfg would prevent that confusion these days, by showing a warning.)

5 Likes

If as of today false errors, then I suppose it's backwards compatible to give it a meaning?

2 Likes

Maybe currently users use cfg(false) to intentionally make the compiler error out for development purposes. Then It'd break current code by making it compile, i.e behave in an unexpected way. scnr

Edit: To clarify, the above text is a joke.

2 Likes

This approach in particular sounds good to me. It's just about maximally descriptive and adheres to the Principle of Least Surprise, something that cannot be said for e.g. none (though it's not hard to get used to that either).