hacktricks/todo/rust-basics.md

612 lines
20 KiB
Markdown
Raw Normal View History

2024-02-10 13:03:23 +00:00
# Fondamenti di Rust
2022-06-26 16:12:47 +00:00
2024-02-10 13:03:23 +00:00
### Tipi generici
2022-06-26 16:12:47 +00:00
2024-02-10 13:03:23 +00:00
Crea una struttura in cui uno dei suoi valori può essere di qualsiasi tipo.
2022-06-26 16:12:47 +00:00
```rust
struct Wrapper<T> {
2024-02-10 13:03:23 +00:00
value: T,
2022-06-26 16:12:47 +00:00
}
impl<T> Wrapper<T> {
2024-02-10 13:03:23 +00:00
pub fn new(value: T) -> Self {
Wrapper { value }
}
2022-06-26 16:12:47 +00:00
}
Wrapper::new(42).value
Wrapper::new("Foo").value, "Foo"
```
### Option, Some & None
2024-02-10 13:03:23 +00:00
Il tipo Option significa che il valore potrebbe essere di tipo Some (c'è qualcosa) o None:
2022-06-26 16:12:47 +00:00
```rust
pub enum Option<T> {
2024-02-10 13:03:23 +00:00
None,
Some(T),
2022-06-26 16:12:47 +00:00
}
```
2024-02-10 13:03:23 +00:00
Puoi utilizzare le funzioni come `is_some()` o `is_none()` per verificare il valore dell'Opzione.
2022-06-26 16:12:47 +00:00
2024-02-10 13:03:23 +00:00
### Macro
2022-06-27 08:23:29 +00:00
2024-02-10 13:03:23 +00:00
Le macro sono più potenti delle funzioni perché si espandono per produrre più codice rispetto a quello che hai scritto manualmente. Ad esempio, una firma di funzione deve dichiarare il numero e il tipo di parametri che la funzione ha. Le macro, d'altra parte, possono prendere un numero variabile di parametri: possiamo chiamare `println!("ciao")` con un argomento o `println!("ciao {}", nome)` con due argomenti. Inoltre, le macro vengono espandete prima che il compilatore interpreti il significato del codice, quindi una macro può, ad esempio, implementare un trait su un determinato tipo. Una funzione non può farlo, perché viene chiamata a runtime e un trait deve essere implementato a compile time.
2022-06-27 08:23:29 +00:00
```rust
macro_rules! my_macro {
2024-02-10 13:03:23 +00:00
() => {
println!("Check out my macro!");
};
($val:expr) => {
println!("Look at this other macro: {}", $val);
}
2022-06-27 08:23:29 +00:00
}
fn main() {
2024-02-10 13:03:23 +00:00
my_macro!();
my_macro!(7777);
2022-06-27 08:23:29 +00:00
}
// Export a macro from a module
mod macros {
2024-02-10 13:03:23 +00:00
#[macro_export]
macro_rules! my_macro {
() => {
println!("Check out my macro!");
};
}
}
```
### Iterare
L'iterazione è un concetto fondamentale nella programmazione. Consiste nel ripetere un blocco di codice per un certo numero di volte o fino a quando una determinata condizione viene soddisfatta. In Rust, ci sono diverse opzioni per implementare l'iterazione.
#### Loop
Il loop è il modo più semplice per iterare in Rust. Puoi utilizzare il costrutto `loop` per creare un ciclo infinito che può essere interrotto manualmente utilizzando l'istruzione `break`. Ad esempio:
```rust
loop {
// Blocco di codice da eseguire ripetutamente
if condition {
break; // Interrompe il ciclo
2022-06-27 08:23:29 +00:00
}
}
```
2024-02-10 13:03:23 +00:00
#### While
Il ciclo `while` viene utilizzato per eseguire un blocco di codice finché una determinata condizione è vera. Ad esempio:
```rust
while condition {
// Blocco di codice da eseguire finché la condizione è vera
}
```
#### For
Il ciclo `for` viene utilizzato per iterare su una sequenza di elementi. Può essere utilizzato con una serie di numeri, un iteratore o una collezione. Ad esempio:
```rust
for item in collection {
// Blocco di codice da eseguire per ogni elemento nella collezione
}
```
#### Iteratori
Gli iteratori sono un concetto potente in Rust che consentono di eseguire operazioni su una sequenza di elementi in modo dichiarativo. Gli iteratori possono essere combinati e concatenati per creare pipeline di elaborazione dei dati. Ad esempio:
2022-06-27 08:23:29 +00:00
2024-02-10 13:03:23 +00:00
```rust
let numbers = vec![1, 2, 3, 4, 5];
let sum: i32 = numbers.iter()
.filter(|&x| x % 2 == 0)
.map(|x| x * 2)
.sum();
```
In questo esempio, viene creato un vettore di numeri e viene calcolata la somma dei numeri pari moltiplicati per 2 utilizzando gli iteratori.
L'iterazione è un concetto fondamentale nella programmazione e comprendere le diverse opzioni disponibili in Rust ti aiuterà a scrivere codice più pulito ed efficiente.
2022-06-27 08:23:29 +00:00
```rust
// Iterate through a vector
let my_fav_fruits = vec!["banana", "raspberry"];
let mut my_iterable_fav_fruits = my_fav_fruits.iter();
assert_eq!(my_iterable_fav_fruits.next(), Some(&"banana"));
assert_eq!(my_iterable_fav_fruits.next(), Some(&"raspberry"));
assert_eq!(my_iterable_fav_fruits.next(), None); // When it's over, it's none
2024-02-10 13:03:23 +00:00
2022-06-27 08:23:29 +00:00
// One line iteration with action
my_fav_fruits.iter().map(|x| capitalize_first(x)).collect()
// Hashmap iteration
for (key, hashvalue) in &*map {
for key in map.keys() {
for value in map.values() {
```
2024-02-10 13:03:23 +00:00
### Box Ricorsivo
A recursive box is a data structure in Rust that allows for the creation of self-referential types. It is similar to a regular box, but with the ability to contain a value that refers to itself.
2022-06-27 08:23:29 +00:00
2024-02-10 13:03:23 +00:00
To create a recursive box, you can use the `Rc` (reference counting) or `Arc` (atomic reference counting) types provided by the `std::rc` module. These types allow multiple ownership of a value and keep track of the number of references to that value.
Here is an example of how to create a recursive box using `Rc`:
2022-06-27 08:23:29 +00:00
2024-02-10 13:03:23 +00:00
```rust
use std::rc::Rc;
struct Node {
value: i32,
next: Option<Rc<Node>>,
}
fn main() {
let node1 = Rc::new(Node {
value: 1,
next: None,
});
let node2 = Rc::new(Node {
value: 2,
next: Some(Rc::clone(&node1)),
});
// Make node1 refer to node2
node1.next = Some(Rc::clone(&node2));
// Access the value of node1
println!("Value of node1: {}", node1.value);
// Access the value of node2 through node1
if let Some(node2) = node1.next {
println!("Value of node2: {}", node2.value);
}
}
```
In this example, we define a `Node` struct that contains a value of type `i32` and an optional `Rc<Node>` that represents the next node in the linked list. By using `Rc`, we can create a circular reference between `node1` and `node2`, allowing us to traverse the linked list indefinitely.
Note that `Rc` is not thread-safe, so if you need to share the recursive box across multiple threads, you should use `Arc` instead.
2022-06-27 08:23:29 +00:00
```rust
enum List {
2024-02-10 13:03:23 +00:00
Cons(i32, List),
Nil,
2022-06-27 08:23:29 +00:00
}
let list = Cons(1, Cons(2, Cons(3, Nil)));
```
2024-02-10 13:03:23 +00:00
### Condizioni
2022-06-26 16:12:47 +00:00
#### if
```rust
let n = 5;
if n < 0 {
2024-02-10 13:03:23 +00:00
print!("{} is negative", n);
2022-06-26 16:12:47 +00:00
} else if n > 0 {
2024-02-10 13:03:23 +00:00
print!("{} is positive", n);
2022-06-26 16:12:47 +00:00
} else {
2024-02-10 13:03:23 +00:00
print!("{} is zero", n);
}
```
#### corrispondenza
La parola chiave `match` in Rust viene utilizzata per eseguire il controllo dei modelli. Consente di confrontare un valore con una serie di modelli e di eseguire il codice corrispondente al modello che viene trovato. La sintassi di base è la seguente:
```rust
match valore {
modello1 => {
// codice da eseguire se il valore corrisponde al modello1
},
modello2 => {
// codice da eseguire se il valore corrisponde al modello2
},
// altri modelli e codice corrispondente
2022-06-26 16:12:47 +00:00
}
```
2024-02-10 13:03:23 +00:00
È possibile utilizzare il modello `_` per gestire tutti i casi non corrispondenti ai modelli specificati. Ad esempio:
2022-06-26 16:12:47 +00:00
2024-02-10 13:03:23 +00:00
```rust
match valore {
1 => {
println!("Il valore è 1");
},
2 => {
println!("Il valore è 2");
},
_ => {
println!("Il valore non è né 1 né 2");
}
}
```
Il blocco di codice corrispondente al modello che viene trovato viene eseguito e l'esecuzione del `match` termina. Se nessun modello corrisponde al valore, il codice non viene eseguito.
2022-06-26 16:12:47 +00:00
```rust
match number {
2024-02-10 13:03:23 +00:00
// Match a single value
1 => println!("One!"),
// Match several values
2 | 3 | 5 | 7 | 11 => println!("This is a prime"),
// TODO ^ Try adding 13 to the list of prime values
// Match an inclusive range
13..=19 => println!("A teen"),
// Handle the rest of cases
_ => println!("Ain't special"),
2022-06-26 16:12:47 +00:00
}
let boolean = true;
// Match is an expression too
let binary = match boolean {
2024-02-10 13:03:23 +00:00
// The arms of a match must cover all the possible values
false => 0,
true => 1,
// TODO ^ Try commenting out one of these arms
2022-06-26 16:12:47 +00:00
};
```
2024-02-10 13:03:23 +00:00
#### loop (infinito)
2022-06-26 16:12:47 +00:00
2024-02-10 13:03:23 +00:00
The `loop` keyword in Rust is used to create an infinite loop. The code inside the loop will continue to execute indefinitely until a break statement is encountered.
2022-06-26 16:12:47 +00:00
```rust
loop {
2024-02-10 13:03:23 +00:00
// code to be executed repeatedly
// until a break statement is encountered
2022-06-26 16:12:47 +00:00
}
```
2024-02-10 13:03:23 +00:00
Il termine `loop` in Rust viene utilizzato per creare un ciclo infinito. Il codice all'interno del ciclo continuerà ad eseguire indefinitamente fino a quando non viene incontrata un'istruzione di interruzione.
```rust
loop {
// codice da eseguire ripetutamente
// fino a quando non viene incontrata un'istruzione di interruzione
}
```
```rust
loop {
count += 1;
if count == 3 {
println!("three");
continue;
}
println!("{}", count);
if count == 5 {
println!("OK, that's enough");
break;
}
}
```
2022-06-26 16:12:47 +00:00
#### while
2024-02-10 13:03:23 +00:00
Il costrutto `while` in Rust viene utilizzato per eseguire un blocco di codice ripetutamente fintanto che una determinata condizione è vera. La sintassi del costrutto `while` è la seguente:
2022-06-26 16:12:47 +00:00
```rust
2024-02-10 13:03:23 +00:00
while condizione {
// blocco di codice da eseguire
2022-06-26 16:12:47 +00:00
}
```
2024-02-10 13:03:23 +00:00
La condizione viene valutata all'inizio di ogni iterazione. Se la condizione è vera, il blocco di codice viene eseguito. Dopo l'esecuzione del blocco di codice, la condizione viene nuovamente valutata. Se la condizione è ancora vera, il blocco di codice viene eseguito di nuovo. Questo processo continua finché la condizione diventa falsa.
Ecco un esempio di utilizzo del costrutto `while` in Rust:
```rust
let mut count = 0;
while count < 5 {
println!("Il valore di count è: {}", count);
count += 1;
}
```
2022-06-26 16:12:47 +00:00
2024-02-10 13:03:23 +00:00
In questo esempio, il blocco di codice all'interno del costrutto `while` viene eseguito finché il valore di `count` è inferiore a 5. Ad ogni iterazione, viene stampato il valore di `count` e viene incrementato di 1.
```rust
let mut n = 1;
while n < 101 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
n += 1;
}
```
#### per
2022-06-26 16:12:47 +00:00
```rust
for n in 1..101 {
2024-02-10 13:03:23 +00:00
if n % 15 == 0 {
println!("fizzbuzz");
} else {
println!("{}", n);
}
2022-06-26 16:12:47 +00:00
}
// Use "..=" to make inclusive both ends
for n in 1..=100 {
2024-02-10 13:03:23 +00:00
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 3 == 0 {
println!("fizz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
2022-06-26 16:12:47 +00:00
}
// ITERATIONS
let names = vec!["Bob", "Frank", "Ferris"];
//iter - Doesn't consume the collection
for name in names.iter() {
2024-02-10 13:03:23 +00:00
match name {
&"Ferris" => println!("There is a rustacean among us!"),
_ => println!("Hello {}", name),
}
2022-06-26 16:12:47 +00:00
}
//into_iter - COnsumes the collection
for name in names.into_iter() {
2024-02-10 13:03:23 +00:00
match name {
"Ferris" => println!("There is a rustacean among us!"),
_ => println!("Hello {}", name),
}
2022-06-26 16:12:47 +00:00
}
//iter_mut - This mutably borrows each element of the collection
for name in names.iter_mut() {
2024-02-10 13:03:23 +00:00
*name = match name {
&mut "Ferris" => "There is a rustacean among us!",
_ => "Hello",
}
2022-06-26 16:12:47 +00:00
}
```
#### if let
2024-02-10 13:03:23 +00:00
La sintassi `if let` in Rust è un modo conciso per gestire il pattern matching in una singola istruzione. È utile quando si desidera eseguire un'azione specifica solo se un valore soddisfa un determinato pattern.
La sintassi di base è la seguente:
```rust
if let pattern = expression {
// codice da eseguire se il pattern corrisponde all'espressione
} else {
// codice da eseguire se il pattern non corrisponde all'espressione
}
```
In pratica, `if let` controlla se l'espressione corrisponde al pattern specificato. Se corrisponde, il codice all'interno del blocco `if` viene eseguito. In caso contrario, il codice all'interno del blocco `else` viene eseguito.
Ecco un esempio di utilizzo di `if let`:
```rust
let value = Some(5);
if let Some(x) = value {
println!("Il valore è {}", x);
} else {
println!("Il valore non è presente");
}
```
In questo esempio, `value` è un'opzione che contiene il valore `5`. Utilizzando `if let`, controlliamo se `value` corrisponde al pattern `Some(x)`. Se corrisponde, il valore `x` viene estratto e stampato a schermo. Altrimenti, viene stampato un messaggio che indica che il valore non è presente.
È possibile utilizzare `if let` anche con altri tipi di pattern, come ad esempio `Some(x)`, `None`, `Ok(x)`, `Err(x)`, ecc. Questa sintassi è particolarmente utile quando si lavora con opzioni, risultati o altri tipi di dati che possono avere valori o errori.
2022-06-26 16:12:47 +00:00
```rust
let optional_word = Some(String::from("rustlings"));
if let word = optional_word {
2024-02-10 13:03:23 +00:00
println!("The word is: {}", word);
2022-06-26 16:12:47 +00:00
} else {
2024-02-10 13:03:23 +00:00
println!("The optional word doesn't contain anything");
2022-06-26 16:12:47 +00:00
}
```
#### while let
2024-02-10 13:03:23 +00:00
Il costrutto `while let` in Rust è un modo conciso per iterare su un'opzione fino a quando non diventa `None`. Questo costrutto è utile quando si desidera eseguire un blocco di codice solo se l'opzione contiene un valore.
Ecco un esempio di come utilizzare `while let`:
```rust
let mut optional_number = Some(5);
while let Some(number) = optional_number {
println!("Il numero è: {}", number);
optional_number = None;
}
```
In questo esempio, abbiamo un'opzione `optional_number` che inizialmente contiene il valore `Some(5)`. Utilizzando `while let`, iteriamo sull'opzione finché contiene un valore. All'interno del blocco `while let`, stampiamo il numero e quindi assegnamo `None` all'opzione per terminare il ciclo.
L'output di questo codice sarà:
```
Il numero è: 5
```
Quando l'opzione diventa `None`, il ciclo termina.
2022-06-26 16:12:47 +00:00
```rust
let mut optional = Some(0);
// This reads: "while `let` destructures `optional` into
// `Some(i)`, evaluate the block (`{}`). Else `break`.
while let Some(i) = optional {
2024-02-10 13:03:23 +00:00
if i > 9 {
println!("Greater than 9, quit!");
optional = None;
} else {
println!("`i` is `{:?}`. Try again.", i);
optional = Some(i + 1);
}
// ^ Less rightward drift and doesn't require
// explicitly handling the failing case.
2022-06-26 16:12:47 +00:00
}
```
### Traits
2024-02-10 13:03:23 +00:00
Creare un nuovo metodo per un tipo
2022-06-26 16:12:47 +00:00
2024-02-10 13:03:23 +00:00
```rust
trait MyTrait {
fn my_method(&self);
}
struct MyStruct;
impl MyTrait for MyStruct {
fn my_method(&self) {
println!("Hello from my_method!");
}
}
fn main() {
let my_struct = MyStruct;
my_struct.my_method();
}
```
Il codice sopra definisce un trait chiamato `MyTrait` che ha un metodo chiamato `my_method`. Successivamente, viene definita una struttura chiamata `MyStruct` e viene implementato il trait `MyTrait` per questa struttura. L'implementazione del metodo `my_method` stampa "Hello from my_method!". Infine, nel metodo `main`, viene creato un'istanza di `MyStruct` chiamata `my_struct` e viene chiamato il metodo `my_method` su di essa.
2022-06-26 16:12:47 +00:00
```rust
trait AppendBar {
2024-02-10 13:03:23 +00:00
fn append_bar(self) -> Self;
2022-06-26 16:12:47 +00:00
}
impl AppendBar for String {
2024-02-10 13:03:23 +00:00
fn append_bar(self) -> Self{
format!("{}Bar", self)
}
2022-06-26 16:12:47 +00:00
}
let s = String::from("Foo");
let s = s.append_bar();
println!("s: {}", s);
```
2024-02-10 13:03:23 +00:00
### Test
I test sono una parte essenziale dello sviluppo del software. I test consentono di verificare che il codice funzioni correttamente e che soddisfi i requisiti specificati. Ci sono diversi tipi di test che possono essere eseguiti durante il processo di sviluppo del software.
#### Test di unità
I test di unità sono utilizzati per verificare il corretto funzionamento di singole unità di codice, come funzioni o metodi. Questi test sono solitamente scritti dagli sviluppatori stessi e possono essere eseguiti in modo automatico.
#### Test di integrazione
I test di integrazione sono utilizzati per verificare che le diverse unità di codice funzionino correttamente quando vengono combinate insieme. Questi test sono solitamente scritti dagli sviluppatori o dagli ingegneri di test e possono essere eseguiti in modo automatico o manuale.
#### Test di sistema
I test di sistema sono utilizzati per verificare che l'intero sistema funzioni correttamente. Questi test sono solitamente scritti dagli ingegneri di test e possono essere eseguiti in modo automatico o manuale.
#### Test di accettazione
I test di accettazione sono utilizzati per verificare che il sistema soddisfi i requisiti specificati dal cliente o dagli utenti finali. Questi test sono solitamente scritti dagli ingegneri di test e possono essere eseguiti in modo automatico o manuale.
#### Test di regressione
I test di regressione sono utilizzati per verificare che le modifiche apportate al codice non abbiano introdotto nuovi bug o rotto funzionalità esistenti. Questi test sono solitamente scritti dagli ingegneri di test e possono essere eseguiti in modo automatico o manuale.
#### Test di carico
I test di carico sono utilizzati per verificare come il sistema si comporta sotto carichi di lavoro elevati. Questi test sono solitamente scritti dagli ingegneri di test e possono essere eseguiti in modo automatico o manuale.
#### Test di sicurezza
I test di sicurezza sono utilizzati per verificare la sicurezza del sistema e identificare eventuali vulnerabilità o falle di sicurezza. Questi test sono solitamente eseguiti da specialisti in sicurezza informatica o hacker etici.
#### Test di performance
I test di performance sono utilizzati per verificare le prestazioni del sistema, come la velocità di risposta e la scalabilità. Questi test sono solitamente scritti dagli ingegneri di test e possono essere eseguiti in modo automatico o manuale.
#### Test di stress
I test di stress sono utilizzati per verificare come il sistema si comporta in situazioni di stress estreme, come carichi di lavoro molto elevati o risorse limitate. Questi test sono solitamente scritti dagli ingegneri di test e possono essere eseguiti in modo automatico o manuale.
#### Test di usabilità
I test di usabilità sono utilizzati per verificare la facilità d'uso del sistema da parte degli utenti finali. Questi test sono solitamente eseguiti da specialisti in usabilità o dagli ingegneri di test.
#### Test di compatibilità
I test di compatibilità sono utilizzati per verificare che il sistema funzioni correttamente su diverse piattaforme, browser o dispositivi. Questi test sono solitamente scritti dagli ingegneri di test e possono essere eseguiti in modo automatico o manuale.
#### Test di localizzazione
I test di localizzazione sono utilizzati per verificare che il sistema funzioni correttamente in diverse lingue o regioni. Questi test sono solitamente eseguiti da specialisti in localizzazione o dagli ingegneri di test.
#### Test di automazione
2022-06-26 16:12:47 +00:00
2024-02-10 13:03:23 +00:00
I test di automazione sono utilizzati per automatizzare l'esecuzione dei test, riducendo così il tempo e lo sforzo necessari per eseguirli manualmente. Questi test sono solitamente scritti dagli ingegneri di test e possono essere eseguiti in modo automatico.
2022-06-26 16:12:47 +00:00
2024-02-10 13:03:23 +00:00
#### Test di copertura
I test di copertura sono utilizzati per verificare quanto del codice è stato testato. Questi test sono solitamente eseguiti dagli strumenti di test e possono essere eseguiti in modo automatico.
#### Test di robustezza
I test di robustezza sono utilizzati per verificare come il sistema si comporta in presenza di input non validi o inaspettati. Questi test sono solitamente scritti dagli ingegneri di test e possono essere eseguiti in modo automatico o manuale.
#### Test di manutenibilità
I test di manutenibilità sono utilizzati per verificare quanto sia facile mantenere e modificare il codice. Questi test sono solitamente eseguiti dagli strumenti di test e possono essere eseguiti in modo automatico.
#### Test di ripristino
I test di ripristino sono utilizzati per verificare che il sistema possa essere ripristinato correttamente dopo un guasto o un'interruzione. Questi test sono solitamente scritti dagli ingegneri di test e possono essere eseguiti in modo automatico o manuale.
#### Test di regressione visiva
I test di regressione visiva sono utilizzati per verificare che le modifiche apportate al sistema non abbiano alterato l'aspetto visivo. Questi test sono solitamente eseguiti dagli ingegneri di test e possono essere eseguiti in modo automatico o manuale.
#### Test di accessibilità
I test di accessibilità sono utilizzati per verificare che il sistema sia accessibile a persone con disabilità o esigenze speciali. Questi test sono solitamente eseguiti da specialisti in accessibilità o dagli ingegneri di test.
#### Test di uscita
I test di uscita sono utilizzati per verificare che il sistema sia pronto per essere rilasciato in produzione. Questi test sono solitamente scritti dagli ingegneri di test e possono essere eseguiti in modo automatico o manuale.
2022-06-26 16:12:47 +00:00
```rust
#[cfg(test)]
mod tests {
2024-02-10 13:03:23 +00:00
#[test]
fn you_can_assert() {
assert!(true);
assert_eq!(true, true);
assert_ne!(true, false);
}
2022-06-27 08:23:29 +00:00
}
```
### Threading
#### Arc
2024-02-10 13:03:23 +00:00
Un Arc può utilizzare Clone per creare ulteriori riferimenti sull'oggetto da passare ai thread. Quando l'ultimo riferimento puntato a un valore esce dallo scope, la variabile viene eliminata.
2022-06-27 08:23:29 +00:00
```rust
use std::sync::Arc;
let apple = Arc::new("the same apple");
for _ in 0..10 {
2024-02-10 13:03:23 +00:00
let apple = Arc::clone(&apple);
thread::spawn(move || {
println!("{:?}", apple);
});
2022-06-27 08:23:29 +00:00
}
```
#### Threads
2024-02-10 13:03:23 +00:00
In questo caso passeremo al thread una variabile che sarà in grado di modificare.
2022-06-27 08:23:29 +00:00
```rust
fn main() {
2024-02-10 13:03:23 +00:00
let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));
let status_shared = Arc::clone(&status);
thread::spawn(move || {
for _ in 0..10 {
thread::sleep(Duration::from_millis(250));
let mut status = status_shared.lock().unwrap();
status.jobs_completed += 1;
}
});
while status.lock().unwrap().jobs_completed < 10 {
println!("waiting... ");
thread::sleep(Duration::from_millis(500));
}
2022-06-26 16:12:47 +00:00
}
```
2024-02-10 13:03:23 +00:00