What the not operator does
The logical not operator, written !, flips a bool: !true is false and !false is true. It is a prefix operator that goes directly in front of one value, unlike && and ||, which sit between two.
7 <= 10 // true
!(7 <= 10) // false
12 != 12 // false
!(12 != 12) // true
!true // false
When the operand is a comparison or any longer expression, wrap it in parentheses. The ! binds tighter than the comparison operators, so !7 <= 10 applies the not to 7 alone and then compares the result with 10. For an integer, ! is a bitwise not (more on that below), so this prints true because -8 <= 10, while !(7 <= 10) prints false. The first form compiles and silently means something else.
The result of a comparison can also be stored first. After let ok = 7 <= 10;, the expression !ok is false, and no parentheses are needed because the operand is a single variable. Naming the condition this way also makes long checks easier to read.
Using it with boolean variables
The operator is most useful with variables that already hold a bool:
let hungry = true;
if !hungry {
println!("I can wait a little longer");
} else {
println!("I need food!");
}
Here hungry is true, so !hungry is false. The if block is skipped, the else runs and the program prints I need food!. Reading !hungry aloud as "not hungry" makes the condition easy to follow.
Not is not only for booleans in Rust
In many languages ! works on any value. In Rust it has a second meaning. On an integer, ! flips every bit, which is a bitwise not. With let n: i32 = 5;, the expression !n is -6, not false. Rust never treats a number as true or false, so you cannot write if !n { ... } to test for zero. Write n == 0 instead. Keep ! for real bool values and comparisons.
Not with and, or
The operator combines with the other two. !(a && b) is the same as !a || !b, and !(a || b) is the same as !a && !b. Often a negated comparison is clearer written directly: !(x == 3) and x != 3 mean the same, and so do !(x > 3) and x <= 3. Applying ! twice cancels out: !!flag equals flag.
Common mistakes
- Writing
not. Rust has nonotkeyword. - Forgetting parentheses around a comparison, as described above.
- Using
!on a number expecting a truth value. It flips bits instead. - Double negatives in names. A variable called
not_readytested with!not_readyis hard to read. Prefer a positive name, such asready.
The lesson's exercise
The starter code declares isSunny as a bool set to true. The task is to print a different message depending on the weather, using !.
- Write an
ifwith the condition!isSunny. It is true only when it is not sunny. - Inside it, print
"Learning to code"withprintln!. - Add an
elseon the same line as the closing brace, and print"Going to the beach". It runs when theifcondition is false, which here means it is sunny.
let isSunny: bool = true;
if !isSunny {
println!("Learning to code");
} else {
println!("Going to the beach");
}
Because isSunny is true, !isSunny is false and the program prints Going to the beach. Setting isSunny to false makes it print Learning to code. The compiler also warns that isSunny should have a snake case name, because Rust style is is_sunny. It is only a warning, and the program still builds and runs.
Key takeaways
!xturnstrueintofalseandfalseintotrue.- Put parentheses around a comparison before negating it.
- On integers,
!is a bitwise not, not a truth test. - Rust style names variables in snake_case, such as
is_sunny.
