Complete Guide to Rust If-Else Conditions

Introduction to Conditional Statements in Rust

Conditional statements are fundamental control flow structures that allow programs to make decisions and execute different code paths based on certain conditions. Rust's approach to conditionals emphasizes safety, expressiveness, and the ability to use conditions as expressions. Understanding conditional statements is crucial for writing flexible and robust Rust programs.

Key Concepts

  • Conditions must be boolean: No automatic conversion from other types
  • Expressions, not statements: if constructs can return values
  • Exhaustive checking: if let and match ensure all cases handled
  • Pattern matching integration: Seamless work with enums and destructuring
  • Zero-cost abstractions: Conditionals compile to efficient branch instructions

1. Basic If-Else Syntax

Simple If Statement

fn main() {
let number = 7;
// Basic if statement
if number < 5 {
println!("Number is less than 5");
}
// Note: Condition must be boolean
// if number { // Error: expected bool, found integer
//     println!("This won't compile");
// }
// Works with boolean expressions
let condition = number > 10;
if condition {
println!("Number is greater than 10");
}
}

If-Else Statement

fn main() {
let number = 7;
if number < 5 {
println!("Number is less than 5");
} else {
println!("Number is greater than or equal to 5");
}
// Real-world example
let temperature = 22;
if temperature > 30 {
println!("It's hot outside! 🌞");
} else {
println!("It's pleasant weather! 😊");
}
}

Else If Ladder

fn main() {
let number = 7;
if number % 4 == 0 {
println!("Number is divisible by 4");
} else if number % 3 == 0 {
println!("Number is divisible by 3");
} else if number % 2 == 0 {
println!("Number is divisible by 2");
} else {
println!("Number is not divisible by 4, 3, or 2");
}
// Grade classification example
let score = 85;
if score >= 90 {
println!("Grade: A");
} else if score >= 80 {
println!("Grade: B");
} else if score >= 70 {
println!("Grade: C");
} else if score >= 60 {
println!("Grade: D");
} else {
println!("Grade: F");
}
// Note: Conditions are evaluated in order
// First matching block executes, rest are skipped
}

2. If as an Expression

Returning Values from If

fn main() {
// If expressions can return values
let condition = true;
let number = if condition { 5 } else { 6 };
println!("Number is: {}", number);
// All arms must return the same type
let x = if condition {
5
} else {
6  // Both arms return i32
};
// This won't compile:
// let y = if condition {
//     5
// } else {
//     "six"  // Error: incompatible types
// };
// Practical example: conditional assignment
let is_even = if number % 2 == 0 { true } else { false };
println!("Is even: {}", is_even);
// Or more concisely:
let is_even = number % 2 == 0;
println!("Is even: {}", is_even);
}

Complex Expressions in If

fn main() {
let x = 10;
let y = 20;
// Complex condition with logical operators
let result = if x > 5 && y < 30 || x == y {
"Complex condition true"
} else {
"Complex condition false"
};
println!("{}", result);
// Nested if expressions
let value = if x > 0 {
if x < 10 {
"x is between 0 and 10"
} else {
"x is greater than or equal to 10"
}
} else {
"x is less than or equal to 0"
};
println!("{}", value);
}

Using If in Function Returns

fn absolute_value(x: i32) -> i32 {
if x >= 0 {
x  // Return x directly
} else {
-x  // Return negated x
}
}
fn describe_number(x: i32) -> &'static str {
if x == 0 {
"zero"
} else if x > 0 {
"positive"
} else {
"negative"
}
}
fn main() {
println!("Absolute of -5: {}", absolute_value(-5));
println!("Absolute of 5: {}", absolute_value(5));
println!("-3 is {}", describe_number(-3));
println!("0 is {}", describe_number(0));
println!("7 is {}", describe_number(7));
}

3. If in Variable Declarations

Conditional Initialization

fn main() {
// Initialize based on condition
let x = if some_condition() { 10 } else { 20 };
// More complex initialization
let data = if has_permission() {
load_secure_data()
} else {
load_public_data()
};
// Conditional struct initialization
struct Config {
timeout: u32,
retries: u32,
}
let is_production = true;
let config = if is_production {
Config {
timeout: 30,
retries: 5,
}
} else {
Config {
timeout: 5,
retries: 1,
}
};
println!("Config: timeout={}, retries={}", config.timeout, config.retries);
}
fn some_condition() -> bool { true }
fn has_permission() -> bool { false }
fn load_secure_data() -> &'static str { "secure data" }
fn load_public_data() -> &'static str { "public data" }

Conditional Vector/Array Creation

fn main() {
let use_large_buffer = true;
// Conditional array size (must be constant)
// This won't work: let arr = if use_large_buffer { [0; 1000] } else { [0; 100] };
// But for Vec it works fine
let buffer: Vec<u8> = if use_large_buffer {
vec![0; 1000]
} else {
vec![0; 100]
};
println!("Buffer size: {}", buffer.len());
// Conditional collection initialization
let items = if has_items() {
vec!["apple", "banana", "orange"]
} else {
Vec::new()
};
println!("Items: {:?}", items);
}
fn has_items() -> bool { false }

4. Nested If Statements

Basic Nesting

fn main() {
let x = 10;
let y = 5;
if x > 0 {
println!("x is positive");
if y > 0 {
println!("y is also positive");
if x > y {
println!("x is greater than y");
}
}
}
// Nested if-else
if x > 0 {
if y > 0 {
println!("Both positive");
} else {
println!("x positive, y non-positive");
}
} else {
if y > 0 {
println!("x non-positive, y positive");
} else {
println!("Both non-positive");
}
}
}

Avoiding Deep Nesting

