Rust - Booleans & Characters

Overview

Estimated time: 30–40 minutes

Master Rust's boolean type for logical operations and the char type for Unicode character handling, including character manipulation and validation methods.

Learning Objectives

Prerequisites

Boolean Type

Basic Boolean Operations


fn main() {
    let is_true = true;
    let is_false = false;
    
    // Logical AND
    println!("true && false = {}", is_true && is_false);
    
    // Logical OR
    println!("true || false = {}", is_true || is_false);
    
    // Logical NOT
    println!("!true = {}", !is_true);
    println!("!false = {}", !is_false);
    
    // Comparison results are booleans
    let x = 5;
    let y = 10;
    println!("{} == {} is {}", x, y, x == y);
    println!("{} < {} is {}", x, y, x < y);
    println!("{} >= {} is {}", x, y, x >= y);
}

Output:

true && false = false
true || false = true
!true = false
!false = true
5 == 10 is false
5 < 10 is true
5 >= 10 is false

Short-Circuit Evaluation


fn expensive_operation() -> bool {
    println!("Expensive operation called!");
    true
}

fn main() {
    // Short-circuit AND: second expression not evaluated if first is false
    let result1 = false && expensive_operation();
    println!("Result 1: {}", result1);
    
    // Short-circuit OR: second expression not evaluated if first is true
    let result2 = true || expensive_operation();
    println!("Result 2: {}", result2);
    
    // Both expressions evaluated when needed
    let result3 = true && expensive_operation();
    println!("Result 3: {}", result3);
}

Output:

Result 1: false
Result 2: true
Expensive operation called!
Result 3: true

Boolean in Control Flow


fn main() {
    let age = 18;
    let has_license = true;
    let has_car = false;
    
    let can_drive = age >= 16 && has_license;
    let can_drive_alone = can_drive && (age >= 18 || has_car);
    
    if can_drive_alone {
        println!("You can drive alone!");
    } else if can_drive {
        println!("You can drive with supervision.");
    } else {
        println!("You cannot drive yet.");
    }
    
    // Boolean to string
    println!("Can drive: {}", can_drive);
    println!("Can drive alone: {}", can_drive_alone);
}

Output:

You can drive alone!
Can drive: true
Can drive alone: true

Character Type

Character Literals


fn main() {
    // Basic ASCII characters
    let letter = 'A';
    let digit = '5';
    let symbol = '$';
    
    // Unicode characters
    let emoji = 'πŸ˜€';
    let chinese = 'δΈ­';
    let math = 'Ο€';
    
    println!("ASCII: {}, {}, {}", letter, digit, symbol);
    println!("Unicode: {}, {}, {}", emoji, chinese, math);
    
    // Escape sequences
    let newline = '\n';
    let tab = '\t';
    let quote = '\'';
    let backslash = '\\';
    
    println!("Escaped characters work in strings too:");
    println!("Quote: {}, Tab:{} Backslash: {}", quote, tab, backslash);
}

Output:

ASCII: A, 5, $
Unicode: πŸ˜€, δΈ­, Ο€
Escaped characters work in strings too:
Quote: ', Tab:	 Backslash: \

Unicode Code Points


fn main() {
    let char_a = 'A';
    let char_unicode = '\u{1F600}';  // πŸ˜€ emoji
    let char_hex = '\x41';           // 'A' in hex
    
    println!("Character: {}", char_a);
    println!("Unicode emoji: {}", char_unicode);
    println!("Hex character: {}", char_hex);
    
    // Get numeric value of character
    let code_point = char_a as u32;
    println!("'A' as u32: {}", code_point);
    
    // Create character from code point
    if let Some(ch) = char::from_u32(65) {
        println!("char from 65: {}", ch);
    }
    
    // Invalid code point
    if let Some(ch) = char::from_u32(0x1FFFFF) {
        println!("Valid: {}", ch);
    } else {
        println!("Invalid Unicode code point");
    }
}

Output:

Character: A
Unicode emoji: πŸ˜€
Hex character: A
'A' as u32: 65
char from 65: A
Invalid Unicode code point

Character Methods

Character Classification


fn main() {
    let chars = ['A', 'z', '5', ' ', '!', 'δΈ­', 'πŸ˜€'];
    
    for ch in chars {
        println!("\nCharacter: '{}'", ch);
        println!("  is_alphabetic: {}", ch.is_alphabetic());
        println!("  is_numeric: {}", ch.is_numeric());
        println!("  is_alphanumeric: {}", ch.is_alphanumeric());
        println!("  is_whitespace: {}", ch.is_whitespace());
        println!("  is_ascii: {}", ch.is_ascii());
        println!("  is_ascii_digit: {}", ch.is_ascii_digit());
        println!("  is_ascii_alphabetic: {}", ch.is_ascii_alphabetic());
    }
}

