====== Curiosità e appunti vari sul linguaggio Rust ====== Autore: **//Fabio Di Matteo//** \\ Ultima revisione: **// 05/04/2026 19:24 //** // // Provenendo da altri linguaggi ho avuto qualche difficoltà ad abituarmi alla sintassi e buone pratiche incoraggiate dal Rust. Metto qualche appunto. Appunti veloci, piu' che altro per uso personale. Il Rust va imparato sul libro. ===== Libri ===== [[https://nixxo.github.io/rust-lang-book-it/|Libro in italiano]] [[https://play.rust-lang.org/|Playground per fare pratica]] ===== Blocco try/catch ===== In Rust il try/catch non esiste. Abbiamo i Result. Ecco alcune soluzioni al caso: ==== Esempio base ==== Creo una funzione che costruisce un errore e poi la uso in un blocco **match**: use std::io; fn divisione(a: i16, b: i16) -> Result { if b == 0 { // Creiamo un errore I/O semplice per il caso di divisione per zero return Err(io::Error::new(io::ErrorKind::Other, "divisione per zero")); } Ok(a / b) } fn main() { match divisione(16, 0) { Ok(r) => { println!("Risultato: {}", r); } Err(err) => { eprintln!("Errore: {}", err); } } println!("Questo viene dopo."); } Altro esempio. Qui non restituisco un tipo di errore come errore, ma solo una stringa. Questo puo' essere piu' giusto nella validazione dei campi inseriti. In quanto potrebbe essere fuorviante stimolare un'errore io, altro. fn maggiorenne(eta: i16) -> Result { if eta < 18 { return Err("Non sei maggiorenne".into()); } Ok(true) } fn main() { match maggiorenne(4) { Ok(m) => { println!( "Perfetto, sei maggiorenne. Dunque la funzione restituisce {}", m ); } Err(e) => { println!("Errore: {}", e); } } println!("Questo viene dopo. E fa parte del programma principale."); } **.into()** In poche parole prende un valore di un tipo e lo converte in un altro tipo, se esiste una conversione definita (tramite i trait From/Into). Qui prende &str e lo converte in String. ==== Con match su una funzione della libreria standard==== use std::fs::File; fn main() { match File::open("hello.txt") { Ok(f) => { println!("File aperto correttamente."); drop(f); } Err(err) => { eprintln!("Errore aprendo file: {}", err); return; } }; println!("Qui siamo dopo.") } ==== Con un semplice if ==== if let Ok(mut f) = File::open("a.txt") { /* successo */ } else { /* gestione fallimento */ } ==== Con is_ok/ is_err ==== let r = File::open("foo.txt"); if r.is_err() { eprintln!("Errore aprendo file: {:?}", r.err().unwrap()); } else { let mut f = r.unwrap(); // usa f } ===== Crosscompilare applicazioni ===== Alcuni comandi utili nella crosscompilazione per altre architetture. Per installare i vari pacchetti useremo **rustup** ==== Elencare i target installabili e installarne uno ==== Con il seguente comando vedremo i target installabili e quelli già installati. rustup target list E' possibile installarne uno con rustup target add Per esempio: rustup target add x86_64-pc-windows-gnu In questo esempio installo il target per le crosscompilazioni su windows con mingw64. Quindi è richiesto in questo caso l'installazione di mingw . ==== Compilare il nostro progetto per target prescelto ==== Se vogliamo compilare il nostro progetto possiamo farlo con il seguente comando: cargo build --target= come per esempio: cargo build --target=x86_64-unknown-linux-musl Quest'ultimo esempio mi genera un eseguibile statico per linux x86_64 grazie alla libreria musl. ==== Note sulla crosscompilazione per arm64 ==== E' necessario installare il compilatore gcc per arm64. Su Arch linux: sudo pacman -S aarch64-linux-gnu-gcc aarch64-linux-gnu-binutils e impostare alcune variabili d'ambiente per constringere rust a usare il linker di arm64: export CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc Crosscopilare con : cargo build --target=aarch64-unknown-linux-gnu ==== Crosscompilare staticamente con musl per aarch64? ==== impostiamo queste variabili d'ambiente: export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-linux-gnu-gcc export CC_aarch64_unknown_linux_musl=aarch64-linux-gnu-gcc e compiliamo con : cargo build --target=aarch64-unknown-linux-musl assicurandoci prima di aver installato il target in sopraindicato. ===== Le classi ===== Un esempio minimo. struct Persona { id: i16, name: String, surname: String, } impl Persona { fn new() -> Self { return Self { id: 0, name: "".into(), surname: "".into(), }; } fn set(&mut self, id: i16, name: String, surname: String) { self.id = id; self.name = name; self.surname = surname; } } fn main() { let mut p = Persona::new(); p.set(1, "Fabio".into(), "Di Matteo".into()); println!("{} {} {}", p.id, p.name, p.surname); } Versione con l'aggiunta di un metodo "getter": struct Persona { id: i16, name: String, surname: String, } impl Persona { fn new() -> Self { return Self { id: 0, name: "".into(), surname: "".into(), }; } fn set(&mut self, id: i16, name: String, surname: String) { self.id = id; self.name = name; self.surname = surname; } fn get(&self, op: String) -> String { if op == "name" { return self.name.clone(); } else if op == "surname" { return self.surname.clone(); } else { return self.id.to_string(); } } } fn main() { let mut p = Persona::new(); p.set(1, "Fabio".into(), "Di Matteo".into()); let nome = p.get("name".into()); let cognome = p.get("surname".into()); let id = p.get("id".into()); println!("{} {} {}", id, nome, cognome); } ===== Sviluppo di programmi dotatati di gui ===== Di seguito le mmie esperienze: * [[programmazione:rust:gtk4_windows|Gtk-rs su windows]] * [[programmazione:rust:gtkrs:gestione_memoria|Appunti sulla gestione della memoria in Gtkrs e Rust]] * [[https://www.freemedialab.org/code/project.php?p=video_cutter|Progetto Gtkrs completo di esempio ]] ===== I thread ===== thread::spawn() lancia direttamente il thread; let t = thread::spawn(|| { for i in 1..100 { println!("ciao numero {i} dal thread generato!"); thread::sleep(Duration::from_secs(1)); } }); Aspettare la fine del thread: t.join().unwrap(); Condivide valori tra thread tramite riferimento grazie ad Arc: use std::sync::Arc; use std::thread; let value = Arc::new(vec![1,2,3]); let v1 = Arc::clone(&value); let handle = thread::spawn(move || { println!("{:?}", v1); }); println!("{:?}", value); handle.join().unwrap(); La condivisione è sicura. Potremmo anche condividere **mpsc::channel** per usare nei vari thread il contenuto magari di un canale str o altro. ===== Funzioni async con tokio ===== Eseguire funzioni che richiedono tempo con il runtime tokio: use tokio::time::{Duration, sleep}; async fn asincrona() { sleep(Duration::from_millis(3000)).await; println!("Ciao dalla asincrona!"); } #[tokio::main] async fn main() { // Avvio una funzione async con tokio e passo il controllo // alle istruzioni successive tokio::spawn(async { asincrona().await }); println!("Scrivo dal main"); sleep(Duration::from_millis(5000)).await; } Aspettiamo che una funzione,lanciata da tokio precedentemente, finisca del lavoro "lento" per poi continuare il flusso del programma principale. use tokio::time::{Duration, sleep}; async fn asincrona() -> i32 { sleep(Duration::from_millis(6000)).await; println!("Ciao dalla asincrona!"); return 6000; } #[tokio::main] async fn main() { // Avvio una funzione async con tokio e passo il controllo // alle istruzioni successive. let r = tokio::spawn(async { asincrona().await }); // Scrivo qualcosa dal main e dopo un secondo il programma dovrebbe terminare // Invece no! Aspetto il valore di "r" proveniente dalla funzione async con // r.await println!("Scrivo dal main"); sleep(Duration::from_millis(1000)).await; // Ora che il valore di "r" è disponibile lo stampo se tutto è andato bene, // oppure stampo "0" println!( "Ho aspettato il valore di r {}", r.await.unwrap_or_else(|_| 0) ); } ===== Comunicare con le funzioni async ===== use std::sync::mpsc; use tokio::time::{Duration, sleep}; async fn asincrona(t: mpsc::Sender<&str>) { loop { t.send("Ciao dalla fn async!").unwrap(); sleep(Duration::from_millis(2000)).await; } } #[tokio::main] async fn main() { let (tx, rx) = mpsc::channel::<&str>(); let tx_clone = tx.clone(); tokio::spawn(async { asincrona(tx_clone).await }); // Un loop che riceve messaggi dalla funzione asincrona e // li stampa a video. loop { sleep(Duration::from_millis(500)).await; let val = rx.recv().unwrap_or_else(|_| "Niente."); println!("Ricevo: {}", val) } } ===== Websocket ===== Un piccolo esempio di server websocket che aspetta un pacchetto json e lo elabora. Ecco la struttura del json: {"name":"miafunzione", "params" :"param1,param2,param3"} use std::net::TcpListener; use std::thread::spawn; use tungstenite::accept; use serde::{Deserialize, Serialize}; use serde_json::Result; //use serde_json::Error; #[derive(Serialize, Deserialize, Debug)] struct Action{ name : String, params: String, } fn parse_json(json_str: &str) -> Result { serde_json::from_str(json_str) } fn handle(j: String) -> String { let a : Action; if let Err(a) = parse_json(j.as_str()) { drop(a); "0".to_string() } else { a=parse_json(j.as_str()).unwrap(); //For example return my "action" name. a.name } } fn main() { let server = TcpListener::bind("127.0.0.1:9001").unwrap(); for stream in server.incoming() { spawn(move || { let mut websocket = accept(stream.unwrap()).unwrap(); loop { if let Ok(msg) = websocket.read(){ if msg.is_binary() || msg.is_text() { if let Err(e) = websocket.send(handle(msg.to_string()).into()){ eprintln!("Error on send to client!"); drop(e); } } } } }); } } **Cargo.toml** [package] name = "pobview_backend" version = "0.1.0" edition = "2024" [dependencies] tungstenite = "0.29.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" ===== Ottenere directory eseguibile ===== use std::{env, io, path::PathBuf}; fn exe_dir() -> Result { let exe = env::current_exe()?; match exe.parent() { Some(p) => Ok(p.to_path_buf()), None => Err(io::Error::new( io::ErrorKind::Other, "no parent directory for executable", )), } } fn main() { // Utilizzarla senza controllo errori così: // let myexe_path = exe_dir(); // println!( // "Il percorso del mio eseguibile: {}", // myexe_path.unwrap().display().to_string() // ); let myexe_path = exe_dir(); match myexe_path { Ok(p) => { println!( "Il percorso del mio eseguibile è {}", p.display().to_string() ); } Err(e) => { eprintln!("Problemi nel ricavare il percorso:{} ", e) } } }