Explorando los Espacios de Trabajo de Cargo en Rust

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 Cargo Workspaces. Esta práctica es parte del Rust Book. Puedes practicar tus habilidades de Rust en LabEx.

En esta práctica, exploraremos la característica de espacios de trabajo de Cargo, que ayuda a administrar múltiples paquetes relacionados desarrollados en paralelo al dividir un paquete en múltiples cajas de biblioteca.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL rust(("Rust")) -.-> rust/BasicConceptsGroup(["Basic Concepts"]) rust(("Rust")) -.-> rust/DataTypesGroup(["Data Types"]) rust(("Rust")) -.-> rust/FunctionsandClosuresGroup(["Functions and Closures"]) rust(("Rust")) -.-> rust/DataStructuresandEnumsGroup(["Data Structures and Enums"]) rust/BasicConceptsGroup -.-> rust/variable_declarations("Variable Declarations") rust/DataTypesGroup -.-> rust/integer_types("Integer Types") 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-100429{{"Explorando los Espacios de Trabajo de Cargo en Rust"}} rust/integer_types -.-> lab-100429{{"Explorando los Espacios de Trabajo de Cargo en Rust"}} rust/function_syntax -.-> lab-100429{{"Explorando los Espacios de Trabajo de Cargo en Rust"}} rust/expressions_statements -.-> lab-100429{{"Explorando los Espacios de Trabajo de Cargo en Rust"}} rust/method_syntax -.-> lab-100429{{"Explorando los Espacios de Trabajo de Cargo en Rust"}} end

Espacios de trabajo de Cargo

En el Capítulo 12, construimos un paquete que incluía una caja binaria y una caja de biblioteca. A medida que tu proyecto evoluciona, es posible que observes que la caja de biblioteca sigue creciendo y desees dividir tu paquete en múltiples cajas de biblioteca. Cargo ofrece una característica llamada espacios de trabajo que puede ayudar a administrar múltiples paquetes relacionados que se desarrollan en paralelo.

Creación de un espacio de trabajo

Un espacio de trabajo es un conjunto de paquetes que comparten el mismo Cargo.lock y directorio de salida. Vamos a crear un proyecto usando un espacio de trabajo: usaremos código trivial para que podamos concentrar nuestra atención en la estructura del espacio de trabajo. Hay múltiples maneras de estructurar un espacio de trabajo, así que solo mostraremos una forma común. Tendremos un espacio de trabajo que contendrá un binario y dos bibliotecas. El binario, que proporcionará la funcionalidad principal, dependerá de las dos bibliotecas. Una biblioteca proporcionará una función add_one y la otra biblioteca una función add_two. Estos tres crates formarán parte del mismo espacio de trabajo. Comenzaremos creando un nuevo directorio para el espacio de trabajo:

mkdir add
cd add

A continuación, en el directorio add, creamos el archivo Cargo.toml que configurará todo el espacio de trabajo. Este archivo no tendrá una sección [package]. En su lugar, comenzará con una sección [workspace] que nos permitirá agregar miembros al espacio de trabajo especificando la ruta al paquete con nuestro crate binario; en este caso, esa ruta es adder:

Nombre del archivo: Cargo.toml

[workspace]

members = [
    "adder",
]

Luego, crearemos el crate binario adder ejecutando cargo new dentro del directorio add:

$ cargo new adder
     Creado el paquete binario (aplicación) `adder`

En este momento, podemos compilar el espacio de trabajo ejecutando cargo build. Los archivos en su directorio add deberían verse así:

├── Cargo.lock
├── Cargo.toml
├── adder
│   ├── Cargo.toml
│   └── src
│       └── main.rs
└── target

