Skip to content

Commit 0cf0f12

Browse files
author
Brian Obot
committed
Add more notes on closures and implemented a version of actix-web router struct
1 parent 75a9308 commit 0cf0f12

2 files changed

Lines changed: 251 additions & 1 deletion

File tree

src/bin/41_utility_traits.rs

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// - Marker traits: traits used to expression contraits on generic types that can be caputured in any other trivial way, Size and Copy
99
// - Public Vocabulary Trait: they are not magival to the compiler but using them mirrors convetional solutions for common problems, Default, AsRed, AsMut, Borrow and BorrowMut
1010

11-
use std::fmt::Debug;
11+
use std::{borrow::Borrow, fmt::Debug};
1212

1313
fn main() {
1414
// Traits, Description
@@ -239,4 +239,42 @@ fn main() {
239239
*
240240
*/
241241
// we have TryFrom and TryInto as equivalent versions of From and Into which can fail and therefore return Result<T, Error>
242+
//
243+
println!("###########################################");
244+
struct FakeHashMap<K, V> {
245+
keys: Vec<K>,
246+
values: Vec<V>,
247+
}
248+
249+
impl<K, V> FakeHashMap<K, V> {
250+
fn new() -> Self {
251+
Self {
252+
keys: Vec::new(),
253+
values: Vec::new(),
254+
}
255+
}
256+
257+
fn insert(&mut self, key: K, value: V) {
258+
self.keys.push(key);
259+
self.values.push(value);
260+
}
261+
262+
fn get<Q>(&self, key: Q) -> Option<&V>
263+
where
264+
K: PartialEq,
265+
Q: Borrow<K> + PartialEq,
266+
{
267+
let index = self.keys.iter().position(|item| item == key.borrow());
268+
269+
match index {
270+
Some(idx) => Some(&self.values[idx]),
271+
None => None,
272+
}
273+
}
274+
}
275+
276+
let mut fmh = FakeHashMap::new();
277+
fmh.insert("String", "value");
278+
let value = fmh.get("String");
279+
println!("Value = {value:?}");
242280
}

src/bin/42_closures_indepth.rs

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
use std::{collections::HashMap, thread};
2+
3+
fn main() -> std::io::Result<()> {
4+
let mut integers = vec![4, 6, 3, 6, 3, 6, 2, 7, 3, 8, 4, 23, 56];
5+
integers.sort();
6+
7+
println!("Sorted Integers: {integers:?}");
8+
9+
#[derive(Default, Debug)]
10+
#[allow(dead_code)]
11+
struct City {
12+
name: String,
13+
capital: String,
14+
population: i64,
15+
}
16+
17+
impl City {
18+
fn get_statistic(&self, _stat: Statistic) -> i64 {
19+
10i64
20+
}
21+
}
22+
23+
#[derive(Copy, Clone)]
24+
struct Statistic {}
25+
26+
fn start_sorting_thread(
27+
mut cities: Vec<City>,
28+
stat: Statistic,
29+
) -> thread::JoinHandle<Vec<City>> {
30+
// the move infront of the closure tells the rust compiler that the closure doesn't borrow the values it uses
31+
// but it steals theme
32+
// this first closure takes ownership of stat
33+
let key_fn = move |city: &City| -> i64 { -city.get_statistic(stat) };
34+
35+
// the second closure here, takes ownership of cities and the key_fn closure
36+
thread::spawn(move || {
37+
cities.sort_by_key(key_fn);
38+
cities
39+
})
40+
}
41+
42+
let cities = vec![
43+
City {
44+
name: "Uyo".to_string(),
45+
..Default::default()
46+
},
47+
City {
48+
name: "Kano".to_string(),
49+
..Default::default()
50+
},
51+
];
52+
let statistic = Statistic {};
53+
let result = start_sorting_thread(cities, statistic).join().unwrap();
54+
55+
dbg!(result);
56+
// Rust offer 2 ways for closures to take data from the enclosing environement, moves
57+
//
58+
// functions and closures have types just like regular values too
59+
// structs may have function typed fields
60+
// a vector can store functions provided their types are the same
61+
// a function value size is just the size of the machine code since they are pointers
62+
// Note: Closures do not have the same type as functions
63+
64+
fn calculate(x: i32) -> i32 {
65+
x * 2
66+
}
67+
68+
// this is the type of the calculate function above
69+
let _func: fn(i32) -> i32 = calculate;
70+
71+
// Every Closure has a type that's only known to the compiler
72+
// and no two closures have the same type
73+
// so when working with closures, your code should be generic and filter based on the Fn(T) trait which
74+
// functions and closures implement
75+
//
76+
// closures are very very fast, they are faster than functions pointer
77+
78+
// FnOnce are closures that kill the values they take and hence must only be called onces
79+
// FnMut are closures that mutate the values they take and are not safe to be passed to threads, this do not kill (drop) the values
80+
//
81+
// Closures are represented as struct that holds references to the values the contain or the values the contain
82+
// based on whether they are borrow closures or move closures
83+
84+
// Closures can also be Copy or Moves types based on how they used the values the capture
85+
// if everything a borrow closure capture is Copy, the closure is Copy too
86+
// if everything a move closure capture is Copy, the closure is Copy too, this applies for Clone types too
87+
//
88+
let mut greeting = String::from("Hello, ");
89+
let greet = move |name| {
90+
greeting.push_str(name);
91+
println!("{}", greeting);
92+
};
93+
greet.clone()("Alfred"); // this clones the value in the greeting for each call to clone
94+
greet.clone()("Bruce");
95+
96+
// implementation of a router as seen in actix-web to show how closures can be used for callbacks
97+
#[derive(Default)]
98+
#[allow(dead_code)]
99+
struct Request {
100+
method: String,
101+
url: String,
102+
headers: HashMap<String, String>,
103+
body: Vec<u8>,
104+
}
105+
106+
#[derive(Default)]
107+
#[allow(dead_code)]
108+
struct Response {
109+
code: u32,
110+
headers: HashMap<String, String>,
111+
body: Vec<u8>,
112+
}
113+
114+
type BoxedCallback = Box<dyn Fn(&Request) -> Response>;
115+
116+
struct BasicRouter {
117+
routes: HashMap<String, BoxedCallback>,
118+
}
119+
120+
impl BasicRouter {
121+
fn new() -> Self {
122+
Self {
123+
routes: HashMap::new(),
124+
}
125+
}
126+
127+
fn add_route<C>(mut self, url: &str, callback: C) -> Self
128+
where
129+
C: Fn(&Request) -> Response + 'static,
130+
{
131+
self.routes.insert(url.to_string(), Box::new(callback));
132+
self
133+
}
134+
135+
fn not_found_response(&self) -> Response {
136+
Response::default()
137+
}
138+
139+
fn handle_request(&self, req: &Request) -> Response {
140+
match self.routes.get(&req.url) {
141+
Some(callback) => callback(req),
142+
None => self.not_found_response(),
143+
}
144+
}
145+
}
146+
147+
struct FnPointerRouter {
148+
routes: HashMap<String, fn(&Request) -> Response>,
149+
}
150+
151+
impl FnPointerRouter {
152+
fn new() -> Self {
153+
Self {
154+
routes: HashMap::new(),
155+
}
156+
}
157+
158+
fn add_route(mut self, url: &str, callback: fn(&Request) -> Response) -> Self {
159+
self.routes.insert(url.to_string(), callback);
160+
self
161+
}
162+
163+
fn not_found_response(&self) -> Response {
164+
Response::default()
165+
}
166+
167+
fn handle_request(&self, req: &Request) -> Response {
168+
match self.routes.get(&req.url) {
169+
Some(callback) => callback(req),
170+
None => self.not_found_response(),
171+
}
172+
}
173+
}
174+
175+
fn home(_req: &Request) -> Response {
176+
println!("Running Home Route Handler ✅");
177+
Response::default()
178+
}
179+
180+
fn about(_req: &Request) -> Response {
181+
println!("Running About Route Handler ✅");
182+
Response::default()
183+
}
184+
185+
let router = BasicRouter::new()
186+
.add_route("/", home)
187+
.add_route("/about", about);
188+
189+
let fn_pointer_router = FnPointerRouter::new()
190+
.add_route("/", home)
191+
.add_route("/home", home)
192+
.add_route("/about", about);
193+
194+
println!("Creating Requests");
195+
let home_request = Request {
196+
url: "/".to_string(),
197+
..Default::default()
198+
};
199+
let about_request = Request {
200+
url: "/about".to_string(),
201+
..Default::default()
202+
};
203+
204+
println!("Successfully Created Requests");
205+
let _response = router.handle_request(&home_request);
206+
let _response = router.handle_request(&about_request);
207+
208+
let _response = fn_pointer_router.handle_request(&home_request);
209+
let _response = fn_pointer_router.handle_request(&about_request);
210+
211+
Ok(())
212+
}

0 commit comments

Comments
 (0)