Fundamentals 13 min read

Build a Full-Featured Rust CLI Calculator from Scratch

This tutorial walks you through creating a command-line calculator in Rust, covering project setup, core features like basic arithmetic, input parsing, robust error handling, modular function design, performance tips, and ideas for extending functionality.

Architecture Development Notes
Architecture Development Notes
Architecture Development Notes
Build a Full-Featured Rust CLI Calculator from Scratch

Project Overview and Feature Planning

We will build a calculator with the following core capabilities:

Support basic arithmetic operations: addition, subtraction, multiplication, division, and modulo.

Parse user-entered mathematical expressions.

Provide clear error messages and exception handling.

Use a modular function design.

The program reads an expression such as 5 + 3, parses the numbers and operator, performs the calculation, and outputs the result.

Environment Setup and Project Initialization

Before coding, create a new Rust project:

mkdir rust_calculator
cd rust_calculator
cargo init

This creates the standard Rust project structure with src/main.rs and Cargo.toml.

Full Code Implementation

Below is the complete source code for the calculator:

use std::io;

fn main() {
    println!("Simple Calculator");
    println!("Available operations: +, -, *, /, %");
    println!("Enter your expression (e.g., 5 + 3):");

    let mut input = String::new();
    io::stdin()
        .read_line(&mut input)
        .expect("Failed to read input");

    let tokens: Vec<&str> = input.trim().split_whitespace().collect();

    if tokens.len() != 3 {
        println!("Invalid input. Please follow the format: number operator number");
        return;
    }

    let num1: f64 = match tokens[0].parse() {
        Ok(n) => n,
        Err(_) => {
            println!("Invalid first number.");
            return;
        }
    };

    let operator = tokens[1];

    let num2: f64 = match tokens[2].parse() {
        Ok(n) => n,
        Err(_) => {
            println!("Invalid second number.");
            return;
        }
    };

    let result = match operator {
        "+" => add(num1, num2),
        "-" => subtract(num1, num2),
        "*" => multiply(num1, num2),
        "/" => divide(num1, num2),
        "%" => modulo(num1, num2),
        _ => {
            println!("Invalid operator. Use +, -, *, /, or %.");
            return;
        }
    };

    println!("Result: {:.2}", result);
}

fn add(a: f64, b: f64) -> f64 { a + b }
fn subtract(a: f64, b: f64) -> f64 { a - b }
fn multiply(a: f64, b: f64) -> f64 { a * b }

fn divide(a: f64, b: f64) -> f64 {
    if b == 0.0 {
        println!("Division by zero is not allowed.");
        std::process::exit(1);
    }
    a / b
}

fn modulo(a: f64, b: f64) -> f64 {
    if b == 0.0 {
        println!("Division by zero is not allowed.");
        std::process::exit(1);
    }
    ((a % b) + b) % b
}

In-Depth Code Structure Analysis

Dependency Import and Main Function Framework

The line use std::io; imports Rust's standard I/O module, enabling keyboard input handling and keeping the code explicit and maintainable.

User Interaction and Input Handling

The program greets the user with println! macros, then reads input into a mutable String using io::stdin().read_line() and handles I/O errors with expect().

Input Parsing and Validation

The input string is trimmed, split by whitespace, and collected into a Vec<&str>. The program checks that exactly three tokens are present; otherwise it prints an error and exits.

Number Parsing and Error Handling

Each numeric token is parsed to f64 using match tokens[i].parse(). Successful parses yield the number; failures print an error message and terminate the function.

Operator Matching and Function Calls

A match expression selects the appropriate arithmetic function based on the operator token, with a wildcard branch handling unsupported operators.

Details of Math Function Implementations

Basic Arithmetic Functions

The first three functions are straightforward:

fn add(a: f64, b: f64) -> f64 { a + b }
fn subtract(a: f64, b: f64) -> f64 { a - b }
fn multiply(a: f64, b: f64) -> f64 { a * b }

Safe Division Handling

The divide function checks for a zero divisor, prints an error, and exits the program to avoid a panic.

Special Modulo Implementation

The modulo function also guards against division by zero and uses the formula ((a % b) + b) % b to ensure a non‑negative result, matching mathematical modulo semantics.

Running and Testing the Program

Build the project with: cargo build Run it using: cargo run Example inputs and expected outputs: 7 + 3

10.00
15 - 8

7.00
4 * 6

24.00
20 / 4

5.00
17 % 5

2.00

Error Handling Mechanism Analysis

Input Reading Errors : Handled with expect().

Format Validation Errors : Checks token count.

Number Parsing Errors : Uses match on parse().

Operator Validation Errors : Wildcard branch in match.

Mathematical Errors : Division by zero checks.

These layers ensure robustness and a good user experience.

Performance Optimization Considerations

Memory Usage : Reusing buffers instead of allocating a new string each time.

Parsing Efficiency : For complex expressions, consider a dedicated parsing library.

Error Handling : Use Result types instead of exiting directly.

Feature Extension Suggestions

Looped Calculations : Allow multiple calculations without restarting.

Complex Expressions : Add operator precedence and parentheses.

History : Store and display past calculations.

Variables : Enable user-defined variables.

More Math Functions : Include exponentiation, trigonometry, etc.

Code Quality and Best Practices

Ownership Management : Proper use of borrowing and ownership for strings.

Error Handling : Follows Rust's recommended patterns.

Function Design : Each function has a clear responsibility and I/O.

Type Safety : Leverages Rust's type system to avoid runtime errors.

Learning Outcomes and Skill Improvements

By building this calculator you will master:

Rust syntax and basic data types.

User input handling and string manipulation.

Pattern matching and error handling.

Function definition and invocation.

Fundamental program structure design.

These skills lay a solid foundation for more advanced Rust projects, whether in web development, systems programming, or other domains.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

CLIRustcode optimizationerror handlingProgramming tutorialCalculator
Architecture Development Notes
Written by

Architecture Development Notes

Focused on architecture design, technology trend analysis, and practical development experience sharing.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.