El espacio de trabajo tiene un directorio target en el nivel superior donde se colocarán los artefactos compilados; el paquete adder no tiene su propio directorio target. Incluso si ejecutáramos cargo build desde dentro del directorio adder, los artefactos compilados todavía terminarían en add/target en lugar de add/adder/target. Cargo estructura el directorio target en un espacio de trabajo de esta manera porque los crates en un espacio de trabajo están destinados a depender unos de otros. Si cada crate tuviera su propio directorio target, cada crate tendría que recompilar cada uno de los otros crates en el espacio de trabajo para colocar los artefactos en su propio directorio target. Al compartir un solo directorio target, los crates pueden evitar la recompilación innecesaria.

Creación del segundo paquete en el espacio de trabajo

A continuación, creemos otro paquete miembro en el espacio de trabajo y lo llamemos add_one. Cambiemos el Cargo.toml de nivel superior para especificar la ruta add_one en la lista members:

Nombre del archivo: Cargo.toml

[workspace]

members = [
    "adder",
    "add_one",
]

Luego genere un nuevo crate de biblioteca llamado add_one:

$ cargo new add_one --lib
Creado el paquete de biblioteca $(add_one)

Su directorio add ahora debería tener estos directorios y archivos:

├── Cargo.lock
├── Cargo.toml
├── add_one
│   ├── Cargo.toml
│   └── src
│       └── lib.rs
├── adder
│   ├── Cargo.toml
│   └── src
│       └── main.rs
└── target

En el archivo add_one/src/lib.rs, agreguemos una función add_one:

Nombre del archivo: add_one/src/lib.rs

pub fn add_one(x: i32) -> i32 {
    x + 1
}

Ahora podemos hacer que el paquete adder con nuestro binario dependa del paquete add_one que tiene nuestra biblioteca. Primero necesitaremos agregar una dependencia de ruta en add_one a adder/Cargo.toml:

Nombre del archivo: adder/Cargo.toml

[dependencies]
add_one = { path = "../add_one" }

Cargo no asume que los crates en un espacio de trabajo dependerán unos de otros, por lo que debemos ser explícitos sobre las relaciones de dependencia.

A continuación, usemos la función add_one (del crate add_one) en el crate adder. Abra el archivo adder/src/main.rs y agregue una línea use en la parte superior para traer el nuevo crate de biblioteca add_one al ámbito. Luego cambie la función main para llamar a la función add_one, como en la Lista 14-7.

Nombre del archivo: adder/src/main.rs

use add_one;

fn main() {
    let num = 10;
    println!(
        "Hello, world! {num} plus one is {}!",
        add_one::add_one(num)
    );
}

Lista 14-7: Usando el crate de biblioteca add_one desde el crate adder

¡Compilamos el espacio de trabajo ejecutando cargo build en el directorio add de nivel superior!

