Traer Rutas al Alcance con la Palabra Clave use

RustRustBeginner
Practicar Ahora

This tutorial is from open-source community. Access the source code

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

Bienvenido a Bringing Paths Into Scope With the Use Keyword. Esta práctica es parte del Rust Book. Puedes practicar tus habilidades de Rust en LabEx.

En esta práctica, aprenderemos cómo traer rutas al alcance utilizando la palabra clave use para crear atajos para llamar a funciones y módulos.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL rust(("Rust")) -.-> rust/DataStructuresandEnumsGroup(["Data Structures and Enums"]) rust(("Rust")) -.-> rust/BasicConceptsGroup(["Basic Concepts"]) rust(("Rust")) -.-> rust/DataTypesGroup(["Data Types"]) rust(("Rust")) -.-> rust/FunctionsandClosuresGroup(["Functions and Closures"]) rust/BasicConceptsGroup -.-> rust/variable_declarations("Variable Declarations") rust/BasicConceptsGroup -.-> rust/mutable_variables("Mutable Variables") rust/DataTypesGroup -.-> rust/type_casting("Type Conversion and Casting") rust/FunctionsandClosuresGroup -.-> rust/function_syntax("Function Syntax") rust/FunctionsandClosuresGroup -.-> rust/expressions_statements("Expressions and Statements") rust/DataStructuresandEnumsGroup -.-> rust/method_syntax("Method Syntax") subgraph Lab Skills rust/variable_declarations -.-> lab-100404{{"Traer Rutas al Alcance con la Palabra Clave use"}} rust/mutable_variables -.-> lab-100404{{"Traer Rutas al Alcance con la Palabra Clave use"}} rust/type_casting -.-> lab-100404{{"Traer Rutas al Alcance con la Palabra Clave use"}} rust/function_syntax -.-> lab-100404{{"Traer Rutas al Alcance con la Palabra Clave use"}} rust/expressions_statements -.-> lab-100404{{"Traer Rutas al Alcance con la Palabra Clave use"}} rust/method_syntax -.-> lab-100404{{"Traer Rutas al Alcance con la Palabra Clave use"}} end

Traer rutas al alcance con la palabra clave use

Tener que escribir las rutas para llamar a funciones puede resultar incómodo y repetitivo. En la Lista 7-7, ya sea que hayamos elegido la ruta absoluta o relativa a la función add_to_waitlist, cada vez que queríamos llamar a add_to_waitlist también teníamos que especificar front_of_house y hosting. Afortunadamente, existe una forma de simplificar este proceso: podemos crear un atajo a una ruta una vez con la palabra clave use, y luego utilizar el nombre más corto en cualquier otro lugar del ámbito.

En la Lista 7-11, traemos el módulo crate::front_of_house::hosting al ámbito de la función eat_at_restaurant para que solo tengamos que especificar hosting::add_to_waitlist para llamar a la función add_to_waitlist en eat_at_restaurant.

Nombre del archivo: src/lib.rs

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}

Lista 7-11: Traer un módulo al alcance con use

Agregar use y una ruta en un ámbito es similar a crear un enlace simbólico en el sistema de archivos. Al agregar use crate::front_of_house::hosting en la raíz del crat, hosting ahora es un nombre válido en ese ámbito, al igual que si el módulo hosting hubiera sido definido en la raíz del crat. Las rutas traídas al alcance con use también verifican la privacidad, al igual que cualquier otra ruta.

Tenga en cuenta que use solo crea el atajo para el ámbito particular en el que se produce el use. La Lista 7-12 mueve la función eat_at_restaurant a un nuevo módulo hijo llamado customer, que luego es un ámbito diferente al de la declaración use, por lo que el cuerpo de la función no se compilará.

Nombre del archivo: src/lib.rs

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

use crate::front_of_house::hosting;

mod customer {
    pub fn eat_at_restaurant() {
        hosting::add_to_waitlist();
    }
}

Lista 7-12: Una declaración use solo se aplica en el ámbito en el que se encuentra.

El error del compilador muestra que el atajo ya no es aplicable dentro del módulo customer:

error[E0433]: failed to resolve: use of undeclared crate or module `hosting`
  --> src/lib.rs:11:9
   |
11 |         hosting::add_to_waitlist();
   |         ^^^^^^^ use of undeclared crate or module `hosting`

warning: unused import: `crate::front_of_house::hosting`
 --> src/lib.rs:7:5
  |
7 | use crate::front_of_house::hosting;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

Tenga en cuenta que también hay una advertencia de que el use ya no se utiliza en su ámbito. Para solucionar este problema, mueva el use dentro del módulo customer también, o haga referencia al atajo en el módulo padre con super::hosting dentro del módulo hijo customer.

Crear rutas use idiómáticas

En la Lista 7-11, es posible que te hayas preguntado por qué especificamos use crate::front_of_house::hosting y luego llamamos a hosting::add_to_waitlist en eat_at_restaurant, en lugar de especificar la ruta use hasta la función add_to_waitlist para obtener el mismo resultado, como en la Lista 7-13.

