Skip to content

Commit

Permalink
lesson2
Browse files Browse the repository at this point in the history
  • Loading branch information
stream-rahul committed Jan 26, 2025
1 parent b8f2749 commit 7e815ce
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 236 deletions.
287 changes: 160 additions & 127 deletions _posts/2024-01-27-lesson2.md
Original file line number Diff line number Diff line change
@@ -1,149 +1,182 @@
---
layout: default
title: "Lesson 2: Functions and Control Flow in Rust"
title: "Lesson 2: Getting Current Directory in Rust"
date: 2024-01-27
categories: rust
permalink: /lesson2/ # ← Added trailing slash
permalink: /lesson2/
---

# Lesson 2: Functions and Control Flow in Rust
# Lesson 2: PWD In Rust 🗂️

In this lesson, we'll learn how to define functions and use control flow constructs like `if` statements and loops in Rust.
## Understanding PWD Implementation

---
<details>
<summary>🔍 View PWD Implementation in C (with Internal Details)</summary>

## Functions in Rust <span class="math">(1)</span>
```c
#include <windows.h>
#include <stdio.h>
#include <tchar.h>

```rust
fn greet() { // (1.1) Void function declaration
println!("Hello, Rustacean!"); // (1.2) Side effect operation
// Mathematical basis: O(1) time complexity
// Edge case: No input parameters means no validation needed
// Potential pitfall: Could create infinite recursion if called recursively
}
int _tmain(int argc, TCHAR* argv[]) {
// (1) Probe for Buffer Size: Compiler injects call
// to GetCurrentDirectory, returning required buffer
// size. Triggers kernel API interaction, marking
// CPU cycles for syscall handling. DWORD integral
// type maps to CPU register efficiency.
DWORD requiredSize = GetCurrentDirectory(0, NULL);

fn main() {
greet(); // (1.3) Function invocation
// Control flow path: Linear execution
// Memory allocation: Stack frame created for greet()
}
if (requiredSize == 0) { // (2) Handle Probe Failure
// GetLastError interacts with thread-local
// storage, fetching last-error code. Calls
// context-switch to retrieve specific error.
// Low-level formatted I/O via _ftprintf,
// exploiting buffered I/O optimizations.
_ftprintf(stderr, _T("Error 0x%lx: Buffer size probe failed\n"), GetLastError());
return 1; // Non-zero exit triggers OS-level
// failure signal.
}

fn add(x: i32, y: i32) -> i32 { // (2.1) Function signature
// Parameters: x (i32, 4 bytes), y (i32, 4 bytes)
// Return type: i32 (32-bit signed integer)
// Mathematical operation: x + y ∈ [-2³¹, 2³¹-1]
// Edge case: x = 2,147,483,647, y = 1 → overflow
x + y // (2.2) Implicit return
// Assembly equivalent: `lea eax, [rdi + rsi]`
// No semicolon: Expression vs statement distinction
}
// (3) Allocate Memory Buffer: malloc triggers heap
// allocation in process virtual address space. Size
// calculation uses sizeof(TCHAR) to ensure proper
// byte-alignment per character. TCHAR abstracts
// character width, enabling single code path for
// Unicode support.
TCHAR* buffer = (TCHAR*)malloc(requiredSize * sizeof(TCHAR));

fn main() {
let sum = add(5, 3); // (2.3) Function call
// Stack behavior: Parameters pushed right-to-left
// Type inference: sum deduced as i32
// Memory layout: 5 (0x00000005), 3 (0x00000003)
println!("5 + 3 = {}", sum); // (2.4) Format string
// Format specifier: {} uses Display trait
// String interpolation: Heap allocation for formatted string
}
if (!buffer) { // (4) Handle Allocation Failure
// Buffer allocation failure traps memory
// constraints; error message via _ftprintf.
// Aligns with system I/O operations.
_ftprintf(stderr, _T("Error: Alloc %lu chars failed\n"), requiredSize);
return 1; // Propagates failure state to OS.
}

// (5) Retrieve Directory Path: GetCurrentDirectory
// writes to allocated buffer, interacting with
// kernel-mode, transferring data from file system
// to user space. charsWritten holds count of
// characters written, internal buffer data alignment
// optimized for sequential memory access.
DWORD charsWritten = GetCurrentDirectory(requiredSize, buffer);

fn main() {
let number = 7; // (3.1) Immutable binding
// Binary representation: 0b00000111
// Prime number property: 7 is a Mersenne prime (2³ - 1)

if number % 2 == 0 { // (3.2) Conditional check
// Mathematical operation: number mod 2
// Branch prediction: Static vs dynamic
println!("{} is even", number);
} else { // (3.3) Alternative path
// Probability: 50% for random uniform distribution
// Code coverage: Requires odd test case
println!("{} is odd", number);
}
// Control flow graph: Diamond structure
// Cyclomatic complexity: 2 paths
}
fn main() {
let mut count = 0; // (4.1.1) Mutable state
// Initial value: 0 (0x00000000)
// Alignment: 4 bytes on stack

loop { // (4.1.2) Infinite loop
// Hardware equivalent: jmp instruction
// Termination: Depends on break condition
println!("Count: {}", count);
// Format string parsing: O(n) time
count += 1; // (4.1.3) Mutation
// Atomic operation: Not thread-safe
// Binary operation: count = count + 1

if count >= 5 { // (4.1.4) Exit condition
// Boundary check: >= vs ==
// Edge case: count = i32::MAX would overflow
break; // (4.1.5) Loop termination
// Control flow: jumps to loop end
}
if (charsWritten == 0) { // (6) Handle Path Retrieval Failure
// Error handling engages GetLastError, buffering
// I/O through _ftprintf. Prevents memory leak
// via free(), deallocating heap space.
_ftprintf(stderr, _T("Error 0x%lx: Path read failed\n"), GetLastError());
free(buffer); // Critical to avoid resource leak.
return 1; // Non-zero exit signals failure to OS.
}
// Final state: count = 5
// Loop iterations: 5 (0-4 inclusive)
}
fn main() {
let mut count = 0; // (4.2.1) Initialization
// Memory address: stack offset 8

while count < 5 { // (4.2.2) Pre-checked loop
// Condition evaluation: Before each iteration
// Comparison: signed less-than
println!("Count: {}", count);
// I/O operation: System call overhead
count += 1; // (4.2.3) Increment
// Optimization: Could be replaced with for loop

// (7) Validate Path Length: Ensures charsWritten
// matches requiredSize - 1, detecting inconsistencies.
// charsWritten reflects accurate character count.
// Discrepancy here suggests potential issues in file
// system or path changes during execution.
if (charsWritten != requiredSize - 1) {
_ftprintf(stderr, _T("Error: Size mismatch (%lu vs %lu)\n"), requiredSize - 1, charsWritten);
free(buffer); // Ensure memory release.
return 1; // Exit signals resource validation fail.
}
// Loop invariant: 0 ≤ count ≤ 5
// Termination proof: count strictly increases
}
fn is_positive(n: i32) -> bool { // (5.1) Predicate function
// Domain: n ∈ [-2³¹, 2³¹-1]
// Range: {true, false}
// Mathematical definition: n > 0
// Edge case: n = 0 → false
n > 0 // (5.2) Boolean expression
// Assembly: test instruction + setg

// (8) Output Directory Path: _tprintf interacts with
// stdout, leveraging terminal I/O subsystems.
// Buffer data remains intact due to prior validation,
// ensuring correct output.
_tprintf(_T("%s\n"), _T("The dir is \n"));
_tprintf(_T("%s\n"), buffer);

free(buffer); // Deallocates dynamic memory,
// releases heap space, crucial for avoiding leaks.
return 0; // Zero exit status signals success,
// concluding process cycle efficiently.
}


```
</details>
```rust
use std::env; // (1) `std::env` acts as a bridge between Rust
// and the underlying OS, using syscalls
// like `GetCurrentDirectory` (Windows) or
// `getcwd` (POSIX). These rely on syscall
// error handling, mapping to Rust's Result.
use std::io; // (2) IO imports connect to platform-specific
// stdio operations, heavily relying on libc.
// Provides abstractions for syscall-safe
// I/O, preventing UB caused by direct OS calls.
use std::path::PathBuf; // (3) `PathBuf` is an owned, mutable
// representation of filesystem paths,
// internally handling path separators
// for cross-platform compatibility.
fn main() {
// Task 1: Test is_positive
let num = 42; // (5.3) Test value
// Hexadecimal: 0x2A
// Prime factors: 2 × 3 × 7
println!("Is {} positive? {}", num, is_positive(num));

// Task 2: Print 1-5 using loop
let mut i = 1; // (5.4) Counter
// Initialization: Start at 1
loop {
println!("Count: {}", i); // (5.5) Output
// String formatting: Allocates temporary buffer
i += 1; // (5.6) Increment
// Optimization: Could use range iterator

if i > 5 { // (5.7) Exit condition
// Boundary: Exclusive upper limit
break;
// (4) `current_dir` syscall-like abstraction
// retrieves current working directory, translating
// OS-specific errors (e.g., errno) into Rust enums.
let path = env::current_dir().unwrap_or_else(|e| {
// (5) Error recovery logic uses lambdas (closures).
// `raw_os_error` extracts OS-level error codes,
// stored in platform-dependent formats (e.g.,
// Windows NTSTATUS or POSIX errno values).
match e.raw_os_error() {
Some(code) => eprintln!(
"Error 0x{:x}: Path resolution failed",
code
), // (6) `eprintln!` directly interacts with
// stderr using `libc`-backed syscalls like
// `write(2)`. Optimized for debug contexts.
None => eprintln!("Unknown error occurred"),
}
}

// Task 3: Check if number > 10
let value = 15; // (5.8) Test value
// Binary: 0b00001111
if value > 10 { // (5.9) Comparison
// Branch prediction: Likely taken
println!("{} exceeds threshold", value);
}
// Alternative path: No else clause
// Code coverage: Need value ≤10 to test
std::process::exit(1); // (7) `exit` bypasses stack
// unwinding, triggering immediate
// OS termination via `exit_group`
// syscall (Linux) or `_exit`
// (Windows).
});
// (8) `to_str` converts the internal byte buffer in `PathBuf`
// to a UTF-8 `&str`, ensuring safe memory reads. If path
// contains invalid UTF-8, returns `None`, preventing UB
// common in direct C-style string manipulations.
let path_str = path.to_str().unwrap_or_else(|| {
eprintln!("Error: Invalid UTF-8 in path");
std::process::exit(1);
});
// (9) `ends_with` inspects path buffer tail bytes without
// unnecessary allocations, leveraging slice comparisons.
// `trim_end_matches` avoids reallocation unless necessary.
let formatted_path = if path_str.ends_with('\\') && path_str.len() == 3 {
// (10) Edge case logic to handle root directories.
// Special cases for paths like `C:\` are preserved
// explicitly to mirror Windows behavior. Rust runtime
// avoids reallocating unchanged `&str` slices.
path_str
} else {
path_str.trim_end_matches('\\') // (11) Optimized trim
// avoids intermediate
// memory copies by
// working directly
// on slices.
};
println!("{}", formatted_path); // (12) `println!` invokes
// buffered stdout syscalls
// (via `write` or `WriteFile`),
// ensuring atomic writes.
}
```
```

Exercise:

Let us implement this in C++ using closures and compile time tricks
Loading

0 comments on commit 7e815ce

Please sign in to comment.