$ cargo build
   Compilando add_one v0.1.0 (file:///projects/add/add_one)
   Compilando adder v0.1.0 (file:///projects/add/adder)
    Compilación finalizada [no optimizada + información de depuración] en 0.68s

Para ejecutar el crate binario desde el directorio add, podemos especificar qué paquete del espacio de trabajo queremos ejecutar usando el argumento -p y el nombre del paquete con cargo run:

$ cargo run -p adder
Compilación finalizada [no optimizada + información de depuración] en 0.0s
Ejecutando $(target/debug/adder)
Hello, world! 10 más uno es 11!

Esto ejecuta el código en adder/src/main.rs, que depende del crate add_one.

Dependiendo de un paquete externo en un espacio de trabajo

Tenga en cuenta que el espacio de trabajo tiene solo un archivo Cargo.lock en el nivel superior, en lugar de tener un Cargo.lock en el directorio de cada crate. Esto garantiza que todos los crates estén usando la misma versión de todas las dependencias. Si agregamos el paquete rand a los archivos adder/Cargo.toml y add_one/Cargo.toml, Cargo resolverá ambos a una versión de rand y registrará eso en el único Cargo.lock. Hacer que todos los crates en el espacio de trabajo usen las mismas dependencias significa que los crates siempre serán compatibles entre sí. Agreguemos el crate rand a la sección [dependencies] en el archivo add_one/Cargo.toml para que podamos usar el crate rand en el crate add_one:

Nombre del archivo: add_one/Cargo.toml

[dependencies]
rand = "0.8.5"

Ahora podemos agregar use rand; al archivo add_one/src/lib.rs, y compilar todo el espacio de trabajo ejecutando cargo build en el directorio add traerá y compilará el crate rand. Obtendremos una advertencia porque no estamos referenciando el rand que trajimos al ámbito:

$ cargo build
    Actualizando el índice de crates.io
  Descargado rand v0.8.5
   --snip--
   Compilando rand v0.8.5
   Compilando add_one v0.1.0 (file:///projects/add/add_one)
   Compilando adder v0.1.0 (file:///projects/add/adder)
    Compilación finalizada [no optimizada + información de depuración] en 10.18s

El Cargo.lock de nivel superior ahora contiene información sobre la dependencia de add_one en rand. Sin embargo, aunque rand se use en algún lugar del espacio de trabajo, no podemos usarlo en otros crates del espacio de trabajo a menos que lo agreguemos a sus archivos Cargo.toml también. Por ejemplo, si agregamos use rand; al archivo adder/src/main.rs para el paquete adder, obtendremos un error:

$ cargo build
   --snip--
   Compilando adder v0.1.0 (file:///projects/add/adder)
error[E0432]: import no resuelto `rand`
 --> adder/src/main.rs:2:5
  |
2 | use rand;
  |     ^^^^ no hay crate externo `rand`

Para solucionar esto, edite el archivo Cargo.toml para el paquete adder e indique que rand es una dependencia para él también. Compilar el paquete adder agregará rand a la lista de dependencias de adder en Cargo.lock, pero no se descargarán copias adicionales de rand. Cargo ha garantizado que cada crate en cada paquete en el espacio de trabajo que use el paquete rand usará la misma versión, ahorrándonos espacio y asegurando que los crates en el espacio de trabajo serán compatibles entre sí.

Agregar una prueba a un espacio de trabajo

Para otra mejora, agreguemos una prueba de la función add_one::add_one dentro del crate add_one:

Nombre del archivo: add_one/src/lib.rs

pub fn add_one(x: i32) -> i32 {
    x + 1
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        assert_eq!(3, add_one(2));
    }
}

Ahora ejecute cargo test en el directorio add de nivel superior. Ejecutar cargo test en un espacio de trabajo estructurado como este ejecutará las pruebas para todos los crates en el espacio de trabajo:

[object Object]

La primera sección de la salida muestra que la prueba it_works en el crate add_one pasó. La siguiente sección muestra que se encontraron cero pruebas en el crate adder, y luego la última sección muestra que se encontraron cero pruebas de documentación en el crate add_one.

También podemos ejecutar las pruebas para un crate en particular en un espacio de trabajo desde el directorio de nivel superior usando la bandera -p y especificando el nombre del crate que queremos probar:

[object Object]

Esta salida muestra que cargo test solo ejecutó las pruebas para el crate add_one y no ejecutó las pruebas del crate adder.

Si publica los crates en el espacio de trabajo en https://crates.io, cada crate en el espacio de trabajo debe ser publicado por separado. Al igual que cargo test, podemos publicar un crate en particular en nuestro espacio de trabajo usando la bandera -p y especificando el nombre del crate que queremos publicar.

Para más práctica, agregue un crate add_two a este espacio de trabajo de manera similar al crate add_one!

A medida que su proyecto crece, considere usar un espacio de trabajo: proporciona componentes individuales más fáciles de entender y más pequeños que un gran bloque de código. Además, mantener los crates en un espacio de trabajo puede facilitar la coordinación entre los crates si a menudo se modifican al mismo tiempo.

Resumen

¡Felicitaciones! Has completado el laboratorio de Espacios de Trabajo de Cargo. Puedes practicar más laboratorios en LabEx para mejorar tus habilidades.