@@ -6,9 +6,9 @@ categories: rust
6
6
permalink : /lesson0/
7
7
---
8
8
9
- # Lesson 0: CP
9
+ ## Lesson 0: CP
10
10
11
- # Co relation to the C language usage
11
+ ## Co relation to the C language usage
12
12
13
13
1 . ** Args** : ` env::args() ` ↔ ` argc/argv ` (Rust is type-safe)
14
14
2 . ** Files** : ` File::open() ` ↔ ` fopen() ` (` Result<> ` vs ` NULL ` )
@@ -17,182 +17,168 @@ permalink: /lesson0/
17
17
5 . ** Memory** : Auto ` drop() ` ↔ Manual ` fclose() `
18
18
6 . ** Exit** : ` process::exit() ` ↔ ` exit() ` (same codes)
19
19
20
+ ## Rust Concepts for this Lesson
20
21
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
21
42
22
43
``` 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
+
23
53
use std :: env;
24
54
use std :: fs :: File ;
25
55
use std :: io :: {Read , Write };
26
56
use std :: process;
27
57
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 ;
45
66
46
67
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 );
85
84
}
86
85
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 );
105
103
}
106
104
};
107
105
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 );
128
123
}
129
124
};
130
125
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 ];
131
131
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
147
135
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 );
163
152
}
164
153
};
165
154
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 ;
173
161
}
174
162
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 );
191
172
}
192
173
}
193
174
}
175
+
176
+
177
+
178
+
179
+
194
180
```
195
181
196
- ### Exercise
182
+ ## Exercise
197
183
198
184
Let us do byte by byte copy, exactly one byte. Read exactly one, write exactly one. No more and no less.
0 commit comments