fn main() {
// Deep nesting (hard to read)
let user = Some("Alice");
let age = Some(30);
let verified = true;
// Bad: Deep nesting
if let Some(name) = user {
if let Some(age_val) = age {
if verified {
if age_val >= 18 {
println!("{} is an adult", name);
}
}
}
}
// Good: Early returns with ?
fn process_user() -> Option<()> {
let name = user?;
let age_val = age?;
if !verified {
return None;
}
if age_val >= 18 {
println!("{} is an adult", name);
}
Some(())
}
process_user();
// Good: Combine conditions with &&
if let (Some(name), Some(age_val)) = (user, age) {
if verified && age_val >= 18 {
println!("{} is an adult", name);
}
}
}

5. If Let Syntax

Basic If Let

fn main() {
// Instead of match for single pattern
let optional = Some(5);
// Match version
match optional {
Some(x) => println!("Value: {}", x),
None => (),
}
// If let version (more concise)
if let Some(x) = optional {
println!("Value: {}", x);
}
// With else
if let Some(x) = optional {
println!("Value: {}", x);
} else {
println!("No value");
}
// With Option::None
let none: Option<i32> = None;
if let Some(x) = none {
println!("Value: {}", x);
} else {
println!("No value");
}
}

If Let with Enums

enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
fn main() {
let msg = Message::Move { x: 10, y: 20 };
// Match on specific variant
if let Message::Move { x, y } = msg {
println!("Moving to ({}, {})", x, y);
}
let msg2 = Message::Write(String::from("hello"));
if let Message::Write(text) = msg2 {
println!("Writing: {}", text);
} else {
println!("Not a Write message");
}
// Multiple patterns (with else if)
let msg3 = Message::ChangeColor(255, 0, 0);
if let Message::Quit = msg3 {
println!("Quitting");
} else if let Message::Move { x, y } = msg3 {
println!("Moving to ({}, {})", x, y);
} else if let Message::Write(text) = msg3 {
println!("Writing: {}", text);
} else if let Message::ChangeColor(r, g, b) = msg3 {
println!("Changing color to RGB({}, {}, {})", r, g, b);
}
}

If Let with Destructuring

fn main() {
// Destructuring tuples
let pair = (10, 20);
if let (10, y) = pair {
println!("First is 10, second is {}", y);
}
// Destructuring structs
struct Point {
x: i32,
y: i32,
}
let point = Point { x: 5, y: 10 };
if let Point { x: 5, y } = point {
println!("x is 5, y is {}", y);
}
// Destructuring with ignored fields
if let Point { y, .. } = point {
println!("y coordinate is {}", y);
}
// Destructuring enums with data
enum Result {
Success(i32),
Failure { code: u32, message: String },
}
let result = Result::Failure {
code: 404,
message: String::from("Not Found"),
};
if let Result::Failure { code, message } = result {
println!("Error {}: {}", code, message);
}
}

6. While Let

Basic While Let

fn main() {
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
stack.push(4);
// While let with Option
while let Some(top) = stack.pop() {
println!("Popped: {}", top);
}
println!("Stack is now empty: {:?}", stack);
// While let with Iterator
let mut iter = (0..5).into_iter();
while let Some(value) = iter.next() {
println!("Iterator value: {}", value);
}
// While let with custom condition
let mut x = Some(0);
while let Some(i) = x {
if i > 5 {
println!("Breaking at {}", i);
x = None;
} else {
println!("i = {}", i);
x = Some(i + 1);
}
}
}

While Let with Mutable References

fn main() {
let mut data = Some(vec![1, 2, 3, 4, 5]);
while let Some(vec) = data.as_mut() {
if let Some(value) = vec.pop() {
println!("Popped: {}", value);
} else {
data = None;
}
}
// More idiomatic way
let mut data2 = vec![1, 2, 3, 4, 5];
while let Some(value) = data2.pop() {
println!("Value: {}", value);
}
// Processing a queue
use std::collections::VecDeque;
let mut queue = VecDeque::new();
queue.push_back(1);
queue.push_back(2);
queue.push_back(3);
while let Some(front) = queue.pop_front() {
println!("Processing: {}", front);
}
}

7. Match vs If Let

When to Use Each

fn main() {
let optional = Some(5);
let number = 42;
// If let - when interested in one pattern
if let Some(x) = optional {
println!("Got a value: {}", x);
}
// Match - when need to handle multiple patterns
match optional {
Some(x) if x > 10 => println!("Large value: {}", x),
Some(x) => println!("Value: {}", x),
None => println!("No value"),
}
// If let with complex patterns
#[derive(Debug)]
enum ComplexEnum {
A(i32),
B { x: i32, y: i32 },
C(String),
}
let value = ComplexEnum::B { x: 10, y: 20 };
// If let is perfect for single variant check
if let ComplexEnum::B { x, y } = value {
println!("Got B with x={}, y={}", x, y);
}
// Match for comprehensive handling
match value {
ComplexEnum::A(x) => println!("A: {}", x),
ComplexEnum::B { x, y } => println!("B: ({}, {})", x, y),
ComplexEnum::C(s) => println!("C: {}", s),
}
}

If Let with Guards

fn main() {
let optional = Some(15);
// If let with guard
if let Some(x) = optional {
if x > 10 {
println!("Value {} is greater than 10", x);
}
}
// More elegantly with match
match optional {
Some(x) if x > 10 => println!("Value {} is greater than 10", x),
Some(x) => println!("Value {} is not greater than 10", x),
None => println!("No value"),
}
// But if let can't have guards directly
// This doesn't work:
// if let Some(x) = optional && x > 10 { // Not valid
//     println!("Value is >10");
// }
// Workaround with nested if
if let Some(x) = optional {
if x > 10 {
println!("Value is >10");
}
}
}

8. Conditional Compilation

Cf

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper