exercises + threads

This commit is contained in:
2025-07-23 10:21:13 +02:00
parent 266bd1e125
commit 1d2bfbac3f
8 changed files with 1874 additions and 7 deletions

1645
Cargo.lock generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,8 @@ edition = "2021"
default-run = "rust-by-example"
[dependencies]
rand = "0.9.2"
reqwest = "0.12.22"
# clap = "2.27.1" # from crates.io
# rand = { git = "https://github.com/rust-lang-nursery/rand" } # from online repo
# bar = { path = "../bar" } # from a path in the local filesystem

View File

@@ -1 +1 @@
# Rust by example
# [Rust by example](https://doc.rust-lang.org/stable/rust-by-example/)

View File

@@ -0,0 +1,66 @@
/*
*
Exercise: Implement a Multithreaded Web Crawler
#### Objective:
Create a simple multithreaded web crawler in Rust that can visit a set of web pages concurrently and extract all the links from those pages.
#### Requirements:
1. **Concurrent Fetching:** The crawler should utilize multiple threads to fetch web pages to improve performance.
2. **Link Extraction:** For each page visited by the crawler, extract all the hyperlinks (`<a href="...">`) present on the page.
3. **Avoid Cycles:** Ensure the crawler doesn't visit the same page more than once.
4. **Limit Depth:** Allow the user to specify the maximum depth for crawling (i.e., the maximum number of links to follow from the initial set of given URLs).
5. **Graceful Shutdown:** Allow the crawler to be stopped gracefully upon receiving a user interrupt (like pressing Ctrl+C).
#### Steps to Implement:
1. **Set Up the Project:**
- Create a new Rust project using Cargo.
2. **Implement URL Fetching:**
- Use `reqwest` or a similar library to fetch the contents of a web page.
- Handle networking errors gracefully.
3. **Extract Links:**
- Use the `scraper` crate or implement your own HTML parsing logic to extract links from the fetched HTML content.
4. **Multithreading:**
- Utilize Rust's `std::thread` or the `tokio` runtime to spawn threads that can concurrently fetch and process web pages.
5. **Avoid Revisits:**
- Maintain a `HashSet` or similar structure to keep track of visited URLs to prevent revisiting the same page.
6. **Depth Control:**
- Use a data structure (like a queue) to manage the list of URLs to visit, and keep track of the current depth level for each URL.
7. **Graceful Shutdown:**
- Implement signal handling to catch user interrupts and terminate the crawling process gracefully.
8. **Testing:**
- Write tests to ensure the crawler functions as expected, including edge cases like broken links, non-responsive pages, etc.
#### Hints:
- Consider using Rust's ownership model to manage shared state across threads safely.
- Make use of concurrency primitives like `Arc` and `Mutex` to safely share data between threads.
- Efficiently manage the queue of URLs to visit, making sure to handle potential deadlocks and race conditions.
This exercise will test the candidate's ability to work with network requests, concurrency, and data structures in Rust, while also evaluating their understanding of graceful error handling and software design.
*/
// #[derive(Debug)]
// struct HyperLink<'a> {
// href: &'a str,
// label: &'a str,
// }
// struct PageData<'a> {
// links: Vec<HyperLink<'a>>,
// url: &'a str,
// }
// pub fn web_crawler(url_list: Vec<&str>) -> Vec<PageData> {}
// fn get_page_links(url: &str) -> PageData {
// PageData { links: (), url }
// }
// fn get_page_body(url: &str) -> &str {}

View File

@@ -1,3 +1,48 @@
/*
* Count squares
*/
pub fn count_squares(matrix: &Vec<Vec<bool>>) -> usize {
let mut square_count: usize = 0;
for j in 0..matrix.len() {
for i in 0..matrix[0].len() {
if matrix[i][j] == true {}
}
}
square_count
}
fn does_matrix_square_at(matrix: &Vec<Vec<bool>>) -> bool {}
/*
* Mirror number
*/
pub fn mirror_number(mut value: u32) -> u32 {
const BASE: u32 = 10;
let mut mirror: u32 = 0;
let size = value.to_string().len();
for index in 1..=size {
let last = value % BASE;
value = value / BASE;
mirror += last * BASE.pow(size as u32 - index as u32);
}
mirror
}
pub fn mirror_number_lazy(value: u32) -> u32 {
value
.to_string()
.chars()
.rev()
.collect::<String>()
.parse::<u32>()
.unwrap()
}
pub fn longest_palindrome(value: &str) -> String {
let mut lp: String = "0".to_string();
let input_size = value.len() - 1;
@@ -21,6 +66,9 @@ pub fn longest_palindrome(value: &str) -> String {
lp
}
/*
* Quick sort
*/
pub fn quick_sort<T>(list: Vec<T>) -> Vec<T>
where
T: PartialOrd + Copy,

View File

@@ -1,8 +1,46 @@
use std::time::Instant;
mod easy_difficulty;
mod hard_difficulty;
mod medium_difficulty;
pub fn run_hard() {
println!();
}
pub fn run_medium() {
println!("{}", medium_difficulty::longest_palindrome("asdffdas"));
println!(
"Longest palindrome of 'asdffdas' is {}",
medium_difficulty::longest_palindrome("asdffdas")
);
let to_mirror = 12345u32;
let begin = Instant::now();
println!(
"The mirror of {} is {} and took {:?}",
to_mirror,
medium_difficulty::mirror_number(to_mirror),
begin.elapsed()
);
let begin = Instant::now();
println!(
"(lazy) The mirror of {} is {} and took {:?}",
to_mirror,
medium_difficulty::mirror_number_lazy(to_mirror),
begin.elapsed()
);
let boolean_matrix = vec![
vec![true, false, true, true],
vec![false, false, true, true],
];
println!(
"The matrix:\n{:?}\nhas {} squares inside",
boolean_matrix,
medium_difficulty::count_squares(&boolean_matrix)
);
}
pub fn run_easy() {

View File

@@ -5,8 +5,7 @@
// : /// Doc comment: generate library docs for the following item
// : //! Doc comment
mod macros;
use macros::macros_rule;
mod threads;
#[cfg(test)]
#[path = "./test/basic_test.rs"]
@@ -22,10 +21,13 @@ fn main() {
// controlflow::control_flow_module();
// traits::traits_exercise();
// str_types::str_types_module();
// exercises::run_easy();
// my_lib::public_function();
// functions::functions_module();
// concepts::box_basics();
// operators::operators_module();
macros_rule();
// macros_rule();
// exercises::run_easy();
// exercises::run_medium();
threads::threads_module();
}

68
src/threads.rs Normal file
View File

@@ -0,0 +1,68 @@
use std::{thread, time::Instant};
use rand::Rng;
const NTHREADS: u32 = 6;
fn generate_number_block(size: usize) -> String {
let mut block = String::new();
for _ in 0..size {
block.push_str(&rand::rng().random_range(0..9).to_string());
}
block
}
pub fn threads_module() {
let number_block = generate_number_block(999999);
let time = Instant::now();
let result_base = sum_of_block(&number_block);
let elapsed_base = time.elapsed();
println!("Functional took {}", elapsed_base.as_micros());
let time = Instant::now();
let result_parallel = sum_of_block_parallel(&number_block, 10);
let elapsed_parallel = time.elapsed();
println!("Parallel took {}", elapsed_parallel.as_micros());
if result_base == result_parallel {
println!(
"The results are the same ({}) and the time difference is {}",
result_base,
elapsed_parallel.as_micros() - elapsed_base.as_micros()
)
} else {
println!("One is wrong...");
}
}
fn sum_of_block_parallel(digits: &str, nthreads: usize) -> u32 {
let digits = digits.split_at(digits.len() / 2);
3
}
fn sum_of_block(digits: &str) -> u32 {
digits
.chars()
.into_iter()
.map(|character| character.to_digit(10).unwrap())
.sum::<u32>()
}
fn basic_thread_wait() {
let mut thread_list = vec![];
for i in 0..NTHREADS {
thread_list.push(thread::spawn(move || {
println!("this is the thread {}", i);
return 1;
}));
}
for th in thread_list {
let result = th.join();
match result {
Ok(r) => println!("Thread finished! {:?}", r),
Err(e) => println!("Thread error {:?}", e),
}
}
}