Esta traducción fue asistida por IA y revisada superficialmente. Si encuentras errores o algo no suena natural, escríbeme - lo agradezco.
Ya has buscado algo antes - en un diccionario, en una guía telefónica, en una base de datos. A veces
está. A veces no. En la mayoría de los lenguajes te encoges de hombros y sigues adelante: recibes el
valor, o recibes null, undefined o nil, y el lenguaje confía en que recuerdes cuál te tocó
antes de tocarlo. Rust no confía en que lo recuerdes. Te obliga a mirar.
El archivo
Imagina el archivo municipal - el de la fachada de piedra indiferente y un archivero que ha tramitado toda clase de peticiones sin que ninguna le haya impresionado. Presentas una solicitud para un libro. El archivero no te entrega el libro. El archivero te entrega una caja precintada.
Dentro hay una de dos cosas, y no sabrás cuál hasta abrirla: el libro en sí, o una nota que explica por qué no hay libro. Si el libro simplemente nunca se adquirió, la nota lo dice sin rodeos - no consta. Si ha pasado algo peor - el archivo ardió en el 89, la polilla se comió el registro - la nota dice eso en su lugar, y explica qué salió mal.
El archivo tiene una política, y no se dobla: lo que hay dentro no se adivina. Se abre la caja. Siempre. No importa que tengas prisa.
Rust llama Option al primer tipo de caja. Al segundo lo llama Result. Ambas están precintadas
hasta que se abren; solo se diferencian en lo que la nota puede decir. La nota de Option es
siempre una frase: no consta (None). La nota de Result tiene que explicarse (Err, con un
motivo dentro).
El hábito de otros lenguajes
En la mayoría de los otros lenguajes simplemente meterías la mano en la caja - found.title -
confiando en que hay algo razonable al otro lado. Si la búsqueda falla, te devuelve null, y o
bien el lenguaje protesta en el momento en que lo tocas, o bien te entrega un valor basura y te
enteras tres archivos más tarde. La costumbre no es tonta. Es también la razón por la que el hombre
que inventó null lo llama, con sus propias palabras, su error de mil millones de dólares.
struct Book {
id: u32,
title: String,
}
fn request_book(id: u32, archive: &[Book]) -> Option<&Book> {
archive.iter().find(|book| book.id == id)
}
fn main() {
let archive = vec![Book { id: 1, title: String::from("Reaper Man") }];
let found = request_book(2, &archive);
println!("Book: {}", found.title); // metiendo la mano en la caja sin abrirla
}
error[E0609]: no field `title` on type `Option<&Book>`
--> src/main.rs:12:32
|
12 | println!("Book: {}", found.title);
| ^^^^^ unknown field
|
= note: see the API docs for `Option<T>` for methods that unwrap or inspect its contents
Abrir la caja
El compilador no se pone difícil. Es el archivero, que no te deja salir con una caja precintada
bajo el brazo. Option<&Book> no es un Book que podría faltar - es la propia caja, y Rust no te
deja llegar al contenido sin abrirla.
Ábrela como espera el archivo: lee la nota antes de actuar según lo que diga.
match request_book(2, &archive) {
Some(book) => println!("Book: {}", book.title),
None => println!("No book with that id - not held"),
}
Result se abre igual, pero su nota lleva un motivo en vez de un encogimiento de hombros:
fn parse_priority(input: &str) -> Result<u8, String> {
input.parse().map_err(|e| format!("not a priority: {e}"))
}
match parse_priority("urgent") {
Ok(priority) => println!("Priority: {priority}"),
Err(reason) => println!("Bad priority - {reason}"), // la nota se explica sola
}
Qué cambia
Dejas de meter la mano en las cajas por fe. Cada match te obliga a responder a las dos ramas - el
libro está, o no está - y el compilador no te deja dejar una sin escribir. Ya no queda ningún camino
en el que un libro desaparecido se convierta en silencio en un puntero nulo tres archivos más allá.
Option<T>- el archivo respondiendo “¿está aquí?” (Some/None)Result<T, E>- el archivo respondiendo “¿qué ha salido mal?” (Ok/Err(reason))- Ninguna caja se abre por suposición. El compilador lo comprueba por ti, en tiempo de compilación, cada vez.