Quiero tener un módulo con múltiples estructuras, cada una en su propio archivo. Usando un Math
módulo como ejemplo:
Math/
Vector.rs
Matrix.rs
Complex.rs
Quiero que cada estructura esté en el mismo módulo, que usaría desde 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 hacer esto. Parece que solo le permite tener todo su módulo en un archivo. ¿Es esto poco rústico? Si no es así, ¿cómo hago esto?
foo::bar::Baz
debe definirse enfoo/bar.rs
ofoo/bar/mod.rs
.Respuestas:
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 de
pub use
, lo que le permitirá reexportar identificadores de otros módulos. Hay un precedente para esto en lastd::io
caja de Rust, donde algunos tipos de submódulos se reexportan para su uso enstd::io
.Para adaptar su ejemplo, podríamos comenzar con esta estructura de directorio:
Aquí está tu
main.rs
:Y tu
src/lib.rs
:Y finalmente
src/vector.rs
:Y aquí es donde ocurre la magia. Hemos definido un submó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 haya unvector_a
submódulo. En cambio, nos gustaría que esté disponible en elmath::vector
módulo. Esto se hace conpub use self::vector_a::VectorA
, que reexporta elvector_a::VectorA
identificador en el módulo actual.Pero preguntaste cómo hacer esto para poder poner tus implementaciones vectoriales especiales en diferentes archivos. Esto es lo que hace la
mod vector_b;
línea. Indica al compilador de Rust que busque unvector_b.rs
archivo para la implementación de ese módulo. Y efectivamente, aquí está nuestrosrc/vector_b.rs
archivo:Desde la perspectiva del cliente, el hecho de que
VectorA
yVectorB
estén definidos en dos módulos diferentes en dos archivos diferentes es completamente opaco.Si está en el mismo directorio que
main.rs
, debería poder ejecutarlo con:En general, el capítulo "Cajas y módulos" del libro de Rust es bastante bueno. Hay muchos ejemplos.
Finalmente, el compilador de Rust también busca en subdirectorios automáticamente. Por ejemplo, el código anterior funcionará sin cambios con esta estructura de directorio:
Los comandos para compilar y ejecutar siguen siendo los mismos.
fuente
math::Vec2
lugar demath::vector::Vec2
. (es decir, el mismo concepto pero un módulo más profundo.)Las reglas del módulo Rust son:
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 encontrarás en tu código fuente. Esta es una correspondencia uno a uno de rutas de archivo y rutas de módulo 2 .Entonces puede importar una estructura
Matrix
conuse math::matrix::Matrix
, porque la estructura está dentro del archivo matrix.rs en un directorio math. ¿No feliz? Preferiríasuse math::Matrix;
mucho en su lugar, ¿no? Es posible. Vuelva a exportar el identificadormath::matrix::Matrix
en math / mod.rs con:Hay otro paso para que esto funcione. Rust necesita una declaración de módulo para cargar el módulo. Agregue un
mod math;
en main.rs. Si no lo hace, recibirá un mensaje de error del compilador al importar así:La pista es engañosa aquí. No hay necesidad de cajas adicionales, excepto, por supuesto, que realmente tiene la intención de escribir una biblioteca separada.
Agregue esto en la parte superior de main.rs:
La declaración módulo también es necesaria para utilizar los submódulos
vector
,matrix
ycomplex
, debido amath
las necesidades de las cargan a re-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 identificadormath::matrix::Matrix
que necesita escribirmod matrix;
. Puede hacer esto en math / mod.rs. Por lo tanto, cree el archivo con este contenido:Aaa y ya está.
1 Los nombres de los archivos de origen suelen comenzar con una letra minúscula en Rust. Por eso uso matrix.rs y no Matrix.rs.
2 Java es diferente. También declaras el camino con
package
. Es redundante. La ruta ya es evidente desde la ubicación del archivo de origen 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 archivo en el sistema de archivos. Puedo entender a las personas que dicen que es menos confuso.fuente
Los puristas de Rusts probablemente me llamarán hereje y odiarán esta solución, pero esto es mucho más simple: simplemente haga cada cosa en su propio archivo, luego use la macro " include! " En mod.rs:
De esa manera, no obtendrá módulos anidados agregados y evitará complicadas reglas de exportación y reescritura. Simple, efectivo, sin complicaciones.
fuente
use super::*
). No puede ocultar el código de otros archivos (lo cual es importante para el uso inseguro de abstracciones seguras)Muy bien, luché con mi compilador por un tiempo y finalmente lo hice funcionar (gracias a BurntSushi por señalarlo
pub use
.main.rs:
matemáticas / mod.rs:
matemáticas / vector.rs
Se podrían agregar otras estructuras de la misma manera. NOTA: compilado con 0.9, no master.
fuente
mod math;
enmain.rs
parejas de sumain
programa con su biblioteca. Si desea que sumath
módulo sea independiente, deberá compilarlo por separado y vincularlo conextern crate math
(como se muestra en mi respuesta). En Rust 0.9, es posible que la sintaxis seaextern mod math
.Me gustaría agregar aquí cómo se incluyen los archivos Rust cuando están profundamente anidados. Tengo la siguiente estructura:
¿Cómo accedes
sink.rs
otoilet.rs
desdemain.rs
?Como han mencionado otros, Rust no tiene conocimiento de archivos. En cambio, ve todo como módulos y submódulos. Para acceder a los archivos dentro del directorio del baño, debe exportarlos o colocarlos en la parte superior. Para ello, especifique un nombre de archivo con el directorio al que desea acceder y
pub mod filename_inside_the_dir_without_rs_ext
dentro del archivo.Ejemplo.
Cree un archivo llamado
bathroom.rs
dentro delhome
directorio:Exportar los nombres de archivo:
Cree un archivo llamado
home.rs
junto amain.rs
pub mod
el archivo bathroom.rsDentro
main.rs
use
Las declaraciones también se pueden utilizar:Incluir otros módulos hermanos (archivos) dentro de los submódulos
En el caso de que desee utilizar
sink.rs
desdetoilet.rs
, puede llamar al módulo especificando las palabras claveself
osuper
.Estructura de directorio final
Terminarías con algo como esto:
La estructura anterior solo funciona con Rust 2018 en adelante. La siguiente estructura de directorios también es válida para 2018, pero así es como solía funcionar 2015.
En que
home/mod.rs
es igual que./home.rs
yhome/bathroom/mod.rs
es igual quehome/bathroom.rs
. Rust hizo este cambio porque el compilador se confundiría si incluyese un archivo con el mismo nombre que el directorio. La versión 2018 (la que se muestra primero) corrige esa estructura.Consulte este repositorio para obtener más información y este video de YouTube para obtener una explicación general.
Una última cosa ... ¡evita los guiones! Úselo en su
snake_case
lugar.Nota IMPORTANTE
Usted debe barril todos los archivos en la parte superior, incluso si los archivos profundos no son requeridos por los de nivel superior.
Esto significa que, para
sink.rs
descubrirlotoilet.rs
, deberías utilizar los métodos anteriores hasta el finalmain.rs
.En otras palabras, hacer
pub mod sink;
ouse self::sink;
en el interiortoilet.rs
se no trabajo a menos que haya expuesto a todo el camino hastamain.rs
!Por lo tanto, recuerde siempre llevar sus archivos a la cima.
fuente