Nombre del archivo: src/lib.rs

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

use crate::front_of_house::hosting::add_to_waitlist;

pub fn eat_at_restaurant() {
    add_to_waitlist();
}

Lista 7-13: Traer la función add_to_waitlist al alcance con use, lo cual no es idiómático

Aunque tanto la Lista 7-11 como la Lista 7-13 realizan la misma tarea, la Lista 7-11 es la forma idiómática de traer una función al alcance con use. Traer el módulo padre de la función al alcance con use significa que tenemos que especificar el módulo padre cuando llamamos a la función. Especificar el módulo padre cuando llamamos a la función hace que quede claro que la función no está definida localmente, mientras que minimiza la repetición de la ruta completa. El código de la Lista 7-13 no es claro sobre dónde está definida add_to_waitlist.

Por otro lado, cuando se traen structs, enums y otros elementos con use, es idiómático especificar la ruta completa. La Lista 7-14 muestra la forma idiómática de traer la struct HashMap de la biblioteca estándar al alcance de un crat binario.

Nombre del archivo: src/main.rs

use std::collections::HashMap;

fn main() {
    let mut map = HashMap::new();
    map.insert(1, 2);
}

Lista 7-14: Traer HashMap al alcance de una forma idiómática

No hay una razón fuerte detrás de este idioma: simplemente es la convención que ha surgido, y la gente se ha acostumbrado a leer y escribir código Rust de esta manera.

La excepción a este idioma es si estamos trayendo dos elementos con el mismo nombre al alcance con declaraciones use, porque Rust no lo permite. La Lista 7-15 muestra cómo traer dos tipos Result al alcance que tienen el mismo nombre pero módulos padres diferentes, y cómo hacer referencia a ellos.

Nombre del archivo: src/lib.rs

use std::fmt;
use std::io;

fn function1() -> fmt::Result {
    --snip--
}

fn function2() -> io::Result<()> {
    --snip--
}

Lista 7-15: Traer dos tipos con el mismo nombre al mismo alcance requiere usar sus módulos padres.

Como puedes ver, usar los módulos padres distingue los dos tipos Result. Si en cambio especificáramos use std::fmt::Result y use std::io::Result, tendríamos dos tipos Result en el mismo alcance, y Rust no sabría cuál de ellos queremos decir cuando usamos Result.

Proporcionar nuevos nombres con la palabra clave as

Hay otra solución al problema de traer dos tipos con el mismo nombre al mismo alcance con use: después de la ruta, podemos especificar as y un nuevo nombre local, o alias, para el tipo. La Lista 7-16 muestra otra forma de escribir el código de la Lista 7-15 renomblando uno de los dos tipos Result usando as.

Nombre del archivo: src/lib.rs

use std::fmt::Result;
use std::io::Result as IoResult;

fn function1() -> Result {
    --snip--
}

fn function2() -> IoResult<()> {
    --snip--
}

Lista 7-16: Renombrar un tipo cuando se trae al alcance con la palabra clave as

En la segunda declaración use, elegimos el nuevo nombre IoResult para el tipo std::io::Result, que no entrará en conflicto con el Result de std::fmt que también hemos traído al alcance. La Lista 7-15 y la Lista 7-16 se consideran idiómáticas, ¡así que la elección es tuya!

Re-exportar nombres con pub use

Cuando traemos un nombre al alcance con la palabra clave use, el nombre disponible en el nuevo alcance es privado. Para permitir que el código que llama a nuestro código haga referencia a ese nombre como si hubiera sido definido en el alcance de ese código, podemos combinar pub y use. Esta técnica se llama re-exportación porque estamos trayendo un elemento al alcance pero también haciéndolo disponible para que otros lo traigan a su alcance.

La Lista 7-17 muestra el código de la Lista 7-11 con use en el módulo raíz cambiado a pub use.

Nombre del archivo: src/lib.rs

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

pub use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}

Lista 7-17: Hacer un nombre disponible para que cualquier código lo use desde un nuevo alcance con pub use

Antes de este cambio, el código externo tendría que llamar a la función add_to_waitlist usando la ruta restaurant::front_of_house::hosting::add_to_waitlist(). Ahora que este pub use ha re-exportado el módulo hosting desde el módulo raíz, el código externo puede usar la ruta restaurant::hosting::add_to_waitlist() en su lugar.

La re-exportación es útil cuando la estructura interna de su código es diferente de cómo los programadores que llaman a su código pensarían sobre el dominio. Por ejemplo, en esta metáfora del restaurante, las personas que administran el restaurante piensan en "frente de casa" y "trasera de casa". Pero los clientes que visitan un restaurante probablemente no pensarían en las partes del restaurante en esos términos. Con pub use, podemos escribir nuestro código con una estructura pero exponer una estructura diferente. Hacer esto hace que nuestra biblioteca esté bien organizada para los programadores que trabajan en la biblioteca y los programadores que llaman a la biblioteca. Veremos otro ejemplo de pub use y cómo afecta a la documentación de su crat en "Exporting a Convenient Public API with pub use".

