Módulos en Rust
Hasta ahora, todos los ejemplos han vivido en un solo archivo main.rs. Para programas pequeños está bien, pero los proyectos reales dividen el código en partes lógicas. La herramienta de Rust para eso es el sistema de módulos: mod, pub y use.
Definir un módulo
Sección titulada «Definir un módulo»Un módulo es un bloque con nombre que agrupa código relacionado:
mod math { pub fn add(a: i32, b: i32) -> i32 { a + b }}
fn main() { let sum = math::add(2, 3); println!("{sum}");}Salida:
5Hay dos cosas en las que fijarse. La función se llama con math::add(...), usando el nombre del módulo como ruta. Y está marcada con pub: sin eso, no se podría usar desde fuera del módulo.
Todo es privado por defecto
Sección titulada «Todo es privado por defecto»Vale la pena destacarlo porque sorprende a mucha gente: el contenido de un módulo es privado por defecto. Si quitas pub del ejemplo anterior, no compila:
mod math { fn add(a: i32, b: i32) -> i32 { // sin `pub` a + b }}
fn main() { let sum = math::add(2, 3); // ❌ error: function `add` is private println!("{sum}");}El mensaje del compilador (en inglés, como todos los de rustc) dice que la función add es privada. Tienes que marcar con pub, de forma explícita, todo lo que quieras que se use desde fuera del módulo. Es a propósito: así un módulo puede esconder sus funciones auxiliares internas y mostrar solo las partes pensadas como API pública.
Módulos anidados
Sección titulada «Módulos anidados»Un módulo puede contener otros módulos. Así es como los proyectos grandes van formando una estructura en forma de árbol:
mod shapes { pub mod circle { pub fn area(radius: f64) -> f64 { 3.14159 * radius * radius } }}
fn main() { let area = shapes::circle::area(2.0); println!("{area}");}use: acortar rutas largas
Sección titulada «use: acortar rutas largas»Escribir shapes::circle::area(...) cada vez acaba cansando. use trae una ruta al ámbito actual para que puedas usarla directamente:
mod shapes { pub mod circle { pub fn area(radius: f64) -> f64 { 3.14159 * radius * radius } }}
use shapes::circle;
fn main() { let area = circle::area(2.0); // más corto que shapes::circle::area println!("{area}");}En realidad ya has usado use: es la misma palabra clave que viste en la página de HashMap (use std::collections::HashMap;).
Dividir los módulos en varios archivos
Sección titulada «Dividir los módulos en varios archivos»Los ejemplos anteriores ponen todo en un solo archivo, porque el Playground de Rust lo exige. En un proyecto real en tu propio equipo, cada módulo suele tener su propio archivo. Un proyecto con un módulo math normalmente se vería así:
src/├── main.rs└── math.rsmod math; // le dice a Rust: busca el código de este módulo en math.rs
fn main() { let sum = math::add(2, 3); println!("{sum}");}pub fn add(a: i32, b: i32) -> i32 { a + b}mod math; (con punto y coma y sin cuerpo) le indica a Rust que cargue el contenido del módulo desde math.rs, en la misma carpeta. Es el patrón que usarás en cuanto tu proyecto crezca más allá de un solo archivo.
Práctica
Sección titulada «Práctica»1. Un módulo de temperaturas
Sección titulada «1. Un módulo de temperaturas»Crea un módulo llamado convert con dos funciones públicas: c_to_f (de Celsius a Fahrenheit) y f_to_c (al revés). Desde main, imprime 100°C son 212°F y 212°F son 100°C.
Pista: las fórmulas son c * 9.0 / 5.0 + 32.0 y (f - 32.0) * 5.0 / 9.0. No olvides pub, y llama a las funciones con el nombre del módulo, así: convert::c_to_f(100.0).
Ver solución
mod convert { pub fn c_to_f(c: f64) -> f64 { c * 9.0 / 5.0 + 32.0 }
pub fn f_to_c(f: f64) -> f64 { (f - 32.0) * 5.0 / 9.0 }}
fn main() { println!("100°C son {}°F", convert::c_to_f(100.0)); println!("212°F son {}°C", convert::f_to_c(212.0));}Salida:
100°C son 212°F212°F son 100°CLas dos funciones son pub, así que main (que está fuera del módulo) puede llamarlas. Quita un pub y el compilador te dirá que esa función es privada.
2. Una función auxiliar privada
Sección titulada «2. Una función auxiliar privada»Crea un módulo anidado school::grades con una función pública letter(score: u32) -> char que devuelva 'A' para 90 o más, 'B' para 80 o más y 'C' en cualquier otro caso. Dentro de grades, escribe también una función auxiliar privada is_at_least(score: u32, min: u32) -> bool que use letter. Trae school::grades al ámbito con use e imprime la letra de 95, 85 y 70.
Pista: tanto el mod grades interior de school como fn letter necesitan pub, pero is_at_least no debe llevarlo. El código de un mismo módulo siempre puede llamar a sus funciones privadas.
Ver solución
mod school { pub mod grades { pub fn letter(score: u32) -> char { if is_at_least(score, 90) { 'A' } else if is_at_least(score, 80) { 'B' } else { 'C' } }
// privada: solo el código dentro de `grades` puede llamarla fn is_at_least(score: u32, min: u32) -> bool { score >= min } }}
use school::grades;
fn main() { for score in [95, 85, 70] { println!("{score} -> {}", grades::letter(score)); }}Salida:
95 -> A85 -> B70 -> Cletter puede llamar a is_at_least porque están en el mismo módulo. Desde main, grades::is_at_least(95, 90) fallaría con “function is_at_least is private” (la función is_at_least es privada). De eso se trata: el módulo muestra una API pública pequeña y se guarda sus funciones auxiliares para sí.
Resumen
Sección titulada «Resumen»mod nombre { ... }agrupa código relacionado en un módulo con nombre.- Todo lo que hay dentro de un módulo es privado por defecto: añade
pubpara que se pueda usar desde fuera. usetrae una ruta al ámbito para que no tengas que escribirla completa cada vez.- En un proyecto con varios archivos,
mod nombre;(sin cuerpo) carga el código del módulo desde el archivo con ese nombre. - El código de un módulo siempre puede usar los elementos privados de ese módulo, así que deja privadas las funciones auxiliares y expón solo lo que necesitan quienes lo usan.
Comprueba lo que aprendiste
Sección titulada «Comprueba lo que aprendiste»Comprueba lo aprendido
1. Por defecto, ¿los elementos dentro de un mod son públicos o privados?
2. ¿Cómo le dices a Rust que cargue el código de un módulo desde math.rs en lugar de escribirlo en el mismo archivo?
Puntuación: 0 / 2
Para leer más
Sección titulada «Para leer más»- Managing Growing Projects with Packages, Crates, and Modules — The Rust Book (en inglés): el capítulo oficial sobre paquetes, crates y módulos, y cómo encajan entre sí.
- Defining Modules to Control Scope and Privacy — The Rust Book (en inglés): un ejemplo completo de un árbol de módulos, con un práctico resumen de las reglas.
- Separating Modules into Different Files — The Rust Book (en inglés): la organización en varios archivos con más detalle, incluidas las carpetas para módulos anidados.
- Modules — Rust by Example (en inglés): ejemplos ejecutables de visibilidad,
use,superyself. - Visibility and privacy — The Rust Reference (en inglés): las reglas exactas, incluido
pub(crate)para “público solo dentro de mi crate”.