29,99 €
Descubra el mundo de Rust, el revolucionario lenguaje de programación de sistemas de código abierto que combina eficiencia con ergonomía. Le permitirá mejorar la productividad y evitar las molestias asociadas con los lenguajes de bajo nivel. Lenguaje de programación Rust es su guía definitiva para navegar por las características y capacidades únicas de Rust 2021. Escrito por Klabnik y Nichols, antiguos miembros del Rust Core Team, este libro le llevará desde los conceptos más básicos hasta las técnicas avanzada y le permitirá escribir software más rápido y confiable. "Aprenderá las innovadoras características de Rust, como la propiedad, el préstamo, los tiempos de vida, los genéricos, los traits y los objetos trait para comunicar las restricciones de su programa al compilador. "Conocerá los punteros inteligentes y multihilo, y cómo la propiedad interactúa con ellos para permitir una concurrencia segura. "Asimilará cómo utilizar Cargo, el gestor de paquetes integrado en Rust, para compilar, documentar el código y gestionar las dependencias. "Interiorizará las mejores maneras para probar, gestionar errores, refactorizar y aprovechar la concordancia expresiva de patrones. Además de los innumerables ejemplos de código, encontrará tres capítulos dedicados a la elaboración de proyectos completos: un juego numérico de adivinanzas, la implementación en Rust de una herramienta de línea de comandos y un servidor multihilo. Este libro es esencial para todo desarrollador, tanto si está buscando iniciar su viaje en Rust como si busca perfeccionar sus habilidades. ¡Embárquese en este viaje y domine Rust! SOBRE LOS AUTORES Steve Klabnik fue jefe del equipo de documentación de Rust y uno de sus principales desarrolladores. Klabnik es un conferenciante habitual y un prolífico colaborador de código abierto. Carol Nichols es miembro del equipo de Rust Crates.io y antigua miembro del Rust Core Team. Nichols es cofundadora de Integer 32, la primera consultoría de software del mundo centrada en Rust, y también ha organizado la Rust Belt Rust Conference.
Sie lesen das E-Book in den Legimi-Apps auf:
Seitenzahl: 832
Copyright © 2023 by the Rust Foundation and the Rust Project Developers. Title of Englishlanguage original: The Rust Programming Language, 2nd Edition, ISBN 9781718503106, published by No Starch Press Inc. 245 8th Street, San Francisco, California United States 94103. The Spanish-language 2nd edition Copyright © 2024 by Marcombo, S.L. under license by No Starch Press Inc. All rights reserved.
Segunda edición original publicada en inglés por No Starch Press Inc. con el título The Rust Programming Language, ISBN 9781718503106 © Steve Klabnik y Carol Nichols, 2023.
Título de la edición en español:Lenguaje de programación Rust
Segunda edición en español, 2024
© 2024 MARCOMBO, S.L.www.marcombo.com
Ilustración de portada: Karen Rustad Tölva
Diseño del interior: Octopod Studios
Revisor técnico: JT
Traducción: Francisco Martínez Carreño
Corrección: Anna Alberola Banasco
Directora de producción: M.a Rosa Castillo
Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta obra solo puede ser realizada con la autorización de sus titulares, salvo excepción prevista por la ley. La presente publicación contiene la opinión del autor y tiene el objetivo de informar de forma precisa y concisa. La elaboración del contenido, aunque se ha trabajado de forma escrupulosa, no puede comportar una responsabilidad específica para el autor ni el editor de los posibles errores o imprecisiones que pudiera contener la presente obra.
ISBN del libro en papel: 978-84-267-3751-9
ISBN del libro electrónico: 978-84-267-3838-7
Producción del ePub: booqlab
Steve Klabnik dirigió el equipo de documentación de Rust y fue uno de los principales desarrolladores de Rust. Conferenciante habitual y prolífico colaborador de código abierto, anteriormente trabajó en proyectos como Ruby y Ruby on Rails.
Carol Nichols es miembro del equipo de Rust Crates.io y exmiembro del equipo central de Rust. Es cofundadora de Integer 32, LLC, la primera consultoría de software del mundo centrada en Rust. Nichols también ha organizado la Rust Belt Rust Conference.
JT es miembro del equipo principal de Rust y cocreador del formato de mensajes de error de Rust, Rust Language Server (RLS) y Nushell. Empezó a usar Rust en 2011 y en 2016 se unió a Mozilla para trabajar en Rust a tiempo completo, ayudando a dar forma a su realización para su uso generalizado. En la actualidad, es formador autónomo de Rust y defensor de la programación segura de sistemas.
Prólogo
Prefacio
Agradecimientos
Introducción
Capítulo 1. Empezamos
Capítulo 2. Programación de un juego de adivinanzas
Capítulo 3. Conceptos de programación habituales
Capítulo 4. Comprensión de la propiedad
Capítulo 5. Uso de structs para estructurar datos relacionados
Capítulo 6. Enums y coincidencia de patrones
Capítulo 7. Gestión de proyectos en expansión con paquetes, crates y módulos
Capítulo 8. Colecciones típicas
Capítulo 9. Gestión de errores
Capítulo 10. Tipos genéricos, traits y lifetimes
Capítulo 11. Escritura de pruebas automatizadas
Capítulo 12. Proyecto de E/S: construcción de un programa de línea de comandos
Capítulo 13. Características de los lenguajes funcionales: iteradores y closures
Capítulo 14. Más sobre Cargo y Crates.io
Capítulo 15. Punteros inteligentes
Capítulo 16. Concurrencia sin preocupación
Capítulo 17. Características de la programación orientada a objetos
Capítulo 18. Patrones y coincidencias
Capítulo 19. Características avanzadas
Capítulo 20. Proyecto final: creación de un servidor web multihilo
Apéndice A. Palabras clave
Apéndice B. Operadores y símbolos
Apéndice C. Traits derivables
Apéndice D. Herramientas útiles de desarrollo
Apéndice E. Ediciones
PRÓLOGO
PREFACIO
AGRADECIMIENTOS
INTRODUCCIÓN
A quién le interesa Rush
Equipos de desarrolladores
Estudiantes
Empresas
Desarrolladores de código abierto
Personas que valoran la rapidez y la estabilidad
A quién va dirigido el libro
Cómo utilizar el libro
Recursos y cómo contribuir al libro
1. EMPEZAMOS
Instalación
Instalación de rustup en Linux o macOS
Instalación de rustup en Windows
Solución de problemas
Actualización y desinstalación
Documentación local
Hello, World!
Creación de un directorio de proyectos
Escritura y ejecución de un programa en Rust
Anatomía de un programa en Rust
Compilación y ejecución son pasos separados
¡Hola, Cargo!
Creación de un proyecto con Cargo
Construcción y ejecución de un proyecto con Cargo
Construcción para la versión de lanzamiento (Release)
Cargo como convención
Resumen
2. PROGRAMACIÓN DE UN JUEGO DE ADIVINANZAS
Configuración de un nuevo proyecto
Procesamiento de una propuesta
Almacenamiento de valores con variables
Recepción de datos introducidos por el usuario
Gestión de posibles fallos con Result
Impresión de valores con println! Marcadores
Prueba de la primera parte
Generación de un número secreto
Uso de un crate para conseguir mayor funcionalidad
Generación de un número aleatorio
Comparación del número propuesto con el número secreto
Admisión de varias propuestas utilizando un bucle
Detención del programa después de hacer una propuesta acertada
Gestión de entradas no válidas
Resumen
3. CONCEPTOS DE PROGRAMACIÓN HABITUALES
Variables y mutabilidad
Constantes
Enmascaramiento
Tipos de datos
Tipos de escalares
Tipos compuestos
Funciones
Parámetros
Sentencias y expresiones
Funciones con valores de retorno
Comentarios
Control de flujo
Expresiones if
Repetición mediante bucles
Resumen
4. COMPRENSIÓN DE LA PROPIEDAD
¿Qué es la propiedad?
Reglas de la propiedad
Ámbito de las variables
El tipo String
Memoria y asignación
Propiedad y funciones
Valores de retorno y ámbito
Referencias y préstamos
Referencias mutables
Referencias colgantes
Reglas de referencias
El tipo rebanada (slice)
String slices (rebanadas de cadena)
Otras rebanadas
Resumen
5. USO DE STRUCTS PARA ESTRUCTURAR DATOS RELACIONADOS
Definición e instanciación de structs
Uso de la forma abreviada de inicialización de campos
Creación de instancias a partir de otras instancias con la sintaxis de actualización de structs
Uso de structs de tupla que no tienen nombres asociados a sus campos para crear diferentes tipos
Structs unitarias sin campos
Programa de ejemplo que utiliza structs
Refactorización con tuplas
Refactorización con structs: aclaración del significado
Mejora de la funcionalidad con traits derivados
Sintaxis de métodos
Definiciones de métodos
Métodos con más parámetros
Funciones asociadas
Varios bloques impl
Resumen
6. ENUMS Y COINCIDENCIA DE PATRONES
Definición de enum
Valores enum
Enum Option y sus ventajas sobre los valores nulos
Construcción match de control de flujo
Patrones que vinculan valores
Comparación con Option<T>
Las matches son exhaustivas
Patrones Catch-All y _ Placeholder
Control de flujo conciso con if let
Resumen
7. GESTIÓN DE PROYECTOS EN EXPANSIÓN CON PAQUETES, CRATES Y MÓDULOS
Paquetes y crates
Definición de módulos para controlar el ámbito y la privacidad
Rutas para hacer referencia a un elemento en el árbol de módulos
Exposición de rutas con la palabra clave pub
Comienzo de rutas relativas con super
Cómo hacer públicas las structs y enums
Introducción de rutas en el ámbito con la palabra clave use
Creación de rutas de use idiomáticas
Provisión de nuevos nombres con la palabra clave as
Reexportación de nombres con pub use
Uso de paquetes externos
Uso de rutas anidadas para limpiar extensas listas de use
El operador glob
Separación de módulos en diferentes archivos
Resumen
8. COLECCIONES TÍPICAS
Almacenamiento de listas de valores con vectores
Creación de un nuevo vector
Actualización de vectores
Lectura de los elementos de un vector
Iteración sobre los valores de un vector
Uso de enum para almacenar varios tipos
La eliminación de un vector elimina sus elementos
Almacenamiento de texto codificado en UTF-8 con strings
¿Qué es String?
Creación de una nueva String
Actualización de Strings
Indexación de Strings
Slicing strings (Rebanadas de cadenas)
Métodos de iteración sobre cadenas
Las cadenas no son tan simples
Almacenamiento de claves con valores asociados en mapas hash
Creación de un nuevo mapa hash
Acceso a los valores de mapas hash
Mapas hash y propiedad
Actualización de mapas hash
Funciones hashing
Resumen
9. GESTIÓN DE ERRORES
Errores irreparables con panic!
Errores reparables con Result
Coincidencia de diferentes errores
Propagación de errores
Entrar en panic! o no entrar en panic!
Ejemplos, código de prototipos y pruebas
Casos en los que tenemos más información que el compilador
Directrices para la gestión de errores
Creación de tipos personalizados para la validación
Resumen
10. TIPOS GENÉRICOS, TRAITS Y LIFETIMES
Eliminación de la duplicación de código mediante su extracción a una función
Tipos de datos genéricos
En las definiciones de funciones
En las definiciones de structs
En las definiciones de enums
En las definiciones de métodos
Rendimiento del código cuando se utilizan genéricos
Traits: definición de comportamiento compartido
Definición de trait
Implementación de un trait en un tipo
Implementaciones por defecto
Traits como parámetros
Devolución de tipos que implementan traits
Uso de las restricciones de trait para implementar métodos de forma condicional
Validación de referencias con lifetimes
Cómo evitar referencias colgantes con lifetimes
Verificador de préstamos
Lifetimes genéricos en funciones
Sintaxis de las anotaciones de lifetimes
Anotaciones de lifetimes en las firmas de las funciones
Pensar en términos de lifetimes
Anotaciones de lifetimes en las definiciones de structs
Elisión de lifetime
Anotaciones de lifetimes en las definiciones de métodos
Lifetime estático
Parámetros de tipo genérico, restricciones de traits y lifetimes juntos
Resumen
11. ESCRITURA DE PRUEBAS AUTOMATIZADAS
Cómo escribir pruebas
Anatomía de las funciones de prueba
Verificación de resultados con la macro assert!
Pruebas de igualdad con las macros assert_eq! y assert_ne!
Adición de mensajes personalizados de error
Comprobación de panics con should_panic
Uso de Result<T, E> en las pruebas
Control de la ejecución de pruebas
Ejecución de pruebas en paralelo o de forma consecutiva
Presentación de la salida de una función
Ejecución de un subconjunto de pruebas por su nombre
Omisión de algunas pruebas a menos que se soliciten expresamente
Organización de las pruebas
Pruebas unitarias
Pruebas de integración
Resumen
12. PROYECTO DE E/S: CONSTRUCCIÓN DE UN PROGRAMA DE LÍNEA DE COMANDOS
Aceptación de argumentos de la línea de comandos
Lectura de valores de los argumentos
Almacenamiento de los valores de los argumentos en variables
Lectura de archivos
Refactorización para mejorar la modularidad y la gestión de errores
Separación de responsabilidades en proyectos binarios
Corrección de la gestión de errores
Extracción de la lógica de main
Separación de código en el crate de biblioteca
Desarrollo de la funcionalidad de la biblioteca con el desarrollo dirigido por pruebas
Escritura de pruebas que fallan
Escritura de código para que la prueba pase
Trabajo con variables de entorno
Escritura de una prueba fallida para la función de búsqueda insensible a mayúsculas y minúsculas
Implementacion de la función search_case_insensitive
Escritura de mensajes de error en la salida de error estándar en lugar de en la salida estándar
Comprobación de dónde están escritos los errores
Impresión de errores en la salida de error estándar
Resumen
13. CARACTERÍSTICAS DE LOS LENGUAJES FUNCIONALES: ITERADORES Y CLOSURES
Closures: funciones anónimas que capturan su entorno
Capturas del entorno con closures
Inferencia y anotación de tipos para closures
Captura de referencias o transferencia de la propiedad
Movimiento de valores capturados afuera de los closures y los traits Fn
Procesamiento de series de elementos con iteradores
El trait iterator y el método next
Métodos que consumen al iterador
Métodos que generan otros iteradores
Uso de closures que capturan su entorno
Cómo mejorar nuestro proyecto de E/S
Eliminación de clones usando iteradores
Código más claro con adaptadores de iteradores
Cómo elegir entre bucles e iteradores
Comparación del rendimiento: bucles frente a iteradores
Resumen
14. MÁS SOBRE CARGO Y CRATES.IO
Personalización de compilaciones con perfiles de lanzamiento
Publicación de un crate en Crates.io
Cómo hacer comentarios útiles en la documentación
Exportación de una API pública adecuada con pub use
Configuración de una cuenta en Crates.io
Adición de metadatos a un nuevo crate
Publicación en Crates.io
Publicación de una nueva versión de un crate existente
Versiones obsoletas de Crates.io con cargo yank
Workspaces (espacios de trabajo) de Cargo
Creación de workspaces
Creación de un segundo paquete en workspace
Instalación de binarios con cargo install
Ampliación de cargo con comandos personalizados
Resumen
15. PUNTEROS INTELIGENTES
Uso de Box<T> para apuntar a los datos en heap
Uso de Box<T> para almacenar datos en heap
Habilitación de tipos recursivos con boxes
Tratamiento de los punteros inteligentes como referencias regulares con Deref
Seguimiento del puntero hasta el valor
Uso de Box<T> como una referencia
Definición de nuestro propio puntero inteligente
Implementación del trait Deref
Coerciones deref implícitas con funciones y metodos
Cómo interactúa la coerción deref con la mutabilidad
Ejecución de código durante la limpieza con el trait Drop
Rc<T>, el puntero inteligente con recuento de referencias
Uso de Rc<T> para compartir datos
La clonación de Rc<T> aumenta el recuento de referencias
RefCell<T> y el patrón de mutabilidad interna
Imposición de las reglas de préstamo en tiempo de ejecución con RefCell<T>
Mutabilidad interna: un préstmao mutable a un valor inmutable
Cómo permitir múltiples propietarios de datos mutables con Rc<T> y RefCell<T>
Los ciclos de referencia pueden provocar fuga de memoria
Creación de ciclos de referencia
Prevención de ciclos de referencia mediante Weak<T>
Resumen
16. CONCURRENCIA SIN PREOCUPACIÓN
Uso de hilos para ejecutar código simultáneamente
Creación de nuevos hilos con spawn
Situación en espera de que finalicen todos los hilos mediante el uso de join Handles
Uso en hilos de closures con move
Uso del paso de mensajes para transferir datos entre hilos
Canales y transferencia de propiedad
Envío de múltiples valores y observación de la espera del receptor
Creación de varios productores mediante la clonación del transmisor
Concurrencia de estados compartidos
Uso de mutex para permitir el acceso a los datos desde un hilo cada vez
Similitudes entre RefCell<T>/Rc<T> y Mutex<T>/Arc<T>
Concurrencia ampliable con los traits Send y Sync
Cómo permitir la transferencia de propiedad entre hilos con Send
Cómo permitir el acceso desde varios hilos con Sync
Las implementaciones manuales de Send y Sync no son seguras
Resumen
17. CARACTERÍSTICAS DE LA PROGRAMACIÓN ORIENTADA A OBJETOS
Características de los lenguajes orientados a objetos
Los objetos contienen datos y comportamientos
La encapsulación que oculta los detalles de implementación
La herencia como sistema de tipos y como compartición de código
Uso de objetos trait que permiten valores de diferentes tipos
Definición de trait para un comportamiento común
Implementación de trait
Los objetos trait realizan despacho dinámico
Implementación de un patrón de diseño orientado a objetos
Definición de la entrada (Post) y creación de una nueva instancia de Post en estado de borrador
Almacenamiento del texto correspondiente al contenido de la entrada
Cómo garantizar que el contenido del borrador de la entrada esté vacío
La solicitud de revisión cambia el estado de la entrada
Adición de approve para cambiar el comportamiento de content
Contrapartidas del patrón de estado
Resumen
18. PATRONES Y COINCIDENCIAS
Partes del código donde se pueden utilizar patrones
Ramas match
Expresiones if let condicionales
Bucles condicionales con while let
Bucles for
Sentencias let
Parámetros de funciones
Refutabilidad: un patrón puede fallar al hacerlo coincidir
Sintaxis de patrones
Literales coincidentes
Coincidencia de variables con nombre
Varios patrones
Coincidenca con rangos de valores usando ..=
Desestructuración para descomponer valores
Cómo ignorar valores en un patrón
Condicionales adicionales utilizando guardas de coincidencia
Vinculaciones con @
Resumen
19. CARACTERÍSTICAS AVANZADAS
Rust no seguro
Superpoderes no seguros
Desreferenciación de un puntero sin procesar
Llamada a una función o método no seguros
Acceso o modificación de una variable estática mutable
Implementación de un trait no seguro
Acceso a los campos de una unión
Cuándo usar código no seguro
Traits avanzados
Tipos asocidos
Parámetros de tipo genérico por defecto y sobrecarga de operadores
Desambiguación entre métodos con el mismo nombre
Uso de supertraits
Uso del patrón newtype para implementar traits externos
Tipos avanzados
Uso del patrón newtype para la seguridad y abstracción de tipos
Creación de sinónimos de tipos con alias de tipos
El tipo never que nunca retorna un valor
Tipos de tamaño dinámico y el trait Sized
Funciones avanzadas y closures
Punteros a funciones
Closures de retorno
Macros
Diferencias entre macros y funciones
Macros declarativas con macro_rules! para metaprogramación en general
Macros procedimentales para generar código a partir de atributos
Cómo escribir una macro derive personalizada
Macros de tipo atributo
Macros de tipo función
Resumen
20. PROYECTO FINAL: CREACIÓN DE UN SERVIDOR WEB MULTIHILO
Creación de un servidor web de un solo hilo
Escucha de la conexión TCP
Lectura de la solicitud
Una mirada más de cerca a una petición HTTP
Escritura de una respuesta
Devolución de HTML real
Validación de la petición y respuesta selectiva
Un toque de refactorización
Conversión del servidor monohilo en servidor multihilo
Simulación de peticiones lentas
Mejora del rendimiento con thread pool
Apagado y limpieza con elegancia
Implementación del trait Drop en ThreadPool
Señalización a los hilos para que dejen de escuchar trabajos
Resumen
A. PALABRAS CLAVE
Palabras clave actualmente en uso
Palabras clave reservadas para su uso en el futuro
Identificadores sin procesar
B. OPERADORES Y SÍMBOLOS
Operadores
Símbolos que no son de operador
C. TRAITS DERIVABLES
Debug de la salida para el programador
PartialEq y Eq para comparaciones de igualdad
PartialOrd y Ord para ordenar comparaciones
Clonación y copia para duplicar valores
Hash para asignar un valor a un valor de tamaño fijo
Default para valores predeterminados
D. HERRAMIENTAS ÚTILES DE DESARROLLO
Formateo automático con rustfmt
Arregle el código con rustfix
Más lints con Clippy
Integración del IDE mediante rust-analyzer
E. EDICIONES
No siempre estuvo tan claro, pero el lenguaje de programación Rust trata fundamentalmente de la capacitación: no importa qué tipo de código escriba ahora, Rust le capacita para llegar más lejos, para programar con confianza en una mayor variedad de dominios de lo que lo hacía antes.
Tomemos, por ejemplo, el trabajo «a nivel de sistemas», que se ocupa de detalles de bajo nivel de gestión de memoria, representación de datos y concurrencia. Tradicionalmente, esta área de la programación se considera como algo arcano, accesible solo a unos pocos elegidos que han dedicado los años necesarios a aprenderla para evitar sus tristemente célebres obstáculos. E incluso quienes lo practican lo hacen con precaución, para evitar que su código esté expuesto a vulnerabilidades, bloqueos o corrupción.
Rust rompe estas barreras eliminando las viejas trampas y proporcionando un conjunto amigable y perfeccionado de herramientas para ayudarle a lo largo del camino. Los programadores que necesiten «sumergirse» en el control de bajo nivel pueden hacerlo con Rust, sin asumir el riesgo habitual de fallos o agujeros de seguridad y sin tener que aprender las sutilezas de una cadena de herramientas voluble. Mejor aún, el lenguaje está diseñado para guiarle de forma natural hacia un código fiable y eficiente en términos de velocidad y uso de memoria.
Los programadores que ya trabajan con código de bajo nivel pueden utilizar Rust para aumentar sus aspiraciones. Por ejemplo, la introducción del paralelismo en Rust es una operación de riesgo relativamente bajo: el compilador detectará los errores clásicos por usted. Y puede abordar optimizaciones más agresivas en su código con la confianza de que no introducirá accidentalmente bloqueos o vulnerabilidades.
Pero Rust no se limita a la programación de sistemas de bajo nivel. Es lo suficientemente expresivo y ergonómico como para hacer que las aplicaciones CLI, servidores web y muchos otros tipos de código sean bastante agradables de escribir (encontrará ejemplos sencillos más adelante en el libro). Trabajar con Rust le permite desarrollar habilidades que se transfieren de un dominio a otro; puede aprender Rust escribiendo una aplicación web, y luego aplicar esas mismas habilidades a su Raspberry Pi.
Este libro adopta plenamente el potencial de Rust para capacitar a sus usuarios. Es un texto amigable y accesible destinado a ayudarle a mejorar no solo sus conocimientos de Rust, sino también su alcance y confianza como programador en general. Así que sumérjase, prepárese para aprender y ¡bienvenido a la comunidad Rust!
Nicholas Matsakis y Aaron Turon
Esta versión del texto asume que está usando Rust 1.62.0 (publicado el 30 de junio de 2022) o posterior con edition="2021" en el archivo Cargo. toml de todos los proyectos para configurarlos con la idea de usar los modismos de la edición Rust 2021. Consulte «Instalación», en la página 1, para obtener instrucciones sobre cómo instalar o actualizar Rust, y consulte el Apéndice E para obtener información sobre las ediciones.
La edición 2021 del lenguaje Rust incluye una serie de mejoras que hacen a Rust más ergonómico y que corrigen algunas inconsistencias. Además de una actualización general para reflejar estas mejoras, esta versión del libro tiene una serie de mejoras para responder a comentarios específicos:
• El capítulo 7 contiene una nueva sección de referencia rápida sobre la organización del código en varios archivos con módulos.
• El capítulo 13 contiene ejemplos de closures nuevos y mejorados que ilustran más claramente las capturas, la palabra clave move y los traits Fn.
• Hemos corregido una serie de pequeños errores e imprecisiones en el libro. ¡Gracias a los lectores que nos los han comunicado!
Hay que tener en cuenta que cualquier código de versiones anteriores de este libro que compile continuará compilando con la edición correspondiente en el archivo Cargo.toml del proyecto, incluso si actualiza la versión del compilador de Rust que está utilizando. ¡Así funcionan las garantías de retrocompatibilidad de Rust!
Queremos dar las gracias a todos los que han trabajado en el lenguaje Rust, por crear un lenguaje increíble sobre el que merece la pena escribir un libro. Damos las gracias a todos los miembros de la comunidad Rust por ser acogedores y crear un entorno en el que merece la pena recibir a más gente.
Estamos especialmente agradecidos a todos los que leyeron las primeras versiones de este libro en línea y aportaron comentarios, informes de errores y solicitudes de extracción. Un agradecimiento especial a Eduard-Mihai Burtescu, Alex Crichton y JT por la revisión técnica, y a Karen Rustad Tölva por lo artístico de la portada. Gracias a nuestro equipo de No Starch, incluidos Bill Pollock, Liz Chadwick y Janelle Ludowise, por mejorar este libro y llevarlo a la imprenta.
Carol agradece la oportunidad de haber trabajado en este libro. Agradece a su familia su constante amor y apoyo, especialmente a su marido Jake Goulding y a su hija Vivian.
Bienvenido al libro de introducción a Rust Lenguaje de programación Rust. El lenguaje de programación Rust le ayudará a escribir software de forma más rápida y confiable. La ergonomía de alto nivel y el control de bajo nivel suelen estar en conflicto en el diseño de lenguajes de programación; Rust desafía ese conflicto. A través del equilibrio entre una potente capacidad técnica y la gran experiencia de los desarrolladores, Rust le proporciona la opción de controlar detalles de bajo nivel (como la utilización de la memoria) sin todos los inconvenientes tradicionalmente asociados a dicho control.
Rust es ideal para muchas personas por varias razones. Veamos algunos de los grupos más importantes a quienes les puede interesar.
Rust está demostrando ser una herramienta productiva para la colaboración entre equipos formados por un gran número de desarrolladores con diferentes niveles de conocimiento en programación de sistemas. El código de bajo nivel es propenso a diversos errores sutiles que, en la mayoría de lenguajes, solo pueden detectarse a través de pruebas exhaustivas y revisiones de código cuidadosas por parte de desarrolladores experimentados. En Rust, el compilador cumple el papel de guardián al negarse a compilar código con estos errores difíciles de encontrar, incluyendo los errores de concurrencia. Al trabajar junto al compilador, el equipo puede dedicar su tiempo a enfocarse en la lógica del programa en lugar de perseguir errores.
Rust también incorpora las últimas herramientas de desarrollo del mundo de la programación de sistemas.
• Cargo, el gestor de dependencias y la herramienta de construcción (compilación más otras actividades) incluida, facilita la adición, compilación y gestión de dependencias de forma sencilla y consistente en todo el ecosistema de Rust.
• La herramienta de formateo rustfmt garantiza un estilo de codificación consistente entre los desarrolladores.
• El Rust Language Server proporciona la integración del entorno de desarrollo integrado (IDE) para la finalización de código (o autocompletar) y para mostrar mensajes de error en línea.
Con la utilización de estas y otras herramientas en el ecosistema de Rust, los desarrolladores pueden ser más productivos al escribir código a nivel de sistema.
Rust está destinado a estudiantes y a aquellos interesados en aprender sobre conceptos de sistemas. Al utilizar Rust, muchas personas han aprendido sobre temas como el desarrollo de sistemas operativos. La comunidad es muy acogedora y está dispuesta a responder las preguntas que formulan los estudiantes. A través de esfuerzos como es el caso de este libro, los equipos de Rust desean que los conceptos de sistemas sean más accesibles para un mayor número de personas, especialmente aquellos que son nuevos en programación.
Cientos de empresas, grandes y pequeñas, utilizan Rust en producción para realizar una gran variedad de tareas, como son las herramientas de línea de comandos, servicios web, herramientas de DevOps, dispositivos integrados, análisis y transcodificación de audio y vídeo, criptomonedas, bioinformática, motores de búsqueda, aplicaciones de Internet de las cosas, aprendizaje automático e incluso partes importantes del navegador web Firefox.
Rust es para personas que quieren construir el lenguaje de programación Rust, la comunidad, las herramientas para desarrolladores y las bibliotecas. Nos encantaría que usted contribuyera al lenguaje Rust.
Rust es para la gente que anhela velocidad y estabilidad en un lenguaje. Por velocidad nos referimos tanto a la rapidez con la que puede ejecutarse el código Rust como a la velocidad a la que Rust le permite escribir programas. Las comprobaciones del compilador de Rust garantizan la estabilidad mediante la adición de características y la refactorización. Esto contrasta con el frágil código heredado de los lenguajes sin estas comprobaciones, que los desarrolladores a menudo temen modificar. Al esforzarse por conseguir abstracciones de coste cero (características de alto nivel que se compilan en código de bajo nivel tan rápido como el código escrito manualmente), Rust se esfuerza por hacer que el código seguro sea también un código rápido.
El lenguaje Rust espera apoyar también a muchos otros usuarios; los mencionados aquí son solo algunos de los principales interesados. En general, la mayor ambición de Rust es eliminar las contrapartidas que los programadores han aceptado durante décadas, proporcionando seguridad y productividad, velocidad y ergonomía. Dele una oportunidad a Rust y compruebe si las opciones que ofrece funcionan para usted.
En el libro se presupone que usted ha escrito código en otro lenguaje de programación, aunque no se hace ninguna suposición sobre cuál. Hemos intentado que el material sea ampliamente accesible para personas con una amplia variedad de conocimientos de programación. No dedicamos mucho tiempo a explicar qué es la programación o cómo pensar en ella. Si es nuevo en programación, le vendrá mejor leer un libro que proporcione específicamente una introducción a la programación.
En general, se da por supuesto que el libro se lee de principio a fin. Los capítulos posteriores se basan en conceptos tratados anteriormente, y puede que los primeros no profundicen en un tema concreto pero lo retomen en un capítulo posterior.
Encontrará dos tipos de capítulo en este libro: capítulos de concepto y capítulos de proyecto. En los capítulos de concepto, aprenderá sobre un aspecto de Rust. En los capítulos de proyectos, crearemos pequeños programas juntos, aplicando lo que haya aprendido hasta ese momento. Los Capítulos 2, 12 y 20 son capítulos de proyectos; el resto son capítulos de conceptos.
El Capítulo 1 explica cómo instalar Rust, cómo escribir el programa "Hello, world!" y cómo utilizar Cargo, el gestor de paquetes y la herramienta de construcción de Rust. El Capítulo 2 es una introducción práctica a la escritura de un programa en Rust, en la que se crea un juego de adivinanza de números. Aquí, tratamos conceptos a alto nivel, y los capítulos posteriores proporcionarán detalles adicionales. Si quiere ensuciarse las manos de inmediato, el Capítulo 2 es el lugar adecuado. El Capítulo 3 trata las características de Rust que son similares a las de otros lenguajes de programación, y en el Capítulo 4 aprenderá sobre el sistema de propiedad de Rust. Si es un aprendiz particularmente meticuloso que prefiere aprender cada detalle antes de pasar al siguiente, puede que quiera saltarse el Capítulo 2 e ir directamente al Capítulo 3, y volver al Capítulo 2 cuando quiera trabajar en un proyecto aplicando los detalles que ha aprendido.
En el Capítulo 5 se discuten las structs y los métodos, y en el Capítulo 6 se tratan las enums, las expresiones match, y la construcción de control de flujo if let. Usará structs y enums para hacer tipos personalizados en Rust.
En el Capítulo 7, aprenderá sobre el sistema de módulos de Rust y sobre las reglas de privacidad para organizar el código y la interfaz pública de programación de aplicaciones (API). En el Capítulo 8 se discuten algunas estructuras de datos de colección habituales que la biblioteca estándar proporciona, tales como vectores, cadenas y mapas hash. El Capítulo 9 explora la filosofía y las técnicas de gestión de errores de Rust.
El Capítulo 10 profundiza en los genéricos, los traits (rasgos) y los lifetimes (tiempos de vida), que le dan el poder de definir código que se aplica a varios tipos. En el Capítulo 11 se tratan las pruebas, que incluso con las garantías de seguridad de Rust son necesarias para asegurar que la lógica del programa es correcta. En el Capítulo 12, construiremos nuestra propia implementación de un subconjunto de funcionalidades de la herramienta de línea de comandos grep que busca texto dentro de archivos. Para esto, usaremos muchos de los conceptos que discutimos en los capítulos anteriores.
El Capítulo 13 explora los closures (cierres) e iteradores: características de Rust que provienen de los lenguajes de programación funcionales. En el Capítulo 14, examinaremos Cargo más a fondo y hablaremos de las mejores prácticas para compartir sus bibliotecas con otros usuarios. En el Capítulo 15 se analizan los punteros inteligentes que proporciona la biblioteca estándar y los traits que permiten su funcionalidad.
En el Capítulo 16, recorreremos diferentes modelos de programación concurrente y hablaremos de cómo Rust le ayuda a programar en múltiples hilos sin preocupación. En el Capítulo 17 se examina cómo los modismos de Rust se comparan con los principios de programación orientada a objetos con los que podría estar familiarizado.
El Capítulo 18 es una referencia sobre patrones y concordancia de patrones, que son poderosas formas de expresar ideas a través de los programas de Rust. El Capítulo 19 contiene una mezcla de temas avanzados de interés, incluyendo Rust no seguro, macros, y otros sobre lifetimes, traits, tipos, funciones y closures.
En el Capítulo 20, completaremos un proyecto en el que implementaremos un servidor web multihilo de bajo nivel.
Por último, algunos apéndices contienen información útil sobre el lenguaje en un formato más parecido a una referencia. En el Apéndice A se tratan las palabras clave de Rust, el Apédice B se ocupa de los operadores y símbolos de Rust, en el Apéndice C se tratan los traits derivables proporcionados por la biblioteca estándar, el Apéndice D se encarga de presentar algunas herramientas de desarrollo que son de utilidad, y en el Apéndice E se explican las ediciones de Rust.
No hay una forma incorrecta de leer este libro: si quiere saltarse algo, ¡hágalo! Es posible que tenga que volver a capítulos anteriores si experimenta alguna confusión. Pero haga lo que más le convenga.
Una parte importante del proceso de aprendizaje de Rust es aprender a leer los mensajes de error que muestra el compilador: estos le guiarán hacia un código funcional. Por lo tanto, proporcionaremos muchos ejemplos que no compilan junto con el mensaje de error que el compilador le mostrará en cada situación. Sepa que si introduce y ejecuta un ejemplo al azar, ¡puede que no compile! Asegúrese de leer el texto anexo para ver si el ejemplo que está intentando ejecutar está destinado a dar error. En la mayoría de las situaciones, le guiaremos a la versión correcta de cualquier código que no compile.
Este libro es de código abierto. Si encuentra algún error, no dude en enviar una incidencia o una pull request (solicitud de extracción) a GitHub en https://github.com/rust-lang/book. Para más información, consulte CONTRIBUTING.md en https://github.com/rust-lang/book/blob/main/CONTRIBUTING.md.
El código fuente de los ejemplos de este libro, las erratas y otra información están disponibles en https://nostarch.com/rust-programming-language-2nd-edition.
¡Comencemos su viaje a través de Rust! Hay mucho que aprender, pero todo recorrido debe comenzar en algún punto. En este capítulo, trataremos:
• Cómo instalar Rust en Linux, macOS, y Windows.
• Cómo escribir un programa que imprima Hello, world!
• Cómo usar cargo, y el sistema de construcción y administración de paquetes de Rust.
El primer paso es la instalación de Rust. Descargaremos Rust utilizando rustup, una herramienta de línea de comandos para gestionar versiones de Rust y herramientas asociadas. Necesitará una conexión a Internet para la descarga.
NOTA
Si decide no utilizar rustup por alguna razón, por favor visite la página Other Rust Installation Methods enhttps://forge.rust-lang.org/infra/other-installation-methods.html, donde podrá disponer de otras opciones.
Mediante los siguientes pasos se instala la última versión estable del compilador de Rust. Las garantías de estabilidad de Rust aseguran que todos los ejemplos del libro que compilen continuarán compilando con versiones más recientes de Rust. El resultado puede diferir ligeramente entre las distintas versiones porque Rust mejora frecuentemente los mensajes de error y las advertencias. En otras palabras, cualquier versión más reciente y estable de Rust que instale siguiendo estos pasos debería funcionar como se espera con el contenido del libro.
NOTACIÓN DE LINEA DE COMANDOS
En este capítulo, y a lo largo del libro, mostraremos algunos comandos que se usan utilizando el terminal. Las líneas que debe teclear en el terminal comienzan todas con $. No es necesario que escriba el carácter $; es el símbolo del sistema que se muestra para indicar el inicio de cada comando. Las líneas que no comienzan con $ generalmente muestran el resultado del comando anterior. Por otra parte, en los ejemplos específicos de PowerShell se utilizará > en lugar de $.
Si utiliza Linux o macOS, abra el terminal y teclee el siguiente comando:
El comando descarga un script y comienza la instalación de la herramienta rustup, que instala la última versión estable de Rust. Es posible que se le solicite una contraseña. Si la instalación tiene éxito, aparecerá la siguiente línea:
También necesitará un enlazador (linker), que es un programa que Rust utiliza para unir los resultados compilados en un archivo. Es probable que ya tenga uno. Si aparecen errores con el enlazador, deberá instalar un compilador de C, que normalmente incluirá un enlazador. El compilador de C también es útil porque algunos de los paquetes de uso más frecuente de Rust dependen del código C y requerirán un compilador de C
En macOS, puede obtener el compilador de C ejecutando lo siguiente:
Los usuarios de Linux generalmente deberán instalar GCC o Clang, dependiendo de la documentación de cada distribución. Por ejemplo, si usa Ubuntu, puede instalar el paquete build-essential.
Para Windows, vaya a https://www.rust-lang.org/tools/install y siga las instrucciones para instalar Rust. En algún momento de la instalación, recibirá un mensaje explicando que también necesitará las herramientas de construcción MSVC para Visual Studio 2013 o posterior.
Para obtener las herramientas de construcción, deberá instalar Visual Studio 2022 desde https://visualstudio.microsoft.com/downloads. Cuando se le pregunte qué cargas de trabajo instalar, incluya:
• Desktop Development con C++”
• Windows 10 u 11 SDK
• El componente del paquete de idioma inglés, junto con cualquier otro paquete de idioma que elija.
En el resto del libro se utilizan comandos que funcionan tanto en cmd.exe como en PowerShell. En el caso de que existan diferencias específicas, explicaremos cuál de ellos usar.
Para verificar que Rush se ha instalado correctamente, abra el terminal y teclee la siguiente línea:
Debería ver el número de versión, el hash del commit (hash de confirmación de cambios) y la fecha del commit de la última versión estable que se ha lanzado, en el siguiente formato:
Si ve esta información, ¡ha instalado Rust correctamente! Si no la ve, verifique que Rust esté en su variable de sistema %PATH% tal como explicamos a continuación.
En Windows CMD, use:
En PowerShell, use:
En Linux y macOS, use:
Si todo está correcto y Rust aún no funciona, hay varios recursos con los que puede obtener ayuda. Descubra cómo ponerse en contacto con otros programadores de Rust (o rustaceans, un apodo divertido que nos ponemos a nosotros mismos) en la página de la comunidad: https://www.rust-lang.org/community.
Una vez que Rust esté instalado mediante rustup, actualizarlo a una versión recién lanzada es fácil. Desde el terminal, ejecute el siguiente script de actualización:
Para desinstalar Rust y rustup, ejecute el siguiente script de desinstalación desde su terminal:
La instalación de Rust también incluye una copia local de la documentación para que pueda leerla sin conexión. Ejecute rustup doc para abrir la documentación local en su navegador.
Cada vez que se proporcione un tipo o una función por parte de la biblioteca estándar y no esté seguro de lo que hace o de cómo usarlo, consulte la documentación de la interfaz de programación de aplicaciones (API) para averiguarlo.
Ahora que ha instalado Rust, es hora de escribir su primer programa en Rust. Es tradición, al aprender un nuevo lenguaje, escribir un pequeño programa que imprima el texto Hello, world! en la pantalla, así que ¡haremos lo mismo aquí!
NOTA
En el libro, se supone que el lector está familiarizado de alguna manera con la línea de comandos. Rust no impone normas específicas sobre su entorno de edición o herramientas, o sobre dónde debe residir su código, por lo que si prefiere usar un entorno de desarrollo integrado (IDE) en lugar de la línea de comandos, puede utilizar su IDE favorito. Muchos IDE ahora tienen, en alguna medida, un soporte para Rust; consulte la documentación del IDE para obtener más detalles. El equipo de Rust se ha enfocado en habilitar un excelente soporte de IDE mediante rust-analyzer. Vea el Apéndice D para ampliar detalles.
Comenzaremos creando un directorio para almacenar el código de Rust. Para Rust es indiferente dónde se encuentre ubicado el código, pero para los ejercicios y proyectos del libro sugerimos crear un directorio llamado projects en su directorio principal y almacenar allí todos sus proyectos.
Abra el terminal y teclee los comandos que veremos a continuación para crear el directorio projects y un directorio para el proyecto «Hello, world!» dentro del directorio projects.
Para Linux, macOS y PowerShell en Windows, introduzca lo siguiente:
Para Windows CMD, introduzca lo siguiente:
A continuación, cree un nuevo archivo fuente y llámelo main.rs. Los archivos de Rust siempre terminan con la extensión .rs. Si utiliza más de una palabra en el nombre del archivo, la convención es usar un guion bajo para separarlas. Por ejemplo, utilice hello_world.rs en lugar de helloworld.rs.
Ahora, abra el archivo main.rs que acaba de crear e introduzca el código del Listado 1-1.
Listado 1-1: Programa que presenta en pantalla Hello, world!
Guarde el archivo y vuelva a la ventana del terminal en el directorio ~/projects/hello_world. En Linux o macOS, introduzca los siguientes comandos para compilar y ejecutar el archivo:
En Windows, introduzca el comando .\main.exe en lugar de./main:
Independientemente de su sistema operativo, la cadena Hello, world! debería aparecer en el terminal. Si no aparece esta salida, consulte la sección «Solución de problemas» para obtener ayuda.
Si ha aparecido Hello, world!, ¡felicidades! Ha escrito oficialmente un programa en Rust. ¡Eso le convierte en programador de Rust! ¡Bienvenido!
Repasemos en detalle el programa «Hello, world!». Aquí está la primera pieza del rompecabezas:
Estas líneas definen una función llamada main. La función main es especial: siempre es el primer código que se ejecuta en cada programa ejecutable de Rust. Aquí, la primera línea declara una función llamada main que no tiene parámetros y no devuelve nada. Si hubiera parámetros, se colocarían dentro del paréntesis ().
El cuerpo de la función está encerrado entre llaves {}. Rust requiere que todos los cuerpos de las funciones se encierren entre llaves. Es una buena práctica colocar la llave de apertura en la misma línea que la declaración de la función, añadiendo un espacio entre ellas.
NOTA
Si desea mantener un estilo estándar en sus proyectos de Rust, puede utilizar una herramienta de formateo automático llamada rustfmt para formatear su código en un estilo particular (en el Apéndice D se amplía la información sobre rustfmt). El equipo de Rust ha incluido esta herramienta en la distribución estándar de Rust, al igual que rustc, por lo que debería estar instalada en su ordenador.
El cuerpo de la función main contiene el siguiente código:
Esta línea hace todo el trabajo en este pequeño programa; presenta el texto en la pantalla. Aquí hay cuatro detalles importantes que hay que tener en cuenta.
Primero, el estilo de Rust es hacer el sangrado con cuatro espacios, no con una tabulación.
Segundo, println! llama a una macro en Rust. Si se tratara de una función, se escribiría como println (sin el signo !). Discutiremos las macros en Rust con más detalle en el Capítulo 19. Por ahora, solo necesita saber que el uso de ! significa que está llamando a una macro en lugar de tratarse de una función normal y que las macros no siempre siguen las mismas reglas que las funciones.
Tercero, puede ver la cadena "Hello, world!". Pasamos esta cadena como argumento a println!, y la cadena se presenta en pantalla.
Cuarto, terminamos la línea con un punto y coma (;), lo cual indica que esta expresión ha terminado y que la siguiente está lista para comenzar. La mayoría de las líneas de código en Rust terminan con un punto y coma.
Acaba de ejecutar el programa recién creado, así que examinemos cada paso del proceso.
Antes de ejecutar un programa en Rust, debe compilarlo utilizando el compilador de Rust, introduciendo el comando rustc y pasando el nombre de su archivo fuente, de la siguiente manera:
Si tiene experiencia en C o C++, notará que lo anterior es similar a gcc o clang. Después de realizar con éxito la compilación, Rust genera un ejecutable binario.
En Linux, macOS y PowerShell en Windows, puede ver el ejecutable tecleando el comando ls en su shell:
En Linux y macOS, verá dos archivos. Con PowerShell en Windows, verá los mismos tres archivos que vería utilizando CMD. Con CMD en Windows, teclearía lo siguiente:
Lo anterior muestra el archivo del código fuente con la extensión .rs, el archivo ejecutable (main.exe en Windows, pero main en todas las demás plataformas) y, al usar Windows, el archivo que contiene información de depuración con la extension .pdb. A partir de aquí, se ejecuta el archivo main o main.exe de la siguiente manera:
Si su main.rs es el programa “Hello, world!”, esta línea presenta Hello, world! en su terminal.
Si está más familiarizado con un lenguaje de programación dinámico como Ruby, Python o JavaScript, es posible que no esté acostumbrado a compilar y ejecutar un programa en pasos separados.
Rust es un lenguaje compilado de antemano, lo que significa que usted puede compilar un programa y proporcionarle el ejecutable a otros, y ellos podrán ejecutarlo incluso sin tener Rust instalado. En cambio, si le da a alguien un archivo .rb, .py, o .js, esa persona necesitará tener instalada una implementación de Ruby, Python o JavaScript (respectivamente). Pero en esos lenguajes, solo se necesita un comando para compilar y ejecutar el programa. Todo es una cuestion de compromiso con el diseño de lenguajes.
Compilar usando solamente rustc es suficiente para programas sencillos, pero a medida que su proyecto crezca, querrá gestionar todas las opciones y facilitar la compartición de su código. A continuación, presentaremos la herramienta Cargo, que le ayudará a escribir programas en Rust para el mundo real.
Cargo es el sistema de construcción y el gestor de paquetes de Rust. La mayoría de los programadores de Rust utilizan esta herramienta para administrar sus proyectos de Rust, porque Cargo puede hacer muchas tareas por usted, como compilar el código, descargar las bibliotecas de las que el código depende y compilar esas bibliotecas. Llamamos dependencias a las bibliotecas que su código necesita.
Los programas más sencillos en Rust, como el que hemos escrito hasta ahora, no tienen dependencias. Si hubiéramos compilado el proyecto «Hello, world!» con Cargo, solo se utilizaría la parte de Cargo que maneja la compilación del código. A medida que escriba programas en Rust más complejos, añadirá dependencias, y si comienza un proyecto utilizando Cargo, será mucho más fácil añadir dependencias.
Debido a que la gran mayoría de los proyectos en Rust utilizan Cargo, en el resto del libro se supone que usted también lo utilizará. Cargo viene instalado con Rust si ha utilizado los instaladores oficiales mencionados en «Instalación». Si ha instalado Rust de alguna otra manera, verifique si Cargo está instalado tecleando lo siguiente en el terminal:
Si ve un número de versión, ¡lo tiene! Si ve un error, como command not found, consulte la documentación de su método de instalación para determinar cómo instalar Cargo por separado.
Vamos a crear un nuevo proyecto utilizando Cargo y vamos a ver cómo difiere de nuestro proyecto original «Hello, world!». Navegue de vuelta a su directorio projects (o donde haya decidido almacenar su código). Luego, en cualquier sistema operativo, ejecute lo siguiente:
El primer comando crea un nuevo directorio y proyecto llamado hello_cargo. Hemos nombrado nuestro proyecto como hello_cargo, y Cargo crea sus archivos en un directorio con el mismo nombre.
Entre en el directorio hello_cargo y liste los archivos. Verá que Cargo ha generado dos archivos y un directorio (en lugar de tener que hacerlo nosotros): un archivo Cargo.toml y un directorio src que contiene el archivo main.rs.
También ha inicializado un nuevo repositorio Git junto con un archivo .gitignore. Los archivos de Git no se generarán si ejecuta cargo new dentro de un repositorio Git existente; puede anular este comportamiento utilizando cargo new --vcs=git.
NOTA
Git es un sistema muy utilizado de control de versiones. Puede cambiar cargo new para utilizar un sistema de control de versiones diferente o ningún sistema de control de versiones utilizando la bandera --vcs. Ejecute cargo new --help para ver las opciones disponibles.
Abra Cargo.toml en el editor de texto que haya elegido. Debería ser similar al código del Listado 1-2.
Listado 1-2: Contenido de Cargo.toml generado por cargo new.
Este archivo está en formato TOML (Tom’s Obvious, Minimal Language), que es el formato de configuración de Cargo.
La primera línea, [package], es un encabezado de sección que indica que las declaraciones siguientes configuran un paquete. A medida que añadamos más información a este archivo, añadiremos otras secciones.
En las tres líneas siguientes se determina la información de configuración que Cargo necesita para compilar el programa: el nombre, la versión y la edición de Rust a utilizar. Hablaremos sobre la clave edition en el Apéndice E.
La última línea, [dependencies], es el inicio de una sección donde se pueden listar las dependencias del proyecto. En Rust, los paquetes de código se conocen como crates. No necesitaremos otros crates para este proyecto, pero los utilizaremos en el primer proyecto del Capítulo 2, por lo que utilizaremos esta sección de dependencias en ese momento.
Ahora abra src/main.rs y eche un vistazo:
Cargo ha generado un programa «Hello, World!» por usted, ¡exactamente como el que escribimos en el Listado 1-1! Hasta ahora, las diferencias entre nuestro proyecto y el proyecto generado por Cargo son que Cargo ha colocado el código en el directorio src y tenemos un archivo de configuración Cargo.toml en el directorio principal.
Cargo espera que los archivos fuente de su proyecto se encuentren dentro del directorio src. El directorio del proyecto de nivel superior es solo para archivos README, información de licencia, archivos de configuración y cualquier otra cosa que no esté relacionada con el código de su proyecto. El uso de Cargo le ayuda a organizar sus proyectos. Hay un lugar para todo, y todo está en su lugar.
Si ha iniciado un proyecto que no utiliza Cargo, como hicimos con el proyecto «Hello, World!», puede convertirlo en un proyecto que sí use Cargo. Mueva el código del proyecto al directorio src y cree el archivo Cargo.toml adecuado.
Ahora ¡veamos qué cambia cuando construimos y ejecutamos el programa «Hello, world!» con Cargo! Desde su directorio hello_cargo, construya su proyecto tecleando el siguiente comando:
Este comando crea un archivo ejecutable en target/debug/hello_ cargo (o target\debug\hello_cargo.exe en Windows), en lugar de hacerlo en su directorio actual. Debido a que la construcción predeterminada es una construcción de depuración, Cargo coloca el binario en un directorio llamado debug. Puede ejecutar el archivo ejecutable con el siguiente comando:
Si todo va bien, Hello, world! debería aparecer en el terminal. Ejecutar cargo build por primera vez también hace que Cargo cree un nuevo archivo en el nivel superior: Cargo.lock. Este archivo realiza un seguimiento de las versiones exactas de las dependencias del proyecto. Como este proyecto no tiene dependencias, el archivo está casi vacío. Nunca necesitará cambiar este archivo manualmente; Cargo gestiona su contenido por usted.
Acabamos de construir un proyecto con cargo build y lo ejecutamos con ./target/debug/hello_cargo, pero también podemos utilizar cargo run para compilar el código y luego ejecutar el ejecutable resultante, todo en un solo comando:
Utilizar cargo run es más conveniente que tener que acordarse de ejecutar cargo build y luego usar la ruta completa hacia el binario, por lo que la mayoría de los desarrolladores utilizan cargo run.
Observe que esta vez no vimos una salida que indicara que Cargo estaba compilando hello_cargo. Cargo ha determinado que los archivos no han cambiado, por lo que no ha vuelto a construir el proyecto antes de ejecutar el binario. Si hubiera modificado su código fuente, Cargo habría vuelto a compilar el proyecto antes de ejecutarlo, y habría visto esta salida:
Cargo también proporciona el comando llamado cargo check. Este comando verifica rápidamente el código para asegurarse de que compila correctamente, pero no produce un ejecutable:
¿Por qué no desearía usted un ejecutable? A menudo, cargo check es mucho más rápido que cargo build porque omite el paso de producir un ejecutable. Si verifica continuamente su trabajo mientras escribe el código, ¡utilizar cargo check acelerará el proceso de informarle si su proyecto sigue compilando! Por eso, muchos desarrolladores de Rust ejecutan cargo check periódicamente mientras escriben su programa para asegurarse de que compila.
Recapitulemos lo que hemos aprendido hasta ahora sobre Cargo:
• Podemos crear un proyecto usando cargo new.
• Podemos construir un proyecto usando cargo build.
• Podemos construir y ejecutar un proyecto en un solo paso usando cargo run.
• Podemos construir un proyecto sin generar un binario para verificar errores usando cargo check.
• En lugar de guardar el resultado de la construcción en el mismo directorio que nuestro código, Cargo lo almacena en el directorio target/debug.
Una ventaja adicional de utilizar Cargo es que los comandos son los mismos sin importar el sistema operativo con el que se esté trabajando. Por lo tanto, a partir de este punto, ya no proporcionaremos instrucciones específicas para Linux y macOS frente a Windows.
Cuando su proyecto esté listo para el lanzamiento, puede utilizar cargo build --release para compilarlo con optimizaciones. Este comando creará un ejecutable en la carpeta target/release en lugar de target/debug. Las optimizaciones hacen que el código de Rust se ejecute más rápido, pero activarlas prolonga el tiempo que se tarda en compilar el programa. Por esta razón, existen dos perfiles diferentes: uno para el desarrollo, cuando se desea volver a construir rápidamente y con frecuencia, y otro para compilar el programa final que entregará a un usuario y que no volverá a compilar repetidamente y se ejecutará lo más rápidamente posible. Si evalúa el tiempo de ejecución de su código, asegúrese de ejecutar cargo build --release y de realizar las pruebas de rendimiento (benchmarking) con el ejecutable en target/release.
Con proyectos sencillos, Cargo no aporta mucho valor con respecto al uso de rustc, pero demostrará su valía a medida que sus programas se vuelvan más complejos. Una vez que los programas crecen y tienen varios archivos o necesitan dependencias, es mucho más fácil dejar que Cargo coordine la construcción.
Aunque el proyecto hello_cargo es sencillo, se utilizan ahora en él gran parte de las herramientas reales que empleará usted en el resto de su carrera en Rust. De hecho, para trabajar en cualquier proyecto existente, puede utilizar los siguientes comandos para revisar el código utilizando Git, cambiar al directorio del proyecto y construirlo:
Para obtener más información sobre Cargo, consulte la documentación en https://doc.rust-lang.org/cargo.
¡Ya ha comenzado su viaje a través de Rust! En este capítulo, ha aprendido cómo:
• Instalar la última versión estable de Rust utilizando rustup.
• Actualizar a una versión más reciente de Rust.
• Abrir la documentación instalada localmente.
• Escribir y ejecutar el programa «Hello, World!» utilizando rustc directamente.
• Crear y ejecutar un nuevo proyecto utilizando las convenciones de Cargo.
Este es un buen momento para construir un programa más sustancial y familiarizarse con la lectura y la escritura de código Rust. En el Capítulo 2, contruiremos un programa de juego de adivinanzas. Si prefiere comenzar aprendiendo cómo funcionan los conceptos de programación más comunes en Rust, consulte el Capítulo 3 y luego regrese al Capítulo 2.
¡Vamos a sumergirnos en Rust trabajando juntos en un proyecto práctico! Este capítulo introduce algunos conceptos comunes de Rust, y le muestra cómo usarlos en un programa real. Aprenderá sobre let, match, métodos, funciones asociadas y crates externos, ¡entre otras cosas! En los siguientes capítulos, exploraremos estas ideas con más detalle. En este capítulo, simplemente practicará los fundamentos.
Implementaremos un problema clásico de programación para principiantes: un juego de adivinanzas. Así es como funciona: el programa generará un número entero aleatorio entre 1 y 100. A continuación, le pedirá al jugador que proponga un valor. Después de introducirlo, el programa indicará si el valor es demasiado bajo o demasiado alto. Si el valor es correcto, el juego imprimirá un mensaje de felicitación y saldrá.
Para configurar un nuevo proyecto, vaya al directorio projects que creó en el Capítulo 1 y cree un nuevo proyecto utilizando Cargo, de la siguiente manera:
El primer comando, cargo new, toma el nombre del proyecto (guessing_game) como primer argumento. El segundo comando cambia al directorio del nuevo proyecto.
Observe el archivo Cargo.toml generado:
Como vio en el Capítulo 1, cargo new genera el programa «Hello, world!» por usted. Eche un vistazo al archivo src/main.rs:
Ahora vamos a compilar el programa «Hello, world!» y lo vamos a ejecutar en un solo paso utilizando el comando cargo run:
El comando run resulta útil cuando se necesita hacer iteraciones rápidas en un proyecto, como haremos en este juego, probando rápidamente cada iteración antes de pasar a la siguiente.
Abra nuevamente el archivo src/main.rs. Usted va a escribir todo el código en este archivo.
La primera parte del programa del juego de adivinanzas solicitará la entrada por parte del usuario, procesará esa entrada y verificará que la entrada tenga la forma esperada. Para empezar, permitiremos al jugador teclear una propuesta. Introduzca ahora el código de la Lista 2-1 en src/main.rs.
Listado 2-1: Código que admite una propuesta del usuario y la imprime.
Este código contiene mucha información, así que vamos a repasarlo línea por línea. Para obtener la entrada del usuario y luego imprimir el resultado como salida, necesitamos importar la biblioteca de entrada/salida io al ámbito de nuestro programa. La biblioteca io proviene de la biblioteca estándar, conocida como std:
Por defecto, Rust tiene un conjunto de elementos definidos en la biblioteca estándar que importa automáticamente al ámbito de cada programa. Este conjunto se llama prelude, y usted puede ver todos sus elementos en https://doc.rust-lang.org/std/prelude/index.html.
Si un tipo que se desea utilizar no está en prelude, se debe importar ese tipo explícitamente al ámbito del programa con una sentencia use. Al utilizar la biblioteca std::io se obtienen una serie de características útiles, incluida la capacidad de aceptar la entrada del usuario.
Como vimos en el Capítulo 1, la función main es el punto de entrada al programa:
La sintaxis fn declara una nueva función; los paréntesis, (), indican que no hay parámetros; y la llave, {, inicia el cuerpo de la función.
Como también aprendió en el Capítulo 1, println! es una macro que imprime una cadena en pantalla:
Este código imprime un mensaje que indica de qué trata el juego y solicita la entrada al usuario.
A continuación, creamos una variable para almacenar la entrada del usuario, de la siguiente manera:
¡Ahora el programa se está poniendo interesante! Es mucho lo que sucede en esta corta línea. Usamos la sentencia let para crear la variable. Aquí tiene otro ejemplo:
Esta línea crea una nueva variable llamada apples y la vincula al valor 5. En Rust, las variables son inmutables por defecto, lo que significa que una vez que le damos a la variable un valor, este no cambiará. Discutiremos este concepto en detalle en la sección «Variables y mutabilidad». Para hacer una variable mutable, añadimos mut antes del nombre de la variable:
NOTA
La sintaxis // inicia un comentario que continúa hasta el final de la línea. Rust ignora todo lo que se encuentra en los comentarios. Discutiremos los comentarios con más detalle en el Capítulo 3.
Volviendo al programa del juego de adivinanzas, usted ahora sabe que let mut guess introducirá una variable mutable llamada guess. El signo igual () le indica ahora a Rust que queremos asignar algo a la variable. A la derecha del signo igual se encuentra el valor al que guess está asignado, que es el resultado de llamar a String::new, una función que devuelve una nueva instancia de String. String es un tipo de cadena proporcionado por la biblioteca estándar codificada en UTF-8 y que puede crecer.
La sintaxis :: en la línea ::new indica que new es una función asociada del tipo String. Una función asociada es una función implementada en un tipo, en este caso String. Esta función new crea una nueva cadena vacía. Encontrará una función new en muchos tipos porque es un nombre común para una función que crea un nuevo valor de algún tipo.
Recuerde que incluimos la funcionalidad de entrada/salida de la biblioteca estándar con use std::io; en la primera línea del programa. Ahora llamaremos a la función stdin del módulo io, que nos permitirá manejar la entrada del usuario:
Si no hubiéramos importado la biblioteca io con use std::io; al principio del programa, aún podríamos usar la función escribiendo esta llamada a función como std::io::stdin. La función stdin devuelve una instancia de std::io::Stdin, que es un tipo que representa al handle (manejador, referenciador) de la entrada estándar para el terminal.
A continuación, la línea .read_line(&mut guess) llama al método read_line en el handle de la entrada estándar para obtener la entrada del usuario. También pasamos &mut guess como argumento a read_line