Usar paquetes externos

En el Capítulo 2, programamos un proyecto de juego de adivinanza que utilizaba un paquete externo llamado rand para obtener números aleatorios. Para usar rand en nuestro proyecto, agregamos esta línea a Cargo.toml:

Nombre del archivo: Cargo.toml

rand = "0.8.5"

Agregar rand como dependencia en Cargo.toml le indica a Cargo que descargue el paquete rand y cualquier dependencia de https://crates.io, y haga rand disponible para nuestro proyecto.

Luego, para traer las definiciones de rand al alcance de nuestro paquete, agregamos una línea use que comienza con el nombre del crat, rand, y listamos los elementos que queríamos traer al alcance. Recuerde que en "Generating a Random Number", trajimos la característica Rng al alcance y llamamos a la función rand::thread_rng:

use rand::Rng;

fn main() {
    let secret_number = rand::thread_rng().gen_range(1..=100);
}

Los miembros de la comunidad de Rust han hecho muchos paquetes disponibles en https://crates.io, y traer cualquiera de ellos a su paquete implica estos mismos pasos: listarlos en el archivo Cargo.toml de su paquete y usar use para traer elementos de sus crates al alcance.

Tenga en cuenta que la biblioteca estándar std también es un crat externo a nuestro paquete. Debido a que la biblioteca estándar se distribuye con el lenguaje Rust, no es necesario cambiar Cargo.toml para incluir std. Pero sí necesitamos referirnos a ella con use para traer elementos de allí al alcance de nuestro paquete. Por ejemplo, con HashMap usaríamos esta línea:

use std::collections::HashMap;

Esta es una ruta absoluta que comienza con std, el nombre del crat de la biblioteca estándar.

Usar rutas anidadas para limpiar listas de use grandes

Si estamos usando múltiples elementos definidos en el mismo crat o el mismo módulo, listar cada elemento en una línea separada puede ocupar mucho espacio vertical en nuestros archivos. Por ejemplo, estas dos declaraciones use que teníamos en el juego de adivinanza en la Lista 2-4 traen elementos de std al alcance:

Nombre del archivo: src/main.rs

--snip--
use std::cmp::Ordering;
use std::io;
--snip--

En lugar de eso, podemos usar rutas anidadas para traer los mismos elementos al alcance en una sola línea. Hacemos esto especificando la parte común de la ruta, seguida de dos puntos, y luego corchetes alrededor de una lista de las partes de las rutas que difieren, como se muestra en la Lista 7-18.

Nombre del archivo: src/main.rs

--snip--
use std::{cmp::Ordering, io};
--snip--

Lista 7-18: Especificar una ruta anidada para traer múltiples elementos con el mismo prefijo al alcance

En programas más grandes, traer muchos elementos al alcance del mismo crat o módulo usando rutas anidadas puede reducir en gran medida el número de declaraciones use separadas necesarias.

Podemos usar una ruta anidada en cualquier nivel de una ruta, lo que es útil al combinar dos declaraciones use que comparten una subruta. Por ejemplo, la Lista 7-19 muestra dos declaraciones use: una que trae std::io al alcance y otra que trae std::io::Write al alcance.

Nombre del archivo: src/lib.rs

use std::io;
use std::io::Write;

Lista 7-19: Dos declaraciones use donde una es una subruta de la otra

La parte común de estas dos rutas es std::io, y esa es la primera ruta completa. Para fusionar estas dos rutas en una sola declaración use, podemos usar self en la ruta anidada, como se muestra en la Lista 7-20.

Nombre del archivo: src/lib.rs

use std::io::{self, Write};

Lista 7-20: Combinar las rutas de la Lista 7-19 en una sola declaración use

Esta línea trae std::io y std::io::Write al alcance.

El operador glob

Si queremos traer a todos los elementos públicos definidos en una ruta al alcance, podemos especificar esa ruta seguida del operador glob *:

use std::collections::*;

Esta declaración use trae todos los elementos públicos definidos en std::collections al alcance actual. Tenga cuidado al usar el operador glob. El glob puede hacer que sea más difícil saber qué nombres están en el alcance y dónde se definió un nombre usado en su programa.

El operador glob se usa a menudo en pruebas para traer todo lo que se va a probar al módulo tests; hablaremos de eso en "Cómo escribir pruebas". El operador glob también se usa a veces como parte del patrón preludio: consulte la documentación de la biblioteca estándar para obtener más información sobre ese patrón.

Resumen

¡Felicitaciones! Has completado el laboratorio de "Bringing Paths Into Scope With the Use Keyword". Puedes practicar más laboratorios en LabEx para mejorar tus habilidades.