que nuevo modulos learn guidebook generar entre diferencia crear componentes componente book agregar module rust

module - nuevo - rust guidebook



Dividir un módulo en varios archivos. (4)

Quiero tener un módulo con varias estructuras en él, cada uno en su propio archivo. Usando un módulo Math como ejemplo:

Math/ Vector.rs Matrix.rs Complex.rs

Quiero que cada estructura esté en el mismo módulo, que usaría de mi archivo principal, así:

use Math::Vector; fn main() { // ... }

Sin embargo, el sistema de módulos de Rust (que es un poco confuso para empezar) no proporciona una forma obvia de hacerlo. Parece que solo te permite tener todo el módulo en un archivo. ¿Esto es poco rústico? Si no, ¿cómo hago esto?


El sistema de módulos de Rust es realmente increíblemente flexible y le permitirá exponer cualquier tipo de estructura que desee mientras oculta cómo se estructura su código en archivos.

Creo que la clave aquí es hacer uso del pub use , lo que le permitirá reexportar identificadores de otros módulos. Existe un precedente para esto en la caja std::io Rust, donde algunos tipos de submódulos se reexportan para su uso en std::io .

Para adaptar su ejemplo, podríamos comenzar con esta estructura de directorios:

src/ lib.rs vector.rs main.rs

Aquí está su main.rs :

extern crate math; use math::vector; fn main() { println!("{:?}", vector::VectorA::new()); println!("{:?}", vector::VectorB::new()); }

Y tu src/lib.rs :

#[crate_id = "math"]; #[crate_type = "lib"]; pub mod vector; // exports the module defined in vector.rs

Y finalmente, src/vector.rs :

// exports identifiers from private sub-modules in the current // module namespace pub use self::vector_a::VectorA; pub use self::vector_b::VectorB; mod vector_b; // private sub-module defined in vector_b.rs mod vector_a { // private sub-module defined in place #[derive(Debug)] pub struct VectorA { xs: Vec<i64>, } impl VectorA { pub fn new() -> VectorA { VectorA { xs: vec![] } } } }

Y aquí es donde ocurre la magia. Hemos definido un sub-módulo math::vector::vector_a que tiene alguna implementación de un tipo especial de vector. Pero no queremos que a los clientes de su biblioteca les importe que exista un vector_a . En su lugar, nos gustaría que esté disponible en el módulo math::vector . Esto se hace con el pub use self::vector_a::VectorA , que reexporta el identificador vector_a::VectorA en el módulo actual.

Pero preguntó cómo hacer esto para poder poner sus implementaciones especiales de vectores en diferentes archivos. Esto es lo que el mod vector_b; la línea hace Le indica al compilador Rust que busque un archivo vector_b.rs para la implementación de ese módulo. Y por supuesto, aquí está nuestro archivo src/vector_b.rs :

#[derive(Debug)] pub struct VectorB { xs: Vec<i64>, } impl VectorB { pub fn new() -> VectorB { VectorB { xs: vec![] } } }

Desde la perspectiva del cliente, el hecho de que VectorA y VectorB se definan en dos módulos diferentes en dos archivos diferentes es completamente opaco.

Si estás en el mismo directorio que main.rs , deberías poder ejecutarlo con:

rustc src/lib.rs rustc -L . main.rs ./main

En general, el capítulo "Cajas y módulos" en el libro de Rust es bastante bueno. Hay muchos ejemplos.

Finalmente, el compilador Rust también busca en los subdirectorios de forma automática. Por ejemplo, el código anterior funcionará sin cambios con esta estructura de directorios:

src/ lib.rs vector/ mod.rs vector_b.rs main.rs

Los comandos para compilar y ejecutar siguen siendo los mismos también.


Las reglas del módulo Rust son:

  1. Un archivo fuente es solo su propio módulo (excepto los archivos especiales main.rs, lib.rs y mod.rs).
  2. Un directorio es solo un componente de la ruta del módulo.
  3. El archivo mod.rs es solo el módulo del directorio.

El archivo matrix.rs 1 en el directorio math es solo el módulo math::matrix . Es fácil. Lo que ves en tu sistema de archivos también lo encuentras en tu código fuente. Esta es una correspondencia uno a uno de las rutas de archivo y las rutas de módulo 2 .

Por lo tanto, puede importar una estructura con Matrix use math::matrix::Matrix , porque la estructura está dentro del archivo matrix.rs en un directorio math. ¿No feliz? Preferirías use math::Matrix; mucho en su lugar, ¿no? Es posible. Vuelva a exportar el identificador math::matrix::Matrix en math / mod.rs con:

pub use self::math::Matrix;

Hay otro paso para que esto funcione. Rust necesita una declaración de módulo para cargar el módulo. Añadir un mod math; en main.rs. Si no lo haces, obtendrás un mensaje de error del compilador al importar de esta manera:

error: unresolved import `math::Matrix`. Maybe a missing `extern crate math`?

La pista es engañosa aquí. No hay necesidad de cajas adicionales, excepto que, por supuesto, realmente tiene la intención de escribir una biblioteca separada.

Agregue esto en la parte superior de main.rs:

mod math; pub use math::Matrix;

La declaración del módulo también es necesaria para los submódulos vector , matrix y complex , porque las math necesitan cargarlos para volver a exportarlos. Una reexportación de un identificador solo funciona si ha cargado el módulo del identificador. Esto significa que, para volver a exportar el identificador math::matrix::Matrix necesitas escribir la mod matrix; . Puedes hacer esto en math / mod.rs. Por eso crea el archivo con este contenido:

mod vector; pub use self::vector::Vector; mod matrix; pub use self::matrix::Matrix; mod complex; pub use self::complex::Complex;

Aaaand ya está hecho.

1 Los nombres de los archivos de origen generalmente comienzan con una letra minúscula en Rust. Es por eso que uso matrix.rs y no Matrix.rs.

2 Java es diferente. Usted declara la ruta con el package , también. Es redundante La ruta ya es evidente desde la ubicación del archivo fuente en el sistema de archivos. ¿Por qué repetir esta información en una declaración en la parte superior del archivo? Por supuesto, a veces es más fácil echar un vistazo rápido al código fuente en lugar de averiguar la ubicación del sistema de archivos del archivo. Puedo entender a la gente que dice que es menos confuso.


Los puristas de Rusts probablemente me llamarán hereje y odiarán esta solución, pero esto es mucho más simple: solo haga cada cosa en su propio archivo, luego use la macro " include! " En mod.rs:

include!("math/Matrix.rs"); include!("math/Vector.rs"); include!("math/Complex.rs");

De esa manera, no se agregan módulos anidados y se evitan las complicadas reglas de exportación y reescritura. Simple, eficaz, sin problemas.


Muy bien, luché con mi compilador por un tiempo y finalmente lo puse a funcionar (gracias a BurntSushi por señalar el pub use .

main.rs:

use math::Vec2; mod math; fn main() { let a = Vec2{x: 10.0, y: 10.0}; let b = Vec2{x: 20.0, y: 20.0}; }

matematicas / mod.rs:

pub use self::vector::Vec2; mod vector;

math / vector.rs

use std::num::sqrt; pub struct Vec2 { x: f64, y: f64 } impl Vec2 { pub fn len(&self) -> f64 { sqrt(self.x * self.x + self.y * self.y) } // other methods... }

Otras estructuras se podrían agregar de la misma manera. NOTA: compilado con 0.9, no master.