Skip to content

Commit fbbc673

Browse files
committed
Update 2024-01-25-lesson0.md
1 parent 6d64990 commit fbbc673

File tree

1 file changed

+135
-149
lines changed

1 file changed

+135
-149
lines changed

_posts/2024-01-25-lesson0.md

+135-149
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ categories: rust
66
permalink: /lesson0/
77
---
88

9-
# Lesson 0: CP
9+
## Lesson 0: CP
1010

11-
# Co relation to the C language usage
11+
## Co relation to the C language usage
1212

1313
1. **Args**: `env::args()``argc/argv` (Rust is type-safe)
1414
2. **Files**: `File::open()``fopen()` (`Result<>` vs `NULL`)
@@ -17,182 +17,168 @@ permalink: /lesson0/
1717
5. **Memory**: Auto `drop()` ↔ Manual `fclose()`
1818
6. **Exit**: `process::exit()``exit()` (same codes)
1919

20+
## Rust Concepts for this Lesson
2021

22+
1. **Result Type**: Rust uses `Result<T, E>` for error handling
23+
- `Ok(T)`: Success case with value of type T
24+
- `Err(E)`: Error case with error of type E
25+
26+
2. **File Operations**:
27+
- Uses the `std::fs::File` struct
28+
- File operations return `Result` types
29+
- Automatic resource cleanup via RAII
30+
31+
3. **Ownership Model**:
32+
- Each value has a single owner
33+
- Owner is responsible for cleanup
34+
- Ensures memory safety without garbage collection
35+
36+
4. **Error Handling**:
37+
- Pattern matching with `match`
38+
39+
5. **Command Line Arguments**:
40+
- Safe iteration over arguments
41+
- UTF-8 validation built-in
2142

