Introducción
Un programa de línea de comandos recibe valores después del nombre de su ejecutable, escribe los resultados normales en la salida estándar e informa de los errores mediante la salida de error estándar y un estado de salida distinto de cero. Estos límites del proceso permiten que las personas y los scripts distingan entre éxito y error.
Completará un pequeño comando de búsqueda de texto. La función de búsqueda ya está preparada en el código fuente de la biblioteca del paquete, por lo que cada paso puede centrarse en analizar argumentos, cargar archivos, gestionar la ausencia de coincidencias y establecer la comunicación final del proceso.
Analizar los argumentos de la línea de comandos
En este paso, validará y extraerá una consulta de búsqueda y una ruta de archivo a partir de los argumentos del proceso.
El proyecto está en /home/labex/project/mini-search. src/main.rs contiene el binario de línea de comandos, mientras que src/lib.rs contiene la lógica de búsqueda reutilizable preparada para un paso posterior. Entre en el proyecto y abra el código fuente del binario:
cd /home/labex/project/mini-search
nano src/main.rs
env::args() produce cada argumento como un String, incluida la ruta del ejecutable en el índice cero. La función main preparada los recopila en un vector. Después pasa &args al parámetro args: &[String].
El tipo &[String] es un segmento de valores String: una vista prestada de solo lectura sobre los elementos del vector. Sigue la misma idea de préstamo que &str, pero representa una secuencia de elementos String en lugar de bytes de texto. El vector sigue siendo propiedad de main mientras run lee sus elementos.
El flujo de datos es:
shell words → env::args() → Vec<String> in main → borrowed &[String] in run
Reemplace el marcador de posición Err(...) dentro de run por:
if args.len() != 3 {
return Err(String::from("usage: mini-search <query> <file>"));
}
let query = args[1].clone();
let path = args[2].clone();
Ok(vec![format!("Query: {query}"), format!("File: {path}")])
Tres entradas exactas significan el nombre del ejecutable más dos argumentos proporcionados por el usuario. La instrucción explícita return Err(...) detiene run inmediatamente cuando esa estructura no es correcta. Se trata de un retorno anticipado, a diferencia de los retornos mediante la expresión final que utilizó en las funciones del laboratorio: el código posterior solo se ejecuta cuando el número de argumentos es válido.
Indexar el segmento prestado produce cadenas prestadas. Las dos llamadas a clone crean intencionadamente copias propias únicamente de la consulta y la ruta, para que el resto de run pueda gestionarlas como valores locales propios. Es una decisión deliberada de propiedad, no una solución general para los errores de movimiento. Después, la macro vec![first, second] crea un vector de dos elementos, y Ok devuelve temporalmente esas dos líneas de inspección.
Guarde el archivo con Ctrl+O, pulse Enter y salga con Ctrl+X. Ejecute el comando con dos argumentos:
cargo run --quiet -- rust data/notes.txt
El primer --quiet reduce los mensajes del propio Cargo. El -- independiente indica a Cargo que deje de interpretar opciones; todo lo que aparece después se pasa al programa.
Query: rust
File: data/notes.txt
Esto demuestra que los argumentos llegaron a las posiciones esperadas.
Leer el archivo e invocar la lógica de la biblioteca
En este paso, reemplazará el resultado temporal de inspección por la carga real del archivo y los resultados de búsqueda.
Abra el código fuente del binario:
nano src/main.rs
Añada estas importaciones debajo de use std::env;:
use std::fs;
use mini_search::find_lines;
El nombre del paquete mini-search se convierte en el nombre de la biblioteca de Rust mini_search. El pub ya preparado en find_lines hace que la función sea accesible fuera del crate de la biblioteca. Al importarla, el binario puede llamar a la función pública de src/lib.rs; mantener allí la lógica de búsqueda separa el procesamiento de datos reutilizable del manejo de argumentos específico del proceso. Esto es una breve introducción al modelo de biblioteca, binario y visibilidad que se enseña por completo en los módulos posteriores del laboratorio.
Reemplace la línea temporal Ok(vec![...]) por:
let contents = fs::read_to_string(&path)
.map_err(|error| format!("could not read {path}: {error}"))?;
Ok(find_lines(&query, &contents))
El error de lectura conserva la ruta solicitada y se propaga mediante ?. Cuando la lectura tiene éxito, el binario presta la consulta y el contenido del archivo, mientras que la biblioteca devuelve las líneas coincidentes como valores propios.
Guarde y salga, y después compruebe y ejecute el programa:
cargo check
cargo run --quiet -- rust data/notes.txt
Rust makes ownership explicit.
Cargo builds Rust packages.
Rust tools help beginners.
La rama Ok preparada en main imprime los resultados normales de búsqueda en la salida estándar.
Convertir una búsqueda vacía en un error útil
En este paso, distinguirá entre una búsqueda correcta con resultados y una búsqueda válida que no encontró nada.
Abra el código fuente:
nano src/main.rs
Reemplace Ok(find_lines(&query, &contents)) por:
let matches = find_lines(&query, &contents);
if matches.is_empty() {
return Err(format!("no lines matched '{query}'"));
}
Ok(matches)
Un vector vacío se puede detectar, pero el comando resulta más útil cuando explica ese resultado. Guarde y salga; después, pruebe con un término que no aparezca:
cargo run --quiet -- python data/notes.txt
no lines matched 'python'
En esta etapa intermedia, la rama Err preparada todavía escribe mediante la salida estándar y termina correctamente. En el siguiente paso, los errores recibirán el comportamiento de proceso adecuado.
Enviar los errores a stderr y salir con un estado distinto de cero
En este paso, completará el límite de la línea de comandos separando la salida normal de los errores.
La salida estándar, o stdout, transporta los resultados solicitados. La salida de error estándar, o stderr, transporta los diagnósticos de forma independiente. Un estado de salida cero significa éxito; un estado distinto de cero significa error. Abra el código fuente del binario:
nano src/main.rs
Añada esta importación debajo de las importaciones existentes de la biblioteca estándar:
use std::process;
Reemplace la rama de una sola línea Err(error) por:
Err(error) => {
eprintln!("{error}");
process::exit(1);
}
eprintln! escribe una línea en stderr. process::exit(1) finaliza inmediatamente el proceso con el estado uno. Guarde y salga; después, compile una vez para poder ejecutar el binario directamente sin que Cargo añada su propio mensaje de error:
cargo build --quiet
./target/debug/mini-search python data/notes.txt
El diagnóstico sigue siendo:
no lines matched 'python'
Imprima inmediatamente el estado del comando anterior:
echo $?
echo imprime sus argumentos y el shell expande $? al estado de salida del comando ejecutado más recientemente:
1
El mismo límite también cubre el uso no válido y los archivos inexistentes. Al ejecutar ./target/debug/mini-search se muestra el mensaje de uso; una ruta como data/missing.txt muestra el error de lectura. Ambos casos escriben únicamente en stderr y terminan con el estado uno, mientras que una búsqueda con coincidencias escribe los resultados en stdout y termina con el estado cero.
Resumen
Recopiló y validó argumentos de la línea de comandos, utilizó el separador -- de Cargo, mantuvo la lógica de búsqueda en el límite de una biblioteca preparada, cargó el archivo solicitado y completó el comportamiento convencional de stdout, stderr y los estados de salida para un caso correcto y tres casos de error.


