La gestione degli errori è un aspetto fondamentale quando si crea un programma o una funzione. Può succedere qualsiasi cosa di inaspettato: un file che non esiste o un puntamento di memoria dove non c’è nessun valore.
- In genere dobbiamo prevedere cosa potrebbe succedere e dire al programma cosa fare.
Alcuni tipi di errore non sono gravi mentre altri possono compromettere il funzionamento del programma.
Panic
- Quando il programma trova un errore e non ha alternative richiamerà il macro panic!(). Mostrerà nella console un messaggio di errore e ci indicherà il punto del codice dove è successo. Ci darà anche una lista di azioni compiute prima e dopo la chiamata alla funzione principale.
- Possiamo anche chiamare noi la macro e indicare un messaggio da mostrare. Possiamo anche usare il metodo expect()
fn main() {
panic!("This is a panic");
}
Console:
thread 'main' panicked at examples/panic.rs:2:5:
This is a panic
stack backtrace:
0: rust_begin_unwind
at /rustc/90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf/library/std/src/panicking.rs:665:5
1: core::panicking::panic_fmt
at /rustc/90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf/library/core/src/panicking.rs:74:14
2: panic::main
at ./examples/panic.rs:2:5
3: core::ops::function::FnOnce::call_once
at /home/marco/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
- Come impostazione predefinita, quando il programma trova un errore e va in panico, pulisce tutto i dati conservati nello stack gestiti dal programma.
- Tuttavia, potremmo volere avere il programma più leggero possibile dopo la compilazione e rimuovere le istruzioni di pulizia. Per farlo dobbiamo inserire la giusta informazione su Cargo.toml.
[package]
name = "rust-tutorial"
version = "0.1.0"
edition = "2021"
[dependencies]
[profile.release]
panic = 'abort'
Result
- Possiamo tornare l’enum Result per gestire gli errori. Questo enumeratore ha due valori: Ok() e Err().
- Funziona in modo simile a Option<> ma è più indicato per gestire gli errori e permette anche di scrivere codice breve e conciso.
- Possiamo inserire al suo interno sia errori reversibili che irreversibili.
use std::{fs::File, io::{self, Read}};
fn main() {
let content = read_content_from_file("hello.txt").unwrap(); //Ottieni panic se c'è un errore
println!("{}",content)
}
fn read_content_from_file(path: &str) -> Result<String, io::Error> {
let mut file_result = File::open(path)?;
let mut file = match file_result {
Ok(file) => file,
Err(e) => return Err(e),
};
let mut content = String::new();
match file.read_to_string(&mut content) {
Ok(_) => Ok(content),
Err(e) => Err(e),
}
}
use std::{fs::File, io::{self, Read}};
fn main() {
let content = read_content_from_file("hello.txt").unwrap_or_default(); //Il programma continua senza panic
println!("{}",content)
}
fn read_content_from_file(path: &str) -> Result<String, io::Error> {
let mut file = File::open(path)?;
let mut content = String::new();
//Reads all bytes until EOF in this source, appending them to buf.
file.read_to_string(&mut content)?;
Ok(content)
}
use std::{fs::File, io::{self, Read}};
fn main() {
let content = read_content_from_file("hello.txt").unwrap_or_default();
println!("{}",content)
}
fn read_content_from_file(path: &str) -> Result<String, io::Error> {
let mut content = String::new();
//Reads all bytes until EOF in this source, appending them to buf.
File::open(path)?.read_to_string(&mut content)?;
Ok(content)
}
use std::{fs, io};
fn main() {
let content = read_content_from_file("hello.txt").unwrap_or_default();
println!("{}",content)
}
fn read_content_from_file(path: &str) -> Result<String, io::Error> {
//Reads the entire contents of a file into a string.
//
// This is a convenience function for using File::open and [read_to_string]
// with fewer imports and without an intermediate variable.
fs::read_to_string(path)
}
Codice finale:
use std::{fs, io};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let content = read_content_from_file("hello.txt")?;
Ok(println!("{}",content))
}
fn read_content_from_file(path: &str) -> Result<String, io::Error> {
//Reads the entire contents of a file into a string.
//
// This is a convenience function for using File::open and [read_to_string]
// with fewer imports and without an intermediate variable.
fs::read_to_string(path)
}
Riassumendo:
- In caso di operazioni che possono fallire o per segnalare errori di inupt e output ritorniamo Result<String, io::Error>
- In caso in cui il risultato è facoltativo e non è richiesto un errore (es. formule matematiche che possono ritornare None), possiamo ritornare un Option<T>.
- Per le funzioni principali che gestiscono vari tipi di errori ritorniamo Result<(), Box <dyn Error>>