Skip to content

Commit 4af7b6e

Browse files
added: on data, lesson 01, 02, on networking 01, 02, 05
1 parent c951f78 commit 4af7b6e

8 files changed

Lines changed: 752 additions & 2 deletions

File tree

docs/desktop_applications/02_Rust/10_Collections.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,11 @@ Beyond the core collections, Rust's standard library offers several others for m
143143

144144
- **`BTreeMap<K, V>`**: A map that stores key-value pairs in a sorted order, comparable to `std::map` in C++. It has a logarithmic $O(\\log n)$ time complexity for operations but guarantees a consistent, sorted iteration order.
145145

146-
- [Official `BTreeMap` Docs](<https://www.google.com/search?q=%5Bhttps://www.google.com/search%3Fq%3D%255Bhttps://www.google.com/search%253Fq%253D%25255Bhttps://doc.rust-lang.org/std/collections/struct.BTreeMap.html%25255D%255D%5D(https://www.google.com/search%3Fq%3D%255Bhttps://www.google.com/search%253Fq%253D%25255Bhttps://doc.rust-lang.org/std/collections/struct.BTreeMap.html%25255D%255D)(%5Bhttps://www.google.com/search%253Fq%253D%25255Bhttps://doc.rust-lang.org/std/collections/struct.BTreeMap.html%25255D%5D(https://www.google.com/search%253Fq%253D%25255Bhttps://doc.rust-lang.org/std/collections/struct.BTreeMap.html%25255D))(%255B%5Bhttps://doc.rust-lang.org/std/collections/struct.BTreeMap.html%255D%5D(https://doc.rust-lang.org/std/collections/struct.BTreeMap.html%255D)(%5Bhttps://doc.rust-lang.org/std/collections/struct.BTreeMap.html%5D(https://doc.rust-lang.org/std/collections/struct.BTreeMap.html)))>)
146+
- [Official `BTreeMap` Docs](https://doc.rust-lang.org/std/collections/struct.BTreeMap.html)
147147

148148
- **`BTreeSet<T>`**: A collection of unique, sorted values, similar to `std::set` in C++. It also provides logarithmic $O(\\log n)$ time complexity and guarantees sorted iteration.
149149

150-
- [Official `BTreeSet` Docs](https://doc.rust-lang.org/std/collections/struct.BTreeMap.html)
150+
- [Official `BTreeSet` Docs](https://doc.rust-lang.org/std/collections/struct.BTreeSet.html)
151151

152152
- **`VecDeque<T>`**: A double-ended queue, which is a growable array optimized for efficient pushes and pops from both the front and the back.
153153

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
---
2+
title: File I/O in Rust
3+
---
4+
5+
File Input/Output (I/O) is how your application reads and writes data to the computer's file system. In Rust, these operations are handled by the `std::fs` module and a set of I/O traits, all designed to be safe and to force you to handle potential errors.
6+
7+
---
8+
9+
### Opening a File
10+
11+
The main type for file operations is `std::fs::File`. To get an instance of this type, you use `File::open`. This function returns a **`Result<File, std::io::Error>`**, not the `File` itself. This is because a file might not exist or be accessible, and Rust requires you to handle these possibilities.
12+
13+
```rust
14+
use std::fs::File;
15+
use std::io::ErrorKind;
16+
17+
fn main() {
18+
let file_result = File::open("config.txt");
19+
20+
// The 'match' expression handles both possible outcomes of the Result.
21+
let file = match file_result {
22+
Ok(file) => file, // The file was opened successfully, we can now use it.
23+
Err(error) => match error.kind() {
24+
// A common, recoverable error: file not found.
25+
ErrorKind::NotFound => {
26+
println!("File not found. Creating a new one...");
27+
File::create("config.txt").unwrap()
28+
},
29+
// Any other error is unrecoverable, so we panic.
30+
other_error => {
31+
panic!("Problem opening the file: {:?}", other_error);
32+
}
33+
},
34+
};
35+
}
36+
```
37+
38+
**Why it can throw errors**: Any interaction with the operating system is prone to failure. The disk could be full, the file could be deleted by another process, or you may not have the correct permissions. Rust's `Result` type prevents these failures from causing a program crash by making you explicitly write code to handle them.
39+
40+
---
41+
42+
### Reading and Writing
43+
44+
Once a file is open, you can read from or write to it. The traits `std::io::Read` and `std::io::Write` provide the necessary methods.
45+
46+
#### Reading a File
47+
48+
To read a text file's entire contents into a `String`, you can use the `read_to_string` method. This method also returns a `Result` because the read operation can fail.
49+
50+
```rust
51+
use std::fs::File;
52+
use std::io::{self, Read};
53+
54+
// Returns a Result containing the file contents on success, or an error on failure.
55+
fn read_config(path: &str) -> Result<String, io::Error> {
56+
let mut file = File::open(path)?; // The '?' operator propagates an error if the file can't be opened.
57+
let mut contents = String::new();
58+
file.read_to_string(&mut contents)?; // '?' propagates an error if reading fails.
59+
Ok(contents)
60+
}
61+
```
62+
63+
Here, we use the **`?` operator** which is a clean and concise way to handle `Result` types. If the `Result` is `Ok`, the value is unwrapped. If it's `Err`, the function immediately returns with that error. This keeps the code from being cluttered with `match` statements.
64+
65+
#### Writing to a File
66+
67+
To write to a file, you first create it with `File::create`, and then use methods like `write_all`.
68+
69+
```rust
70+
use std::fs::File;
71+
use std::io::{self, Write};
72+
73+
fn save_data(path: &str, data: &str) -> Result<(), io::Error> {
74+
let mut file = File::create(path)?; // '?' propagates an error if the file can't be created.
75+
file.write_all(data.as_bytes())?; // '?' propagates an error if writing fails.
76+
Ok(())
77+
}
78+
```
79+
80+
This function demonstrates a common pattern where `Ok(())` is returned to signal a successful operation that doesn't produce a value.
81+
82+
---
83+
84+
### Putting It All Together
85+
86+
For a desktop application, you'll typically have functions that handle I/O and a top-level `main` function that manages the overall flow and displays any final errors to the user.
87+
88+
```rust
89+
use std::io::{self, Write};
90+
use std::fs::File;
91+
use std::error::Error;
92+
93+
// main() can return a Result to handle errors gracefully.
94+
fn main() -> Result<(), Box<dyn Error>> {
95+
// Attempt to save data.
96+
let save_result = save_data("log.txt", "Application started.");
97+
98+
// Handle the result at the top level.
99+
if let Err(e) = save_result {
100+
eprintln!("Error saving log file: {}", e);
101+
// We can choose to continue or exit.
102+
}
103+
104+
// This will work because the ? operator in save_data() ensures we get an error if saving fails.
105+
save_data("log.txt", "Another line of text.")?;
106+
107+
println!("File operations successful!");
108+
Ok(()) // All went well.
109+
}
110+
```
111+
112+
In this example, the `main` function itself returns a `Result`, allowing the program to cleanly exit with an error code if any of the file operations fail. This is a robust pattern for real-world desktop applications.
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
---
2+
title: JSON and the Serde Crate
3+
---
4+
5+
---
6+
7+
JSON (JavaScript Object Notation) is a lightweight, human-readable data format used to exchange data between a server and a web application, or as a configuration file format. It is language-independent, making it the de-facto standard for APIs and many other data-sharing scenarios. A typical JSON object looks like a key-value map, with nested structures and arrays.
8+
9+
---
10+
11+
### Introducing Serde
12+
13+
**Serde** is Rust's premier framework for **ser**ializing and **de**serializing data. Serialization is the process of converting a Rust data structure into a format like JSON, while deserialization is the reverse—converting JSON data into a Rust data structure. Serde is so widely used because it's fast, robust, and provides a simple way to convert complex data structures without writing boilerplate code. We'll use the `serde_json` crate, which provides the tools for handling JSON specifically.
14+
15+
---
16+
17+
### Setting Up Serde
18+
19+
To use Serde in your project, you must add it and `serde_json` to your `Cargo.toml` file. The `derive` feature for `serde` is essential, as it allows Rust to automatically generate the code for serialization and deserialization for your structs and enums.
20+
21+
```toml
22+
[dependencies]
23+
serde = { version = "1.0", features = ["derive"] }
24+
serde_json = "1.0"
25+
```
26+
27+
---
28+
29+
### Deserialization: JSON to Rust Struct
30+
31+
The most common use case is reading a JSON file or API response and converting it into a usable Rust data structure. You do this by defining a `struct` that mirrors the structure of your JSON data and then using the `serde_json::from_str` or `serde_json::from_reader` functions.
32+
33+
Let's assume we have a JSON file named `user.json`:
34+
35+
```json
36+
{
37+
"user_id": 42,
38+
"username": "alice",
39+
"is_active": true,
40+
"roles": ["admin", "member"]
41+
}
42+
```
43+
44+
Now, let's create a Rust struct that corresponds to this JSON data.
45+
46+
```rust
47+
use serde::Deserialize;
48+
use std::fs::File;
49+
use std::io::Read;
50+
51+
// Use the Deserialize trait to allow Serde to parse JSON into this struct.
52+
#[derive(Deserialize, Debug)]
53+
struct User {
54+
user_id: u32,
55+
username: String,
56+
is_active: bool,
57+
roles: Vec<String>,
58+
}
59+
60+
fn main() -> Result<(), Box<dyn std::error::Error>> {
61+
// Open the JSON file.
62+
let mut file = File::open("user.json")?;
63+
let mut contents = String::new();
64+
file.read_to_string(&mut contents)?;
65+
66+
// Use serde_json::from_str to deserialize the JSON into our User struct.
67+
// This function returns a Result, which we handle with the '?' operator.
68+
let user: User = serde_json::from_str(&contents)?;
69+
70+
println!("Deserialized user: {:?}", user);
71+
72+
Ok(())
73+
}
74+
```
75+
76+
The `#[derive(Deserialize, Debug)]` attribute is a procedural macro that automatically generates the code needed to convert a JSON object into a `User` struct. This is the core of Serde's power.
77+
78+
---
79+
80+
### Serialization: Rust Struct to JSON
81+
82+
The reverse process, serialization, is just as easy. You create an instance of your Rust struct and use `serde_json::to_string` to convert it into a JSON string.
83+
84+
```rust
85+
use serde::Serialize;
86+
use std::fs::File;
87+
use std::io::Write;
88+
89+
// We derive Serialize to convert the struct into JSON.
90+
#[derive(Serialize, Debug)]
91+
struct Product {
92+
id: u32,
93+
name: String,
94+
price: f64,
95+
}
96+
97+
fn main() -> Result<(), Box<dyn std::error::Error>> {
98+
let product = Product {
99+
id: 101,
100+
name: "Laptop".to_string(),
101+
price: 1200.50,
102+
};
103+
104+
// Serialize the Product struct into a JSON string.
105+
let serialized_product = serde_json::to_string(&product)?;
106+
107+
println!("Serialized JSON: {}", serialized_product);
108+
109+
// Save the serialized JSON to a file.
110+
let mut file = File::create("product.json")?;
111+
file.write_all(serialized_product.as_bytes())?;
112+
113+
println!("Product saved to product.json");
114+
115+
Ok(())
116+
}
117+
```
118+
119+
The `to_string` function returns a `Result`, which is crucial for handling potential errors during serialization.
120+
121+
---
122+
123+
### Advanced Serde Attributes
124+
125+
Serde provides many attributes to customize the serialization and deserialization process, which are essential for handling real-world JSON data.
126+
127+
- `#[serde(rename = "...")]`: This attribute is used when the key in your JSON file has a different naming convention than your Rust struct field. For example, a common convention is to use snake_case in Rust and kebab-case or camelCase in JSON.
128+
129+
```rust
130+
#[derive(Deserialize, Debug)]
131+
struct UserProfile {
132+
#[serde(rename = "first-name")] // Handles "first-name" from JSON
133+
first_name: String,
134+
#[serde(rename = "last_name")] // Handles "last_name" from JSON
135+
last_name: String,
136+
}
137+
```
138+
139+
- `#[serde(default)]`: If a field is optional in the JSON data, you can mark it with `default`. Serde will use the default value for the field's type if the key is missing from the JSON.
140+
141+
```rust
142+
#[derive(Deserialize, Debug)]
143+
struct Settings {
144+
theme: String,
145+
#[serde(default)] // This field will be 'false' if not present in the JSON.
146+
dark_mode: bool,
147+
}
148+
```
149+
150+
### Naming Convention Differences with Serde
151+
152+
When the naming conventions in your JSON data don't match Rust's standard **snake_case**, you can use the **`#[serde(rename = "...")]`** attribute to map the JSON key to your Rust struct field. This is very common for handling JSON from APIs that use **camelCase** or **kebab-case**.
153+
154+
---
155+
156+
For example, if your JSON data uses `userName` (camelCase) and `isVerified` (camelCase), but you want to use `user_name` and `is_verified` in your Rust struct, you would do this:
157+
158+
```rust
159+
use serde::{Deserialize, Serialize};
160+
161+
// The JSON data has keys 'userName' and 'isVerified'
162+
// but our Rust struct uses snake_case as per Rust conventions.
163+
#[derive(Debug, Serialize, Deserialize)]
164+
struct User {
165+
#[serde(rename = "userName")] // Maps 'userName' from JSON to user_name in Rust
166+
user_name: String,
167+
168+
#[serde(rename = "isVerified")] // Maps 'isVerified' from JSON to is_verified
169+
is_verified: bool,
170+
}
171+
172+
fn main() {
173+
let json_data = r#"
174+
{
175+
"userName": "rustacean_user",
176+
"isVerified": true
177+
}
178+
"#;
179+
180+
let user: User = serde_json::from_str(json_data).unwrap();
181+
182+
println!("Deserialized User: {:?}", user);
183+
// Output: Deserialized User: User { user_name: "rustacean_user", is_verified: true }
184+
}
185+
```
186+
187+
The `#[serde(rename = "…")]` attribute tells Serde to look for the key specified in the JSON data and use its value to populate the corresponding Rust field. This allows you to maintain idiomatic Rust code while working with different external data formats.
188+
189+
---
190+
191+
Serde's power lies in these attributes, which allow you to effortlessly map complex JSON data to clean, idiomatic Rust code.

0 commit comments

Comments
 (0)