2243
```rust
44+
45+
46+
// Variable (1): `args`. Type: `Vec<String>`. Stores
47+
// command-line arguments as strings, where args[0] is
48+
// the program's path.
49+
// Domain: Vector of strings from command line.
50+
// Range: Depends on number of args passed.
51+
// Edge: Empty args, too few or many args given.
52+
2353
use std::env;
2454
use std::fs::File;
2555
use std::io::{Read, Write};
2656
use std::process;
2757

28-
// BUF_SIZE: The size of the buffer used for reading and writing
29-
// files (1).
30-
// Represents the maximum number of bytes read/written in
31-
// a single operation.
32-
// Set to 256 bytes, implying a trade-off between system
33-
// call overhead and memory usage.
34-
// A larger buffer might reduce system calls but increase
35-
// memory consumption.
36-
// A smaller buffer might increase system calls but reduce
37-
// memory consumption.
38-
// The optimal value depends on the specific I/O
39-
// characteristics and system resources.
40-
// Choosing a power of 2 often aligns with memory page
41-
// sizes, potentially improving efficiency.
42-
// Example: If reading a 1KB file, 4 read operations would
43-
// be required (1024 bytes / 256 bytes/read).
44-
const BUF_SIZE: usize = 256; // 2^8 = 256
58+
// Constant (2): `BUF_SIZE`. Type: `usize`.
59+
// Defines fixed size of data buffer.
60+
// Domain: Positive integers, max depending on platform.
61+
// Range: 256, memory safety constraint
62+
// Edge: If too big, causes stack overflow, too small
63+
// then many more reads and writes, perf impact
64+
65+
const BUF_SIZE: usize = 256;
4566

4667
fn main() {
47-
// args: A vector of strings representing the command-line
48-
// arguments passed to the program (2).
49-
// args[0] is the program name itself.
50-
// args[1] is expected to be the input file path.
51-
// args[2] is expected to be the output file path.
52-
// Example: If executed as "./my_program input.txt
53-
// output.txt",
54-
// args[0] would be "./my_program", args[1] would be
55-
// "input.txt", and args[2] would be "output.txt".
56-
let args: Vec<String> = env::args().collect(); // Vec<String> signifies
57-
// a dynamically sized array of String objects, meaning it can store
58-
// a variable number of strings.
59-
60-
// (3) Check if the correct number of arguments is provided
61-
// (program name, input file, output file).
62-
// args.len(): Returns the number of elements in the vector
63-
// `args`. In this code, it checks if exactly two arguments (input
64-
// and output files) are given in addition to the executable name.
65-
if args.len() != 3 { // 3 represents the expected number of
66-
// arguments: program name + input file path + output file path
67-
// eprintln!: Prints an error message to the standard error
68-
// stream (stderr). This is commonly used to display error
69-
// messages to the user in the console without cluttering the
70-
// standard output.
71-
//{}: A placeholder in the format string that will be
72-
// replaced with the corresponding argument.
73-
eprintln!("Usage: {} file1 file2", args[0]); // args[0] is
74-
// always the program name, which provides context to the
75-
// error message
76-
77-
// process::exit(1): Terminates the program with an exit code
78-
// of 1. Non-zero exit codes conventionally indicate an error.
79-
// 1: Signifies a general error exit code. This tells the
80-
// operating system or calling process that the program
81-
// encountered an issue. Different exit codes can be used to
82-
// signal different types of errors.
83-
process::exit(1); // Exit with a non-zero status code
84-
// indicating an error. 1 is a common code for general errors.
68+
// Variable (3): `args`. Type: `Vec<String>`. Stores command
69+
// line args. Purpose is processing file copies
70+
// based on input args.
71+
let args: Vec<String> = env::args().collect();
72+
73+
// Control Flow (4): Conditional `if`. Checks if number
74+
// of command line args is exactly 3 (program name,
75+
// input file path and output file path)
76+
// Edge Case: `args.len()` too short, too long.
77+
if args.len() != 3 {
78+
// Operation (5): Prints usage info on stderr.
79+
// Implies error in number of args.
80+
eprintln!("Usage: {} file1 file2", args[0]);
81+
// Operation (6): Terminate program with failure
82+
// code. Indicates incorrect usage.
83+
process::exit(1);
8584
}
8685

87-
// (4) Attempt to open the input file specified by the first
88-
// command-line argument (`args[1]`).
89-
// File::open(&args[1]): Attempts to open the file specified by
90-
// `args[1]` in read-only mode. Returns a `Result<File, Error>`.
91-
// match: A pattern-matching construct that handles different
92-
// outcomes of the `Result`.
93-
let mut in_file = match File::open(&args[1]) { // Attempt to
94-
// open the input file. args[1] contains the file path
95-
// provided as the first command-line argument.
96-
Ok(file) => file, // If successful, assign the
97-
// opened file to `in_file`.
98-
Err(err) => { // If an error occurs during
99-
// file opening...
100-
eprintln!("{}: {}", args[1], err); // Print an error
101-
// message indicating the file that couldn't be opened and
102-
// the reason.
103-
process::exit(2); // Exit with a non-zero status
104-
// code (2) indicating a file opening error.
86+
// Variable (7): `in_file`. Type: `File`. Represents
87+
// input file object which is opened for reading.
88+
// Purpose is to read contents from it.
89+
// Sub unit : File handle.
90+
// Result: `File`, or `Err`.
91+
let mut in_file = match File::open(&args[1]) {
92+
// Control Flow (8): `match` expression on `Result`.
93+
// `Ok` path opens file, `Err` handles open failure.
94+
Ok(file) => file,
95+
// Control Flow (9): Error handler for file open.
96+
Err(err) => {
97+
// Operation (10): Prints error details of open on
98+
// stderr. Indicates file access failure.
99+
eprintln!("{}: {}", args[1], err);
100+
// Operation (11): Exits program due to open
101+
// failure. Input file is critical.
102+
process::exit(2);
105103
}
106104
};
107105

108-
109-
// (5) Attempt to create the output file specified by the second
110-
// command-line argument (`args[2]`).
111-
//File::create(&args[2]) : Attempts to create a new file at the
112-
// path specified by `args[2]`. If the file already exists, it will
113-
// be truncated (overwritten). It returns a Result<File, Error>.
114-
let mut out_file = match File::create(&args[2]) { // Attempt to
115-
// create the output file. args[2] contains the file path
116-
// provided as the second command-line argument.
117-
Ok(file) => file, // If successful, assign the
118-
// created file to `out_file`.
119-
Err(err) => { // If an error occurs during
120-
// file creation...
121-
drop(in_file); // Close the input file to
122-
// release the resource.
123-
eprintln!("{}: {}", args[2], err); // Print an error
124-
// message indicating the file that couldn't be created and
125-
// the reason.
126-
process::exit(3); // Exit with a non-zero
127-
// status code (3) indicating a file creation error.
106+
// Variable (12): `out_file`. Type: `File`. Represents
107+
// output file object which is opened for writing.
108+
// Purpose is to write contents to it.
109+
// Sub unit : File handle.
110+
// Result: `File`, or `Err`.
111+
let mut out_file = match File::create(&args[2]) {
112+
// Control Flow (13): `match` expression on `Result`.
113+
// `Ok` creates file, `Err` handles create failure.
114+
Ok(file) => file,
115+
// Control Flow (14): Error handler for file create.
116+
Err(err) => {
117+
// Operation (15): Prints error details of file
118+
// create on stderr. Indicates file creation fail.
119+
eprintln!("{}: {}", args[2], err);
120+
// Operation (16): Exits program due to create
121+
// failure. Output file is critical.
122+
process::exit(3);
128123
}
129124
};
130125

126+
// Variable (17): `buffer`. Type: `[u8; BUF_SIZE]`.
127+
// Fixed size buffer to store data read from input
128+
// file. Data unit: fixed size array of bytes.
129+
// Size: BUF_SIZE bytes.
130+
let mut buffer = [0u8; BUF_SIZE];
131131

132-
// buffer: A fixed-size array of unsigned 8-bit integers (bytes)
133-
// used as a temporary storage for reading and writing data (6).
134-
// Initialized with zeros.
135-
// The size is determined by BUF_SIZE (256 bytes). This array
136-
// acts as an intermediary storage location for chunks of data read
137-
// from the input file and before they are written to the output file.
138-
let mut buffer = [0u8; BUF_SIZE]; // Create a buffer to hold
139-
// chunks of data read from the input file. It's initialized
140-
// with 0s.
141-
142-
143-
// (7) Loop to read from the input file and write to the output
144-
// file until the end of the input file is reached.
145-
// loop: An infinite loop that continues until explicitly broken
146-
// using `break`.
132+
// Control Flow (18): Infinite `loop`. Reads data from input file,
133+
// writes data to output file, terminates when
134+
// end-of-file is reached or error occurs
147135
loop {
148-
// (8) Read bytes from the input file into the buffer.
149-
// in_file.read(&mut buffer): Attempts to read bytes from
150-
// `in_file` into the `buffer`. Returns a `Result<usize, Error>`.
151-
// `usize`: Represents the number of bytes successfully read.
152-
let bytes_in = match in_file.read(&mut buffer) { // Read from
153-
// the input file into the buffer.
154-
Ok(n) => n, // If successful, `n` is
155-
// the number of bytes read. Assign it to `bytes_in`.
156-
Err(err) => { // If an error occurs
157-
// during reading...
158-
eprintln!("Error reading file: {}", err); // Print an
159-
// error message.
160-
process::exit(4); // Exit with a
161-
// non-zero status code (4) indicating a file reading
162-
// error.
136+
// Variable (19): `bytes_in`. Type: `usize`. Stores
137+
// number of bytes read from the file in current iteration.
138+
// Domain: Non-negative integers <= BUF_SIZE.
139+
// Range: 0 to BUF_SIZE.
140+
let bytes_in = match in_file.read(&mut buffer) {
141+
// Control Flow (20): `match` for result from read.
142+
// `Ok` provides number of bytes read.
143+
Ok(n) => n,
144+
// Control Flow (21): Error handler for read errors.
145+
Err(err) => {
146+
// Operation (22): Prints file read error to stderr.
147+
// Indicates low level file system issues.
148+
eprintln!("Error reading file: {}", err);
149+
// Operation (23): Terminates program on read error.
150+
// Input file data is necessary.
151+
process::exit(4);
163152
}
164153
};
165154

166-
// (9) Check if the end of the input file has been reached.
167-
// bytes_in == 0 : Checks if the number of bytes read
168-
// (`bytes_in`) is zero. A zero byte read indicates the end
169-
// of the file has been reached.
170-
if bytes_in == 0 { // If no bytes were read, it means
171-
// the end of the file has been reached.
172-
break; // Exit the loop.
155+
// Control Flow (24): Checks for end-of-file condition.
156+
// Checks if bytes read in last read were zero.
157+
if bytes_in == 0 {
158+
// Operation (25): Breaks infinite loop upon EOF.
159+
// Terminates read write cycle.
160+
break;
173161
}
174162

175-
// (10) Write the bytes read from the buffer to the output
176-
// file.
177-
// out_file.write_all(&buffer[..bytes_in]): Attempts to
178-
// write all bytes from the `buffer` up to the index
179-
// `bytes_in` to the `out_file`. Returns a `Result<(), Error>`.
180-
if let Err(err) = out_file.write_all(&buffer[..bytes_in]) {
181-
// Write the data from the buffer to the output file.
182-
// &buffer[..bytes_in] creates a slice of the buffer
183-
// containing only the bytes that were read.
184-
eprintln!("Fatal write error: {}", err); // If an error
185-
// occurs during writing, print an error message.
186-
process::exit(4); // Exit with a
187-
// non-zero status code (4) indicating a file writing
188-
// error. Why 4? Potentially reusing the same error code
189-
// for read/write errors for simplicity, or signifying a
190-
// fatal error at this stage.
163+
// Control Flow (26): Conditional `if let Err()`.
164+
// Writes bytes read to output file, checks for error
165+
if let Err(err) = out_file.write_all(&buffer[..bytes_in]) {
166+
// Operation (27): Print error message on stderr.
167+
// Indicates critical failure in file write.
168+
eprintln!("Fatal write error: {}", err);
169+
// Operation (28): Terminate program upon write fail
170+
// Output file write is necessary.
171+
process::exit(5);
191172
}
192173
}
193174
}
175+
176+
177+
178+
179+
194180
```
195181

196-
### Exercise
182+
## Exercise
197183

198184
Let us do byte by byte copy, exactly one byte. Read exactly one, write exactly one. No more and no less.

0 commit comments

Comments
 (0)