-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b8f2749
commit 7e815ce
Showing
3 changed files
with
194 additions
and
236 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.