Output:


Character: 'A'
  is_alphabetic: true
  is_numeric: false
  is_alphanumeric: true
  is_whitespace: false
  is_ascii: true
  is_ascii_digit: false
  is_ascii_alphabetic: true

Character: 'z'
  is_alphabetic: true
  is_numeric: false
  is_alphanumeric: true
  is_whitespace: false
  is_ascii: true
  is_ascii_digit: false
  is_ascii_alphabetic: true

Character: '5'
  is_alphabetic: false
  is_numeric: true
  is_alphanumeric: true
  is_whitespace: false
  is_ascii: true
  is_ascii_digit: true
  is_ascii_alphabetic: false

Character: ' '
  is_alphabetic: false
  is_numeric: false
  is_alphanumeric: false
  is_whitespace: true
  is_ascii: true
  is_ascii_digit: false
  is_ascii_alphabetic: false

Character: '!'
  is_alphabetic: false
  is_numeric: false
  is_alphanumeric: false
  is_whitespace: false
  is_ascii: true
  is_ascii_digit: false
  is_ascii_alphabetic: false

Character: 'δΈ­'
  is_alphabetic: true
  is_numeric: false
  is_alphanumeric: true
  is_whitespace: false
  is_ascii: false
  is_ascii_digit: false
  is_ascii_alphabetic: false

Character: 'πŸ˜€'
  is_alphabetic: false
  is_numeric: false
  is_alphanumeric: false
  is_whitespace: false
  is_ascii: false
  is_ascii_digit: false
  is_ascii_alphabetic: false

Case Conversion


fn main() {
    let chars = ['A', 'z', 'Γ‘', 'ß', '5'];
    
    for ch in chars {
        println!("Original: '{}'", ch);
        
        // Convert to uppercase
        let uppercase: String = ch.to_uppercase().collect();
        println!("  Uppercase: '{}'", uppercase);
        
        // Convert to lowercase
        let lowercase: String = ch.to_lowercase().collect();
        println!("  Lowercase: '{}'", lowercase);
        
        // ASCII-only conversions (faster)
        if ch.is_ascii() {
            println!("  ASCII uppercase: '{}'", ch.to_ascii_uppercase());
            println!("  ASCII lowercase: '{}'", ch.to_ascii_lowercase());
        }
        println!();
    }
}

Output:

Original: 'A'
  Uppercase: 'A'
  Lowercase: 'a'
  ASCII uppercase: 'A'
  ASCII lowercase: 'a'

Original: 'z'
  Uppercase: 'Z'
  Lowercase: 'z'
  ASCII uppercase: 'Z'
  ASCII lowercase: 'z'

Original: 'Γ‘'
  Uppercase: 'Γ‘'
  Lowercase: 'Γ±'

Original: 'ß'
  Uppercase: 'SS'
  Lowercase: 'ß'

Original: '5'
  Uppercase: '5'
  Lowercase: '5'
  ASCII uppercase: '5'
  ASCII lowercase: '5'

Character Validation and Conversion


fn main() {
    // Digit conversion
    let digit_char = '7';
    if let Some(digit_value) = digit_char.to_digit(10) {
        println!("'{}' as digit: {}", digit_char, digit_value);
    }
    
    // Hex digit conversion
    let hex_char = 'F';
    if let Some(hex_value) = hex_char.to_digit(16) {
        println!("'{}' as hex digit: {}", hex_char, hex_value);
    }
    
    // Create digit character
    if let Some(new_char) = char::from_digit(9, 10) {
        println!("Digit 9 as char: '{}'", new_char);
    }
    
    // Invalid conversions
    let invalid_digit = 'G';
    match invalid_digit.to_digit(10) {
        Some(value) => println!("'{}' as digit: {}", invalid_digit, value),
        None => println!("'{}' is not a valid decimal digit", invalid_digit),
    }
}

Output:

'7' as digit: 7
'F' as hex digit: 15
Digit 9 as char: '9'
'G' is not a valid decimal digit

Practical Examples

Character Processing Function


