Fix E0535: attribute expression is not valid

Rust intermediate Linux macOS Windows WebAssembly

1. Symptoms

When the Rust compiler encounters error E0535, you will see output similar to the following in your build logs:

error[E0535]: attribute expression is not valid here
 --> src/main.rs:5:12
  |
5 | #[cfg(feature == "advanced")]
  |                    ^^ not a valid attribute expression

The error message indicates that the compiler found an unexpected token within an attribute directive. In this specific case, the comparison operator == inside a cfg attribute is not permitted. The error points directly to the problematic token with the caret ^^ notation beneath it.

Additional manifestations of this error include:

error[E0535]: attribute expression is not valid here
 --> src/lib.rs:10:20
  |
10 | #[cfg(all(unix, != "windows"))]
  |                      ^^ not a valid attribute expression

The compiler consistently rejects expressions involving comparison operators (==, !=, <, >, <=, >=) when they appear inside attribute macros. The error always surfaces at the exact position where the invalid syntax begins.

2. Root Cause

The Rust compiler’s attribute system has a restricted grammar for expressions within cfg and similar conditional compilation attributes. These attributes accept only a limited subset of Rust expressions, specifically designed for basic feature flag checking and platform detection.

The cfg attribute uses a simplified expression language that supports:

  • Identifiers (feature flags, platform names)
  • Logical operators: all(), any(), not()
  • Parenthesized expressions

Critically, the cfg expression grammar does not include comparison operators or string literals. When you write feature == "advanced", the compiler interprets == as a binary operator applied to two operands, but the attribute expression grammar has no provision for such operators.

This design decision stems from how conditional compilation works at the source level. The cfg attribute must be parsed and evaluated before type checking occurs, so it uses a lightweight expression parser that operates on identifiers and logical combinations thereof, not on full Rust expressions with type information.

The error E0535 surfaces because the attribute parser encounters a token that belongs to the main Rust expression grammar but not to the restricted attribute expression grammar. The comparison operator == is valid in regular Rust code but has no meaning within the cfg attribute’s expression language.

3. Step-by-Step Fix

To resolve E0535, you must restructure your conditional compilation logic to use the valid forms that cfg attributes accept.

Before:

#[cfg(feature == "advanced")]
mod advanced_features {}

After:

#[cfg(feature = "advanced")]
mod advanced_features {}

The key change is replacing comparison operators with assignment-style syntax. In cfg attributes, feature flags are expressed as simple identifiers, not as equality comparisons. The attribute system checks whether a feature identifier is defined, not whether it equals a particular value.

Before:

#[cfg(all(unix, != "windows"))]
fn platform_specific() {}

After:

#[cfg(all(unix, not(target_os = "windows")))]
fn platform_specific() {}

When you need to check for negation or complex conditions, wrap the condition in not() and use the proper target_os attribute syntax. String literals in comparisons must be replaced with the appropriate cfg identifiers.

Before:

#[cfg(version < "1.50")]
fn legacy_support() {}

After:

#[cfg(not(feature = "version_1_50_or_higher"))]
fn legacy_support() {}

If you need version-specific behavior, define feature flags in your Cargo.toml and check those instead of attempting string comparisons:

# Cargo.toml
[features]
version_1_50_or_higher = []

Before:

#[cfg(debug_assertions == true)]

After:

#[cfg(debug_assertions)]

Boolean cfg expressions don’t need explicit == true or == false comparisons. Simply writing the identifier enables the block when that cfg is set. Use not() to invert the condition:

#[cfg(not(debug_assertions))]
fn production_mode() {}

4. Verification

After applying the fix, compile your project to confirm the error has been resolved:

cargo build

A successful build produces no E0535 errors:

Compiling myproject v0.1.0
    Finished dev [un优化内容]

To verify that the conditional compilation actually works as intended, test both states of your configuration:

# Build with the feature enabled
cargo build --features advanced

# Build without the feature
cargo build

You can also use cargo rustc -- -Z parse-only to verify the attribute parsing succeeds before full compilation:

cargo check --features advanced

For complex conditional expressions, use cargo expand to inspect what the compiler sees after cfg resolution:

cargo install cargo-expand
cargo expand --features advanced

This tool shows the actual code that reaches compilation after cfg attributes are processed, helping you verify that the correct code paths are included or excluded.

5. Common Pitfalls

A frequent mistake is attempting to use string comparisons in cfg attributes, expecting behavior similar to preprocessor macros in C or C++. Rust’s conditional compilation system operates fundamentally differently—it checks for the presence of identifiers rather than comparing values.

Another pitfall involves forgetting that cfg expressions are purely syntactic. You cannot reference variables, constants, or function return values in a cfg attribute. The expression must be determinable at compile time from the cfg flags defined in your manifest or passed via command line.

Developers sometimes write #[cfg(unix == true)] thinking this explicitly checks for the boolean truth of the cfg value. The == true is simply invalid syntax in this context and triggers E0535. Always use the bare identifier form.

When working with optional dependencies, ensure you use #[cfg(feature = "...")] with the exact feature name defined in your [features] table. Typos in feature names silently cause the cfg check to fail, but they don’t produce E0535—they simply result in the cfg evaluating to false without any warning.

Be cautious with negation: #[cfg(not(unix == true))] is invalid, but #[cfg(not(unix))] correctly checks that unix is not defined. The logical NOT operator requires a simple identifier or a parenthesized expression, not a comparison.

Finally, remember that cfg attributes cannot be combined with logical operators in arbitrary ways across multiple lines. Complex conditions must be nested within all() or any() calls, with each condition being a simple identifier or a not() wrapper around an identifier.

E0536: “attribute expression is not valid here” with different specific tokens. This error covers cases where other invalid tokens appear in attribute expressions, such as method calls or field access.

E0537: “invalid predicate for cfg attribute”. This error occurs when using a predicate that the cfg system doesn’t recognize at all, rather than encountering an unexpected operator mid-expression.

E0554: “attributes behind labels are not allowed”. While not directly about cfg expressions, this related error deals with attribute placement restrictions and helps illustrate the complex rules governing where and how attributes can appear in Rust code.

Understanding E0535 requires recognizing that attribute expressions form a restricted grammar separate from regular Rust expressions. When you see this error, examine your attribute for comparison operators, string literals, or other constructs that exist in the main language but not in the cfg expression language.