Rust module system
- Paquetes: Una característica de cargo que te permite compilar, testear y compartir crates.
Crates
: El árbol de módulos que crea una librería o ejecutable.- Módulos y
use
: Te permite controlar la organización, scope, privacidad en las direcciones, etc. - Direcciones: Una forma de nombrar cada elemento, como un
struct
, función, módulo, etc.
Crates y Paquetes
Un crate
(caja) es solo una librería o un ejecutable.
El crate root
es el archivo de código fuente base, el que se compila por
defecto, o el principal, digamos.
Un paquete es el conjunto de crates que tienen cierta funcionalidad. Existen algunas normas sobre estos:
- Debe tener al menos un crate (paquete vacío?).
- Puede tener entre 0 y 1 (inclusive) crates de librería.
- Puede tener los crates binarios que sean necesarios.
Entonces:
- El crate de la librería tan solo se puede definir creando
src/lib.rs
. - El crate binario, para que sea el crate root, se define creando
src/main.rs
. - Pero, al querer crear más crates binarios (como ejemplos de uso para una
librería), debemos almacenarlos en la carpeta
src/bin
. Si no tenemos uncrate root
binario, debemos especificar qué ejecutable deseamos usar:
1cargo run --bin <nombre>
Al usar cargo new <nombre>
, estamos creando un paquete llamado <nombre>
(lo
que contenga un archivo Cargo.toml
y un directorio src
será un paquete). Si,
de base, contiene src/main.rs
será binario, o src/lib.rs
será librería.
Módulos y direcciones
Un módulo nos permite organizar mejor nuestro código dentro del propio crate. Se
definen con la palabra clave mod
:
1mod example {
2 mod other_module {
3 fn other_example() {
4 todo!();
5 }
6 }
7 fn foo_example() {
8 todo!();
9 }
10}
Module tree:
Para acceder a las diferentes partes usamos las direcciones. Estas están compuestas de los nombres del camino que se debe tomar. Hay varias formas:
- Absoluta: Empiezan desde el crate root usando
crate
o su nombre:crate::example::other_module::other_example()
- Relativa: Empiezan desde el módulo actual, podemos referirnos a este usando
self
, al módulo que lo contiene (el padre) consuper
, o un identificador del propio módulo.
Además de usar esta dirección, los módulos y las funciones deben de ser
públicas, dado que pueden ser únicamente para el funcionamiento interno del
módulo. Para cambiar su privacidad se usa pub
:
Tener que escribir estas largas direcciones para utilizar determinada dirección
puede ser engorroso, por eso tenemos la palabra clave use
:
1a::very::long::and::verbose::direction::foo();
2
3use a::very::long::and::verbose::direction;
4direction::foo();
5
6use a::very::long::and::verbose::direction::foo;
7foo();
En el caso de que el último nombre sea muy grande, o prefiramos cambiarlo en
caso de que se repitan nombres, podemos usar as
:
1a::very_long_and_verbose_direction::foo();
2
3use a::very_long_and_verbose_direction as dir;
4dir::foo();
Re-exportando nombres con pub use
Podemos también agrupar numerosos uses que tengan el mismo prefijo:
1use base::path;
2use base::path::one::path;
3use base::path::other_path;
4
5use base::path::{self, one::path, other_path};
O incluso, si queremos usar todo su contenido público (glob operator):
1use base::path::*;
Usando paquetes externos
Después de añadir un paquete a nuestro archivo Cargo.toml, y para poder usar sus
funciones, usamos use
:
Varios archivos de código fuente
Al parecer tenemos las siguientes reglas en cuanto al sistema de módulos de Rust y sus direcciones:
- Un archivo de código fuente es un módulo por sí mismo, excepto los
especiales:
main.rs
,lib.rs
ymod.rs
. - Un directorio es solo un componente de la ruta del módulo.
- El archivo
mod.rs
es el módulo de la carpeta.
De la forma que al describir los paths a nuestras funciones y demás, estas coincidirán en con la estructura de archivos.
Veamos por ejemplo el siguiente caso:
Para llamar la función foo
en other.rs
, dentro de main.rs
escribiríamos:
La palabra clave use
funciona simplemente para evitar tener que escribir toda
la dirección. En este ejemplo, dentro del archivo other, hay un módulo llamado
module
.
Hay que tener en cuenta que deberás seguir utilizando la última dirección para evitar que se confundan módulos.
Estas direcciones pueden ser tanto relativas al directorio actual, como
absolutas (usando crate
al principio de todo si se refiere al crate base).
¿Qué sucede al tener subdirectorios?
En este caso, es necesario disponer de un archivo mod.rs
que se cargará al
usar module
, ya que como veíamos antes, las carpetas son también módulos y
mod.rs
es donde se guarda su código.
En src/main.rs
:
En src/module/mod.rs
:
1pub mod other;