0% found this document useful (0 votes)
5 views7 pages

Project - Building A Command Line Program (Rust)

This document outlines a project to build a command-line program in Rust through six structured labs, focusing on key concepts such as command-line argument handling, file reading, modular code organization, test-driven development, environment variable support, and error message management. Each lab introduces specific objectives and methods, demonstrating best practices in software engineering. The project culminates in a robust application that showcases Rust's core principles including ownership, error handling, and modular design.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views7 pages

Project - Building A Command Line Program (Rust)

This document outlines a project to build a command-line program in Rust through six structured labs, focusing on key concepts such as command-line argument handling, file reading, modular code organization, test-driven development, environment variable support, and error message management. Each lab introduces specific objectives and methods, demonstrating best practices in software engineering. The project culminates in a robust application that showcases Rust's core principles including ownership, error handling, and modular design.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Project: Building a Command Line Program (Rust)

Reference: The Rust Programming Language, Chapter 12

Overview
This project develops a simplified Rust command-line program through six structured labs. The program
reads command-line arguments, reads a file, searches for text, handles errors properly, supports
environment variables, and follows good software engineering practices such as separation of concerns
and test-driven development (TDD).

Lab 1: Using Vectors and Strings

Objective

Use iterators to collect command-line arguments into a vector.

Introduction

• Command-line arguments are values passed to a program when it is executed.


• Vectors ( Vec<T> ) are growable arrays in Rust.
• Iterators allow sequential access to collection elements.
• Rust provides std::env::args() to access command-line arguments as an iterator of String .

Method

1. Documentation

The program collects command-line arguments into a vector and accesses individual elements.

Usage:

cargo run arg1 arg2

2. Code Listing ([Link])

use std::env;

fn main() {
let args: Vec<String> = env::args().collect();

1
println!("Program name: {}", args[0]);

if [Link]() > 1 {
println!("First argument: {}", args[1]);
}
}

3. Testing and Results

• Running cargo run hello prints the program name and hello .

4. Analysis, Discussion, and Conclusion

This lab demonstrates how iterators and vectors simplify handling command-line input. Accessing
arguments via indexing is straightforward but requires length checks to avoid runtime errors.

Lab 2: Reading a File

Objective

Read and display the contents of a file using command-line arguments.

Introduction

Rust reads files using the std::fs module. File operations return Result<T, E> for error handling.

Method

1. Documentation

The program reads a filename passed as a command-line argument and prints its contents.

2. Code Listing

use std::env;
use std::fs;

fn main() {
let args: Vec<String> = env::args().collect();
let filename = &args[1];

let contents = fs::read_to_string(filename)


.expect("Could not read file");

2
println!("{}", contents);
}

3. Testing and Results

• Valid file: contents printed.


• Invalid file: program panics with an error message.

4. Analysis, Discussion, and Conclusion

Rust’s Result type enforces error awareness. Using expect is simple but not ideal for production
programs.

Lab 3: Organising Code in Modules

Objective

Refactor code into modules using separation of concerns.

Introduction

• Modules organize code into logical units.


• Separation of concerns ensures each part of the program has a single responsibility.

Method

[Link] (Program Control)

use std::env;
use std::process;
use minigrep::Config;

fn main() {
let args: Vec<String> = env::args().collect();

let config = Config::new(&args).unwrap_or_else(|err| {


eprintln!("Problem parsing arguments: {}", err);
process::exit(1);
});

if let Err(e) = minigrep::run(config) {


eprintln!("Application error: {}", e);
process::exit(1);

3
}
}

[Link] (Logic)

use std::error::Error;
use std::fs;

pub struct Config {


pub query: String,
pub filename: String,
}

impl Config {
pub fn new(args: &[String]) -> Result<Config, &'static str> {
if [Link]() < 3 {
return Err("Not enough arguments");
}

Ok(Config {
query: args[1].clone(),
filename: args[2].clone(),
})
}
}

pub fn run(config: Config) -> Result<(), Box<dyn Error>> {


let contents = fs::read_to_string([Link])?;
println!("{}", contents);
Ok(())
}

Analysis and Conclusion

Separating logic improves readability, maintainability, and testability. [Link] handles execution flow,
while [Link] contains reusable logic.

Lab 4: Test Driven Development (TDD)

Objective

Search for a word in a file and return matching lines.

4
Introduction

TDD involves writing failing tests before implementing functionality, ensuring correctness and
maintainability.

Method

1. Failing Test

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn one_result() {
let query = "duct";
let contents = "Rust:\nsafe, fast, productive.\nPick three.";

assert_eq!(vec!["safe, fast, productive."], search(query, contents));


}
}

2. Code to Pass the Test

pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {


let mut results = Vec::new();

for line in [Link]() {


if [Link](query) {
[Link](line);
}
}

results
}

Borrowing and Lifetimes

Returned lines borrow from contents , avoiding unnecessary allocations. The lifetime 'a ensures safety.

5
Lab 5: Environment Variables

Objective

Support case-insensitive search using environment variables.

Introduction

Environment variables allow configuring program behavior externally. Rust accesses them via
std::env::var .

Method

use std::env;

pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a


str> {
let query = query.to_lowercase();
let mut results = Vec::new();

for line in [Link]() {


if line.to_lowercase().contains(&query) {
[Link](line);
}
}

results
}

let case_insensitive = env::var("CASE_INSENSITIVE").is_ok();

Lab 6: Writing Error Messages to Standard Error

Objective

Direct error messages to the standard error stream.

Introduction

• stdout is for normal output.


• stderr is for errors.
• Rust uses eprintln! for standard error.

6
Method

if let Err(e) = minigrep::run(config) {


eprintln!("Application error: {}", e);
process::exit(1);
}

Testing and Results

• Redirecting output still displays errors on the terminal.

Final Conclusion
This project demonstrates core Rust concepts including ownership, error handling, modular design, testing,
and environment configuration. Following Chapter 12 of The Rust Book, the labs progressively build a robust
and maintainable command-line application.

You might also like