fn analyze_character(ch: char) -> String {
    let mut description = String::new();
    
    if ch.is_ascii_alphabetic() {
        description.push_str("ASCII letter");
        if ch.is_ascii_uppercase() {
            description.push_str(" (uppercase)");
        } else {
            description.push_str(" (lowercase)");
        }
    } else if ch.is_alphabetic() {
        description.push_str("Unicode letter");
    } else if ch.is_ascii_digit() {
        description.push_str("ASCII digit");
    } else if ch.is_numeric() {
        description.push_str("Unicode numeric");
    } else if ch.is_whitespace() {
        description.push_str("Whitespace");
    } else if ch.is_ascii_punctuation() {
        description.push_str("ASCII punctuation");
    } else {
        description.push_str("Other Unicode character");
    }
    
    description
}

fn main() {
    let test_chars = ['A', 'Γ±', '5', 'Β½', ' ', '!', 'δΈ­', 'πŸ˜€'];
    
    for ch in test_chars {
        println!("'{}': {}", ch, analyze_character(ch));
    }
}

Output:

'A': ASCII letter (uppercase)
'Γ±': Unicode letter
'5': ASCII digit
'Β½': Unicode numeric
' ': Whitespace
'!': ASCII punctuation
'δΈ­': Unicode letter
'πŸ˜€': Other Unicode character

Boolean Logic Example


fn can_access_system(age: u8, has_badge: bool, is_employee: bool, is_weekend: bool) -> bool {
    let is_adult = age >= 18;
    let is_authorized = has_badge && is_employee;
    let is_business_hours = !is_weekend;
    
    // Access granted if adult AND authorized AND (business hours OR has special access)
    is_adult && is_authorized && (is_business_hours || age >= 21)
}

fn main() {
    let scenarios = [
        (17, true, true, false),   // Minor employee, weekday
        (20, true, true, false),   // Adult employee, weekday
        (20, true, true, true),    // Adult employee, weekend
        (25, false, true, false),  // Adult without badge
        (22, true, true, true),    // Adult employee with special access, weekend
    ];
    
    for (age, badge, employee, weekend) in scenarios {
        let access = can_access_system(age, badge, employee, weekend);
        let day_type = if weekend { "weekend" } else { "weekday" };
        println!("Age {}, badge: {}, employee: {}, {}: Access {}",
                 age, badge, employee, day_type,
                 if access { "GRANTED" } else { "DENIED" });
    }
}

Output:

Age 17, badge: true, employee: true, weekday: Access DENIED
Age 20, badge: true, employee: true, weekday: Access GRANTED
Age 20, badge: true, employee: true, weekend: Access DENIED
Age 25, badge: false, employee: true, weekday: Access DENIED
Age 22, badge: true, employee: true, weekend: Access GRANTED

Common Pitfalls

Character Size Assumptions


fn main() {
    // char is always 4 bytes in Rust (Unicode scalar value)
    println!("Size of char: {} bytes", std::mem::size_of::());
    
    // This is different from C/C++ where char is 1 byte
    let ascii_char = 'A';
    let unicode_char = 'πŸ˜€';
    
    println!("'A' takes {} bytes in memory", std::mem::size_of_val(&ascii_char));
    println!("'πŸ˜€' takes {} bytes in memory", std::mem::size_of_val(&unicode_char));
    
    // For UTF-8 encoding, use strings
    let string_ascii = "A";
    let string_unicode = "πŸ˜€";
    
    println!("\"A\" as UTF-8: {} bytes", string_ascii.len());
    println!("\"πŸ˜€\" as UTF-8: {} bytes", string_unicode.len());
}

Output:

Size of char: 4 bytes
'A' takes 4 bytes in memory
'πŸ˜€' takes 4 bytes in memory
"A" as UTF-8: 1 bytes
"πŸ˜€" as UTF-8: 4 bytes

Checks for Understanding

Question 1

What will this expression evaluate to?


let result = false || true && false;
Click to see answer

false. The && operator has higher precedence than ||, so this evaluates as false || (true && false) which is false || false = false.

Question 2

How many bytes does a Rust char take in memory, and why?

Click to see answer

4 bytes. Rust's char represents a Unicode scalar value, which requires up to 4 bytes to represent all possible Unicode characters. This is different from C/C++ where char is typically 1 byte.

Question 3

What's the difference between ch.to_uppercase() and ch.to_ascii_uppercase()?

Click to see answer

to_uppercase() handles full Unicode case conversion and returns an iterator (may produce multiple characters, like ß β†’ SS). to_ascii_uppercase() only works on ASCII characters and returns a single char, but is faster.


← PreviousNext β†’