Manejo de Errores
## Objetivos de Aprendizaje
- Comprender panic vs Result
- Dominar el tipo Option
- Usar Result para errores recuperables
- Propagar errores con el operador ?
- Construir manejo de errores robusto
## Filosofia del Manejo de Errores
Rust tiene dos categorias de errores:
- **Recuperables**: Usar `Result`
- **Irrecuperables**: Usar `panic!`
## Panic
### Cuando Usar Panic
- Se violan invariantes (bug en el codigo)
- Ejemplo: intentar acceder a un indice fuera de limites
### Usando panic
```rust
fn main() {
panic!("crash and burn");
}
```
### Salida de Panic
```text
thread 'main' panicked at 'crash and burn', src/main.rs:2:5
note: run with RUST_BACKTRACE=1 for a backtrace.
```
### Backtrace
```bash
RUST_BACKTRACE=1 cargo run
```
### unwrap y expect
```rust
fn main() {
// unwrap: devuelve el valor o hace panic
let x = Some("value").unwrap();
// expect: devuelve el valor o hace panic con mensaje
let x = Some("value").expect("Expected a value");
// En None/Err
let x: Option<&str> = None;
x.unwrap(); // Panic
}
```
### unwrap en Result
```rust
fn main() {
// unwrap en Ok
let x: Result = Ok(42);
println!("{}", x.unwrap()); // 42
// unwrap en Err - panic
let x: Result = Err("error");
x.unwrap(); // Panic con "error"
// expect con mensaje
let x: Result = Err("error");
x.expect("Should be Ok"); // Panic con "Should be Ok"
}
```
## Manejo de Option
### unwrap_or
```rust
fn main() {
let x = Some("value");
assert_eq!(x.unwrap_or("default"), "value");
let x: Option<&str> = None;
assert_eq!(x.unwrap_or("default"), "default");
}
```
### unwrap_or_else
```rust
fn main() {
let x: Option = None;
// Evaluacion perezosa
let result = x.unwrap_or_else(|| {
println!("Computing default...");
42
});
}
```
### map en Option
```rust
fn main() {
let x = Some(5);
let doubled = x.map(|n| n * 2); // Some(10)
let squared = x.map(|n| n * n); // Some(25)
let x: Option = None;
let doubled = x.map(|n| n * 2); // None
}
```
### and_then (flatmap)
```rust
fn main() {
let x = Some(5);
let result = x.and_then(|n| Some(n * 2)); // Some(10)
let x: Option = None;
let result = x.and_then(|n| Some(n * 2)); // None
}
```
### or y or_else
```rust
fn main() {
let x = Some(1);
let y = Some(2);
assert_eq!(x.or(y), Some(1)); // x es Some, mantenerlo
let x: Option = None;
assert_eq!(x.or(y), Some(2)); // x es None, usar y
let x = None;
let default = x.or_else(|| Some(42)); // valor por defecto perezoso
}
```
### filter
```rust
fn main() {
let x = Some(10);
let result = x.filter(|n| n > 5); // Some(10)
let result = x.filter(|n| n > 15); // None
}
```
## Tipo Result
### Uso Basico
```rust
use std::fs::File;
fn main() {
let file = File::open("hello.txt");
match file {
Ok(_) => println!("File opened successfully"),
Err(e) => println!("Error opening file: {:?}", e),
}
}
```
### Metodos de Result
```rust
fn main() {
let x: Result = Ok(5);
x.is_ok(); // true
x.is_err(); // false
x.ok(); // Some(5)
x.err(); // None
x.unwrap_or(0); // 5
x.unwrap_or_else(|_| 0); // 5
x.map(|n| n * 2); // Ok(10)
x.map_err(|e| format!("Error: {}", e));
}
```
### Recolectando Results
```rust
fn main() {
let results: Vec> = vec![Ok(1), Ok(2), Err("error")];
// Recolectar como Vec de valores (falla si hay algun Err)
// let values: Result, &str> = results.into_iter().collect();
// Particionar en Ok y Err
let (oks, errs): (Vec<_>, Vec<_>) = results.into_iter().partition(Result::is_ok);
let values: Vec<_> = oks.into_iter().map(Result::unwrap).collect();
println!("{:?} {:?}", values, errs); // [1, 2] [Err("error")]
}
```
## El Operador ?
### Fundamentos del Operador ?
```rust
use std::fs::File;
use std::io;
fn read_file() -> Result {
let mut file = File::open("hello.txt")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents)
}
```
### ? con Option
```rust
fn first_word(s: &str) -> Option<&str> {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return Some(&s[0..i]);
}
}
None
}
fn main() {
let text = "hello world";
let first = first_word(text).ok_or("No first word")?;
println!("{}", first);
}
```
### Encadenando ?
```rust
use std::fs::File;
use std::io;
use std::io::Read;
fn read_file_contents(path: &str) -> Result {
let mut file = File::open(path)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents)
}
fn main() {
match read_file_contents("hello.txt") {
Ok(contents) => println!("{}", contents),
Err(e) => eprintln!("Error: {:?}", e),
}
}
```
### Trait From para Conversion
```rust
use std::fs::File;
use std::io;
use std::num::ParseIntError;
fn read_number() -> Result {
let s = "42";
let n: i32 = s.parse()?; // ParseIntError
Ok(n)
}
```
## Tipos de Error Personalizados
### Definir Enum de Error
```rust
use std::fmt;
enum MyError {
Io(std::io::Error),
Parse(std::num::ParseIntError),
}
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MyError::Io(e) => write!(f, "IO error: {}", e),
MyError::Parse(e) => write!(f, "Parse error: {}", e),
}
}
}
impl From for MyError {
fn from(err: std::io::Error) -> Self {
MyError::Io(err)
}
}
impl From for MyError {
fn from(err: std::num::ParseIntError) -> Self {
MyError::Parse(err)
}
}
```
### Usando Errores Personalizados
```rust
fn read_and_parse(path: &str) -> Result {
let contents = std::fs::read_to_string(path)?;
let n: i32 = contents.trim().parse()?;
Ok(n)
}
```
## Mejores Practicas
### No Usar unwrap en Codigo de Libreria
```rust
// Mal: codigo de libreria que hace panic
pub fn get(&self, key: &str) -> &str {
self.map.get(key).unwrap() // Panic si falta
}
// Bien: devolver Result
pub fn get(&self, key: &str) -> Option<&str> {
self.map.get(key)
}
```
### Usar expect para Valores Conocidos
```rust
fn main() {
// Cuando estas seguro de que el valor existe
let x = [1, 2, 3];
let first = x.first().expect("Array should not be empty");
}
```
### Propagar Errores
```rust
use std::io;
fn read_file(path: &str) -> io::Result {
let mut file = io::File::open(path)?; // Propagar error
let mut contents = String::new();
file.read_to_string(&mut contents)?; // Propagar error
Ok(contents)
}
```
## Resumen
- `panic!` para errores irrecuperables
- `unwrap()` devuelve el valor o hace panic
- `expect()` devuelve el valor o hace panic con mensaje
- `Option` para valores que pueden no existir (Some/None)
- `Result` para errores recuperables (Ok/Err)
- El operador `?` propaga errores
- `unwrap_or()` y `unwrap_or_else()` proporcionan valores por defecto
- `map()` transforma valores Option/Result
- Crear tipos de error personalizados con trait From para conversion
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →