diff --git a/bun.lockb b/bun.lockb index 941e7d4..ce76895 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/content/2.basic/index.md b/content/2.basic/0.index.md similarity index 100% rename from content/2.basic/index.md rename to content/2.basic/0.index.md diff --git a/content/2.basic/1.syntax.yml b/content/2.basic/1.syntax.yml new file mode 100644 index 0000000..6041ee8 --- /dev/null +++ b/content/2.basic/1.syntax.yml @@ -0,0 +1,14 @@ +title: 'Sintaxis Básica' +description: '' +data: + type: 'transparent' + topicLevel: 'start' + position: + x: -700 + y: 200 + width: 320 + align: 'center' + sourcePosition: + none: 'top' + targetPosition: + basic: 'right' diff --git a/content/2.basic/10.transfer-ownership.md b/content/2.basic/10.transfer-ownership.md new file mode 100644 index 0000000..85dda6f --- /dev/null +++ b/content/2.basic/10.transfer-ownership.md @@ -0,0 +1,105 @@ +--- +title: 'Transferencia de Ownership' +description: 'Transferencias de Ownership en Rust: Un Cambio Fundamental en la Gestión de Recursos' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: -700 + y: 600 + width: 320 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch04-01-what-is-ownership?highlight=transferencia#valores-de-retorno-y-alcance' +--- +## Transferencias de Ownership en Rust: Un Cambio Fundamental en la Gestión de Recursos + +### Introducción + +Rust es un lenguaje de programación que ha capturado la atención de desarrolladores por su enfoque único en la seguridad y el rendimiento. Un aspecto crucial que diferencia a Rust de otros lenguajes es su sistema de **ownership (propiedad)**. La **transferencia de ownership** es una característica clave de este sistema, que permite manejar la memoria de manera segura y eficiente. En este post, exploraremos en detalle cómo funcionan las transferencias de ownership en Rust y por qué son fundamentales para el paradigma de gestión de recursos del lenguaje. + +### Concepto de Ownership en Rust + +En Rust, cada valor en el programa tiene un único propietario. Esta variable propietaria es responsable de liberar los recursos asociados cuando sale del alcance (scope). Este enfoque asegura que no haya duplicaciones de liberación de memoria ni fugas de memoria, proporcionando una seguridad robusta en tiempo de compilación. + +### ¿Qué es la Transferencia de Ownership? + +La transferencia de ownership ocurre cuando un valor se mueve de una variable a otra, transfiriendo con ello la responsabilidad de gestionar ese valor. En Rust, esto se conoce como "mover" (move). Una vez que un valor ha sido movido, la variable original ya no puede usarse para acceder al valor, evitando así accesos inválidos a memoria. + +### Ejemplo de Transferencia de Ownership + +Consideremos el siguiente ejemplo para ilustrar cómo funciona la transferencia de ownership: + +```rust +fn main() { + let s1 = String::from("Hello"); + let s2 = s1; // s1 se mueve a s2 + println!("{}", s2); // Esto funciona + // println!("{}", s1); // Esto causaría un error de compilación +} +``` + +En este código, la cadena `"Hello"` se asigna a `s1`. Luego, `s1` se mueve a `s2`, transfiriendo la propiedad. Intentar usar `s1` después de la transferencia causará un error de compilación porque `s1` ya no es válido. + +### Transferencias de Ownership en Funciones + +Las transferencias de ownership también ocurren cuando se pasan parámetros a funciones y cuando se retornan valores desde funciones. Veamos un ejemplo: + +```rust +fn main() { + let s1 = String::from("Hello"); + takes_ownership(s1); // s1 se mueve a la función + // println!("{}", s1); // Esto causaría un error de compilación +} + +fn takes_ownership(some_string: String) { + println!("{}", some_string); +} +``` + +En este caso, `s1` se mueve a la función `takes_ownership`, transfiriendo la propiedad. Una vez que `s1` ha sido movido, ya no puede usarse en `main`. + +### Retorno de Valores y Ownership + +Rust también permite transferir ownership cuando se retorna un valor desde una función: + +```rust +fn main() { + let s1 = gives_ownership(); + println!("{}", s1); +} + +fn gives_ownership() -> String { + let some_string = String::from("Hello"); + some_string // se mueve al llamador +} +``` + +Aquí, la función `gives_ownership` crea una cadena y luego la retorna, transfiriendo la propiedad al llamador. + +### Clonación: Copias en Lugar de Movimientos + +En algunos casos, es posible que desees hacer una copia en lugar de mover un valor. Rust permite esto mediante el método `clone`, que crea una copia profunda del valor: + +```rust +fn main() { + let s1 = String::from("Hello"); + let s2 = s1.clone(); // Se clona s1 + println!("{}", s1); // Esto funciona + println!("{}", s2); // Esto también funciona +} +``` + +### Beneficios de la Transferencia de Ownership + +1. **Seguridad en Tiempo de Compilación:** Al transferir ownership de manera explícita, Rust asegura que no haya accesos inválidos a memoria ni liberaciones duplicadas. + +2. **Control de Recursos:** La transferencia de ownership proporciona un control preciso sobre la vida útil de los recursos, lo que es crucial para el rendimiento y la eficiencia. + +3. **Prevención de Errores Comunes:** Errores como las fugas de memoria y las condiciones de carrera se evitan gracias a las reglas de ownership y borrowing. + +### Conclusión + +La transferencia de ownership es un concepto central en Rust que redefine cómo gestionamos la memoria y los recursos. Al asegurar que cada valor tenga un único propietario y al validar las transferencias de ownership en tiempo de compilación, Rust proporciona un nivel de seguridad y eficiencia que es difícil de alcanzar en otros lenguajes. Aunque puede requerir un cambio de mentalidad para los desarrolladores acostumbrados a otros paradigmas, los beneficios que ofrece en términos de seguridad y control hacen que valga la pena adoptar este enfoque. diff --git a/content/2.basic/11.reference-and-mutability.md b/content/2.basic/11.reference-and-mutability.md new file mode 100644 index 0000000..8fe847c --- /dev/null +++ b/content/2.basic/11.reference-and-mutability.md @@ -0,0 +1,184 @@ +--- +title: 'Referencias y Mutabilidad' +description: 'Referencias y Mutabilidad en Rust: Un Enfoque Seguro para la Gestión de Datos' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: -700 + y: 640 + width: 320 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch04-02-references-and-borrowing' +--- +## Referencias y Mutabilidad en Rust: Un Enfoque Seguro para la Gestión de Datos + +### Introducción + +Rust es un lenguaje de programación que se destaca por su enfoque en la seguridad y la eficiencia. Dos conceptos fundamentales que contribuyen a estos objetivos son las **referencias** y la **mutabilidad**. En este post, exploraremos cómo Rust maneja las referencias y la mutabilidad, y cómo estos conceptos trabajan juntos para proporcionar un sistema de gestión de memoria seguro y eficiente. + +### Referencias: Acceso Seguro a los Datos + +En Rust, una **referencia** es un tipo que permite acceder a los datos sin tomar la propiedad de ellos. Las referencias se crean utilizando el operador `&`, y pueden ser inmutables o mutables. + +#### Referencias Inmutables + +Una referencia inmutable permite leer los datos pero no modificarlos. Se crean utilizando `&`: + +```rust +fn main() { + let s = String::from("Hello"); + let len = calculate_length(&s); // Se pasa una referencia inmutable + println!("La longitud de '{}' es {}.", s, len); +} + +fn calculate_length(s: &String) -> usize { + s.len() // Solo lectura +} +``` + +**Gráfico 1: Referencia Inmutable** + +```plaintext + +-------+ +----------------------+ + | s |-----> | "Hello" | + +-------+ +----------------------+ + | + v + +-------+ +----------------------+ + | len | | calculate_length(&s) | + +-------+ +----------------------+ +``` + +#### Referencias Mutables + +Una referencia mutable permite tanto leer como modificar los datos. Se crean utilizando `&mut`: + +```rust +fn main() { + let mut s = String::from("Hello"); + change(&mut s); // Se pasa una referencia mutable + println!("{}", s); +} + +fn change(s: &mut String) { + s.push_str(", world"); // Modifica la cadena +} +``` + +**Gráfico 2: Referencia Mutable** + +```plaintext + +-----------+ +----------------------+ + | s |-----> | "Hello" | + +-----------+ +----------------------+ + | + v + +-----------+ +----------------------+ + | change | | change(&mut s) | + +-----------+ | "Hello, world" | +``` + +### Reglas de las Referencias + +Rust aplica estrictas reglas para el uso de referencias, garantizando la seguridad y evitando condiciones de carrera: + +1. **Solo una referencia mutable a la vez:** No puede haber más de una referencia mutable a un dato en un momento dado. +2. **No se permiten referencias mutables mientras existan referencias inmutables:** Un dato no puede tener una referencia mutable si existe alguna referencia inmutable activa. + +#### Ejemplo de Violación de Reglas + +Intentar violar estas reglas resultará en un error de compilación. Por ejemplo: + +```rust +fn main() { + let mut s = String::from("Hello"); + let r1 = &s; // Referencia inmutable + let r2 = &s; // Otra referencia inmutable + let r3 = &mut s; // Error: no se puede tener una referencia mutable mientras existan referencias inmutables +} +``` + +**Gráfico 3: Violación de Reglas de Referencia** + +```plaintext + +-----------+ +----------------------+ + | s |-----> | "Hello" | + +-----------+ +----------------------+ + | | + | +-> r1 (inmutable) + | + +-> r2 (inmutable) + | + +-> r3 (mutable) - Error! +``` + +### Beneficios de las Reglas de Referencias + +1. **Seguridad en Tiempo de Compilación:** Las reglas de referencias de Rust aseguran que no haya accesos concurrentes inseguros a los datos, eliminando condiciones de carrera. +2. **Control de la Mutabilidad:** Al restringir la mutabilidad a una única referencia a la vez, Rust evita modificaciones no controladas y mantiene la integridad de los datos. +3. **Prevención de Errores Comunes:** Muchos errores comunes en la programación, como los punteros colgantes y los accesos a memoria no válida, se previenen mediante estas reglas. + +### Ejemplos Prácticos de Uso de Referencias y Mutabilidad + +#### Ejemplo 1: Contador de Referencias + +Un contador de referencias puede beneficiarse de las reglas de referencias de Rust para mantener un conteo seguro: + +```rust +fn main() { + let count = 5; + let r1 = &count; + let r2 = &count; + + println!("r1: {}, r2: {}", r1, r2); +} +``` + +**Gráfico 4: Contador de Referencias** + +```plaintext + +---------+ +-------------+ + | count |-----> | 5 | + +---------+ +-------------+ + | | + | +-> r1 (inmutable) + | + +-> r2 (inmutable) +``` + +#### Ejemplo 2: Modificación Controlada + +Controlar la modificación de un valor en una función sin transferir la propiedad: + +```rust +fn main() { + let mut x = 10; + add_five(&mut x); + println!("x: {}", x); +} + +fn add_five(n: &mut i32) { + *n += 5; +} +``` + +**Gráfico 5: Modificación Controlada** + +```plaintext + +---------+ +-------------+ + | x |-----> | 10 | + +---------+ +-------------+ + | + v + +---------+ +-------------+ + | add_five| | 10 + 5 | + +---------+ | 15 | +``` + +### Conclusión + +Las referencias y la mutabilidad son conceptos esenciales en Rust que proporcionan un enfoque seguro y eficiente para la gestión de datos. Al aplicar estrictas reglas en tiempo de compilación, Rust garantiza la seguridad de la memoria y previene errores comunes en la programación. Estos mecanismos permiten a los desarrolladores escribir código robusto y libre de condiciones de carrera, mejorando la calidad y fiabilidad de las aplicaciones. Adoptar y comprender estos conceptos es crucial para aprovechar al máximo las capacidades de Rust y crear software seguro y eficiente. diff --git a/content/2.basic/12.lifetimes.md b/content/2.basic/12.lifetimes.md new file mode 100644 index 0000000..e29cec8 --- /dev/null +++ b/content/2.basic/12.lifetimes.md @@ -0,0 +1,213 @@ +--- +title: 'Tiempos de Vida (Lifetime)' +description: 'Comprendiendo los Lifetimes en Rust: Casos de Uso y Ejemplos Prácticos' +draft: true +data: + type: 'custom' + topicLevel: 'medium' + position: + x: -700 + y: 680 + width: 320 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch10-03-lifetime-syntax' + - name: 'El Tipo Slice' + english: false + link: 'https://book.rustlang-es.org/ch04-03-slices' +--- +## Comprendiendo los Lifetimes en Rust: Casos de Uso y Ejemplos Prácticos + +### Introducción + +Rust es un lenguaje de programación que pone un fuerte énfasis en la seguridad y la gestión eficiente de memoria. Uno de los conceptos más avanzados y, a veces, desafiantes de Rust es el de los **lifetimes** (tiempos de vida). Los lifetimes garantizan que las referencias sean siempre válidas y no apunten a datos que ya han sido liberados. En este blog post, exploraremos el concepto de lifetimes, sus usos y algunos ejemplos prácticos para comprender mejor cómo funcionan. + +### ¿Qué son los Lifetimes? + +Un lifetime es una anotación que le dice al compilador cuánto tiempo debe ser válida una referencia. En Rust, los lifetimes se denotan utilizando apóstrofos (`'`) seguidos de un nombre, como `'a`. El compilador usa estas anotaciones para asegurar que no haya referencias colgantes o inválidas. + +### Sintaxis Básica de Lifetimes + +La sintaxis básica para definir lifetimes en funciones es la siguiente: + +```rust +fn example<'a>(input: &'a str) -> &'a str { + input +} +``` + +En este ejemplo, `'a` es un lifetime que asegura que la referencia de entrada `input` y la referencia de salida comparten el mismo tiempo de vida. + +### Casos de Uso de Lifetimes + +#### 1. Funciones con Referencias + +Cuando se pasan referencias a una función, los lifetimes aseguran que las referencias sean válidas mientras se usen dentro de la función. + +```rust +fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { + if x.len() > y.len() { + x + } else { + y + } +} +``` + +**Gráfico 1: Función con Lifetimes** + +```plaintext + +------+ +-----+ +---------+ + | x |---'a---------> | | | | + +------+ | | | longest | + | y |---'a---------> | | | | + +------+ | | +---------+ + +-----+ | + | |<-----------' + +-----+ +``` + +En este ejemplo, `longest` toma dos referencias con el mismo lifetime `'a` y retorna una referencia con el mismo lifetime, asegurando que la referencia retornada no viva más allá de las referencias de entrada. + +#### 2. Structs con Referencias + +Cuando se definen structs que contienen referencias, es necesario especificar los lifetimes de esas referencias. + +```rust +struct ImportantExcerpt<'a> { + part: &'a str, +} + +fn main() { + let novel = String::from("Call me Ishmael. Some years ago..."); + let first_sentence = novel.split('.').next().expect("Could not find a '.'"); + let i = ImportantExcerpt { part: first_sentence }; + println!("{}", i.part); +} +``` + +**Gráfico 2: Struct con Lifetimes** + +```plaintext + +--------+ + | novel |--'a--> "Call me Ishmael. Some years ago..." + +--------+ | + v + +--------+ + | i |--'a--> "Call me Ishmael" + +--------+ +``` + +Aquí, `ImportantExcerpt` contiene una referencia que debe vivir al menos tanto como la instancia de `ImportantExcerpt`. + +#### 3. Métodos y Lifetimes + +Al definir métodos para structs que contienen referencias, también es necesario especificar los lifetimes. + +```rust +impl<'a> ImportantExcerpt<'a> { + fn level(&self) -> i32 { + 3 + } +} +``` + +**Gráfico 3: Métodos con Lifetimes** + +```plaintext + +--------+ +---------+ + | self |--'a--> | level() | + +--------+ +---------+ +``` + +El método `level` toma una referencia a `self` y retorna un valor entero, sin necesidad de especificar lifetimes adicionales porque no está retornando una referencia. + +#### 4. Referencias en Funciones y Retornos + +Las funciones que aceptan y retornan referencias deben especificar los lifetimes para asegurar que las referencias retornadas sean válidas. + +```rust +fn first_word<'a>(s: &'a str) -> &'a str { + let bytes = s.as_bytes(); + for (i, &item) in bytes.iter().enumerate() { + if item == b' ' { + return &s[0..i]; + } + } + &s[..] +} +``` + +**Gráfico 4: Referencias en Funciones** + +```plaintext + +------+ +-----+ +-------------+ + | s |---'a---------> | | | first_word | + +------+ | | +-------------+ + +-----+ | + | |<-----------' + +-----+ +``` + +### 5. Uso de Slices + +Los slices son referencias a una porción continua de una colección, como un array o una cadena. Los slices también deben cumplir con las reglas de los lifetimes para asegurar que las referencias sean válidas. + +```rust +fn main() { + let arr = [1, 2, 3, 4, 5]; + let slice = &arr[1..3]; + println!("{:?}", slice); // [2, 3] +} +``` + +**Gráfico 5: Uso de Slices** + +```plaintext + +-------+-------+-------+-------+-------+ + | arr | | | | | + | [1, 2,| 3, 4,| 5] | | | + +-------+-------+-------+-------+-------+ + | | + | +-----> +-------+ + | | slice |---> [2, 3] + +----------> +-------+ +``` + +En este ejemplo, `slice` es una referencia a una porción del array `arr`. El lifetime de `slice` debe ser igual o menor al lifetime de `arr`. + +### Casos Avanzados: Lifetimes en Tipos Genéricos + +Los lifetimes también pueden aplicarse a tipos genéricos, proporcionando una mayor flexibilidad y control sobre las referencias en estructuras más complejas. + +```rust +struct Pair<'a, T> { + first: &'a T, + second: &'a T, +} + +impl<'a, T> Pair<'a, T> { + fn new(first: &'a T, second: &'a T) -> Self { + Pair { first, second } + } +} +``` + +**Gráfico 6: Lifetimes en Tipos Genéricos** + +```plaintext + +--------+ + | first |---'a----+ + +--------+ | + v + +--------+ + | Pair | + +--------+ +--------+ + | second |---'a----+ + +--------+ +``` + +### Conclusión + +Los lifetimes son una característica poderosa de Rust que garantizan la seguridad y la eficiencia de las referencias. Aunque pueden ser complejos al principio, comprender y utilizar correctamente los lifetimes es crucial para escribir código Rust seguro y eficiente. Con la práctica y la familiaridad, los lifetimes se vuelven una herramienta invaluable en el arsenal de un desarrollador de Rust, permitiendo un control preciso sobre la validez y la duración de las referencias en el programa. diff --git a/content/2.basic/13.temporary-borrow.md b/content/2.basic/13.temporary-borrow.md new file mode 100644 index 0000000..f38ac86 --- /dev/null +++ b/content/2.basic/13.temporary-borrow.md @@ -0,0 +1,192 @@ +--- +title: 'Préstamos Temporales' +description: 'Comprendiendo los Préstamos Temporales en Rust: Gestión Segura de Memoria y Concurrencia' +draft: true +data: + type: 'custom' + topicLevel: 'medium' + position: + x: -700 + y: 720 + width: 320 +--- +## Comprendiendo los Préstamos Temporales en Rust: Gestión Segura de Memoria y Concurrencia + +### Introducción + +Rust es un lenguaje conocido por su enfoque en la seguridad y la eficiencia en la gestión de memoria. Uno de los conceptos clave que Rust utiliza para garantizar la seguridad es el de los **préstamos temporales**. Los préstamos temporales permiten a los desarrolladores tomar prestadas referencias a datos sin transferir la propiedad, garantizando que no ocurran condiciones de carrera ni referencias inválidas. En este blog post, exploraremos qué son los préstamos temporales, cómo funcionan y cómo pueden ser utilizados de manera efectiva en Rust. + +### ¿Qué son los Préstamos Temporales? + +En Rust, los préstamos temporales se refieren al acto de tomar prestadas referencias a datos, ya sea de forma mutable o inmutable, por un tiempo limitado sin transferir la propiedad de los datos. El concepto se basa en dos tipos principales de préstamos: + +1. **Préstamos Inmutables** (`&T`): Permiten leer los datos pero no modificarlos. +2. **Préstamos Mutables** (`&mut T`): Permiten tanto leer como modificar los datos. + +### Préstamos Inmutables + +Un préstamo inmutable permite tomar una referencia a un dato para leerlo sin modificarlo. Se crean utilizando el operador `&`. + +#### Ejemplo de Préstamo Inmutable + +```rust +fn main() { + let data = String::from("Hello, Rust!"); + let len = calculate_length(&data); // Préstamo inmutable + println!("La longitud de '{}' es {}.", data, len); +} + +fn calculate_length(s: &String) -> usize { + s.len() // Solo lectura +} +``` + +**Gráfico 1: Préstamo Inmutable** + +```plaintext + +--------+ +----------------------+ + | data |-----> | "Hello, Rust!" | + +--------+ +----------------------+ + | + v + +--------+ +----------------------+ + | len | | calculate_length(&s) | + +--------+ +----------------------+ +``` + +En este ejemplo, la función `calculate_length` toma prestada una referencia inmutable a `data`. Esto permite leer el valor de `data` sin transferir su propiedad ni modificarlo. + +### Préstamos Mutables + +Un préstamo mutable permite tomar una referencia a un dato para leerlo y modificarlo. Se crean utilizando el operador `&mut`. + +#### Ejemplo de Préstamo Mutable + +```rust +fn main() { + let mut data = String::from("Hello"); + append_world(&mut data); // Préstamo mutable + println!("{}", data); +} + +fn append_world(s: &mut String) { + s.push_str(", world!"); // Modificación +} +``` + +**Gráfico 2: Préstamo Mutable** + +```plaintext + +------------+ +----------------------+ + | data |-----> | "Hello" | + +------------+ +----------------------+ + | + v + +------------+ +----------------------+ + | append_world | | append_world(&mut s) | + +------------+ | "Hello, world!" | +``` + +En este ejemplo, la función `append_world` toma prestada una referencia mutable a `data`, permitiendo modificar el valor de `data` sin transferir su propiedad. + +### Reglas de los Préstamos + +Rust aplica reglas estrictas para los préstamos temporales para garantizar la seguridad y evitar condiciones de carrera: + +1. **Múltiples Préstamos Inmutables:** Se pueden tener múltiples referencias inmutables a un dato simultáneamente. +2. **Un Solo Préstamo Mutable:** Solo se puede tener una referencia mutable a un dato en un momento dado. +3. **No Se Permiten Préstamos Mutables e Inmutables Simultáneamente:** No se puede tener una referencia mutable si existen referencias inmutables activas. + +#### Ejemplo de Violación de Reglas de Préstamo + +Intentar violar estas reglas resultará en un error de compilación. Por ejemplo: + +```rust +fn main() { + let mut data = String::from("Hello"); + let r1 = &data; // Préstamo inmutable + let r2 = &data; // Otro préstamo inmutable + let r3 = &mut data; // Error: no se puede tener un préstamo mutable mientras existan préstamos inmutables +} +``` + +**Gráfico 3: Violación de Reglas de Préstamo** + +```plaintext + +--------+ +----------------------+ + | data |-----> | "Hello" | + +--------+ +----------------------+ + | | + | +-> r1 (inmutable) + | + +-> r2 (inmutable) + | + +-> r3 (mutable) - Error! +``` + +### Beneficios de los Préstamos Temporales + +1. **Seguridad en Tiempo de Compilación:** Las reglas de préstamos de Rust aseguran que no haya accesos concurrentes inseguros a los datos, eliminando condiciones de carrera. +2. **Control de la Mutabilidad:** Al restringir la mutabilidad a una única referencia a la vez, Rust evita modificaciones no controladas y mantiene la integridad de los datos. +3. **Prevención de Errores Comunes:** Muchos errores comunes en la programación, como los punteros colgantes y los accesos a memoria no válida, se previenen mediante estas reglas. + +### Casos Prácticos de Uso de Préstamos Temporales + +#### Ejemplo 1: Compartir Datos para Lectura + +Cuando varios componentes de un programa necesitan leer el mismo dato, se pueden utilizar préstamos inmutables. + +```rust +fn main() { + let data = String::from("Shared data"); + let r1 = &data; + let r2 = &data; + + println!("r1: {}, r2: {}", r1, r2); +} +``` + +**Gráfico 4: Compartir Datos para Lectura** + +```plaintext + +--------+ +----------------------+ + | data |-----> | "Shared data" | + +--------+ +----------------------+ + | | + | +-> r1 (inmutable) + | + +-> r2 (inmutable) +``` + +#### Ejemplo 2: Modificación Controlada + +Controlar la modificación de un valor en una función sin transferir la propiedad: + +```rust +fn main() { + let mut x = 10; + add_five(&mut x); + println!("x: {}", x); +} + +fn add_five(n: &mut i32) { + *n += 5; +} +``` + +**Gráfico 5: Modificación Controlada** + +```plaintext + +---------+ +-------------+ + | x |-----> | 10 | + +---------+ +-------------+ + | + v + +---------+ +-------------+ + | add_five| | 10 + 5 | + +---------+ | 15 | +``` + +### Conclusión + +Los préstamos temporales son una característica esencial de Rust que permite la gestión segura y eficiente de la memoria. Al seguir estrictas reglas para el uso de préstamos inmutables y mutables, Rust garantiza la seguridad de la memoria y previene errores comunes en la programación. Comprender y utilizar correctamente los préstamos temporales es crucial para escribir código Rust seguro y eficiente. Con la práctica, los desarrolladores pueden aprovechar estos conceptos para crear aplicaciones robustas y libres de errores. diff --git a/content/2.basic/14.primitive-types.yml b/content/2.basic/14.primitive-types.yml new file mode 100644 index 0000000..3611704 --- /dev/null +++ b/content/2.basic/14.primitive-types.yml @@ -0,0 +1,14 @@ +title: 'Tipos de Datos Primitivos' +description: '' +data: + type: 'transparent' + topicLevel: 'start' + position: + x: -50 + y: 240 + width: 320 + align: 'left' + sourcePosition: + none: 'top' + targetPosition: + basic: 'left' diff --git a/content/2.basic/15.boolean.md b/content/2.basic/15.boolean.md new file mode 100644 index 0000000..32846f7 --- /dev/null +++ b/content/2.basic/15.boolean.md @@ -0,0 +1,175 @@ +--- +title: 'bool' +description: 'El Tipo Booleano en Rust: Características, Operaciones y Usos Prácticos' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: -50 + y: 280 + # width: 320 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch03-02-data-types#el-tipo-booleano' + - name: 'Libro de Referencia Rust' + english: true + link: 'https://doc.rust-lang.org/reference/types/boolean.html' +--- +## El Tipo Booleano en Rust: Características, Operaciones y Usos Prácticos + +El tipo `bool` en Rust representa un valor booleano, que puede ser verdadero (`true`) o falso (`false`). Es uno de los tipos más fundamentales en cualquier lenguaje de programación y se utiliza ampliamente en Rust para controlar el flujo de ejecución y tomar decisiones basadas en condiciones. En este post, exploraremos las características principales del tipo `bool`, su funcionamiento en memoria, y algunos usos prácticos, incluyendo operaciones lógicas y comparaciones. + +### Características Principales del Tipo `bool` + +1. **Valores:** El tipo `bool` puede tener solo dos valores: `true` o `false`. + +2. **Tamaño en Memoria:** En Rust, un valor `bool` ocupa un solo byte en la memoria. Aunque solo necesita un bit para almacenar `true` o `false`, utilizar un byte completo simplifica la alineación en memoria y la manipulación de datos a nivel de hardware. + +3. **Operaciones Lógicas:** Los valores `bool` se pueden combinar mediante operaciones lógicas como AND (`&&`), OR (`||`), y NOT (`!`). Estas operaciones son fundamentales para la evaluación de expresiones booleanas. + +4. **Comparaciones:** Los valores `bool` se pueden comparar utilizando los operadores de comparación (`==`, `!=`, `<`, `>`, `<=`, `>=`). Estas comparaciones ayudan a evaluar la igualdad o el orden relativo de los valores booleanos. + +5. **Uso en Expresiones Condicionales:** Los valores `bool` se utilizan comúnmente en expresiones condicionales como las declaraciones `if`, `while`, y `for` para controlar el flujo de ejecución del programa. + +### Ejemplos de Uso del Tipo `bool` en Rust + +#### 1. Declaraciones Condicionales + +```rust +let es_mayor_de_edad = true; + +if es_mayor_de_edad { + println!("Puede comprar alcohol"); +} else { + println!("No puede comprar alcohol"); +} +``` + +**Gráfico 1: Uso de `bool` en Declaraciones Condicionales** + +```plaintext +let es_mayor_de_edad = true; + | + v + +------------+ + | true | + +------------+ + | + v +if es_mayor_de_edad { + println!("Puede comprar alcohol"); +} else { + println!("No puede comprar alcohol"); +} +``` + +#### 2. Operaciones Lógicas + +```rust +let tiene_licencia = true; +let tiene_coche = false; + +if tiene_licencia && tiene_coche { + println!("Puede conducir"); +} else { + println!("No puede conducir"); +} +``` + +**Gráfico 2: Operaciones Lógicas con `bool`** + +```plaintext +let tiene_licencia = true; +let tiene_coche = false; + | | + v v + +------------+ +------------+ + | true | | false | + +------------+ +------------+ + | | + +------AND-------+ + | + v + if tiene_licencia && tiene_coche { + println!("Puede conducir"); + } else { + println!("No puede conducir"); + } +``` + +#### 3. Comparaciones + +```rust +let a = true; +let b = false; + +if a != b { + println!("Los valores son diferentes"); +} else { + println!("Los valores son iguales"); +} +``` + +**Gráfico 3: Comparaciones con `bool`** + +```plaintext +let a = true; +let b = false; + | | + v v + +------------+ +------------+ + | true | | false | + +------------+ +------------+ + | | + +----!=--+ + | + v + if a != b { + println!("Los valores son diferentes"); + } else { + println!("Los valores son iguales"); + } +``` + +### Métodos Más Utilizados + +En Rust, el tipo `bool` es primitivo y no tiene métodos específicos asociados. Sin embargo, se pueden utilizar funciones y métodos proporcionados por Rust para trabajar con valores booleanos. Algunas de las funciones y métodos más comunes son: + +- **true y false:** Valores de tipo `bool`. + +- **not():** Método utilizado para negar un valor booleano. + +- **and():** Método utilizado para realizar una operación lógica AND entre dos valores booleanos. + +- **or():** Método utilizado para realizar una operación lógica OR entre dos valores booleanos. + +#### Ejemplo de Métodos Booleanos + +```rust +fn main() { + if false.and(true).or(false) { + println!("La expresión es verdadera"); + } else { + println!("La expresión es falsa"); + } +} +``` + +**Gráfico 4: Métodos Booleanos** + +```plaintext +false.and(true) --> false +false.or(false) --> false + +if false.and(true).or(false) { + println!("La expresión es verdadera"); +} else { + println!("La expresión es falsa"); +} +``` + +### Conclusión + +El tipo `bool` en Rust, aunque simple, es esencial para el control del flujo de ejecución y la toma de decisiones en los programas. Su implementación del trait `Copy` permite copiar valores booleanos de manera eficiente, y su representación en memoria es directa y eficaz. Además, su uso en operaciones lógicas y comparaciones es fundamental para evaluar y combinar condiciones. Comprender y utilizar correctamente el tipo `bool` nos ayuda a escribir código Rust más seguro y eficiente. diff --git a/content/2.basic/16.float.md b/content/2.basic/16.float.md new file mode 100644 index 0000000..163f2f9 --- /dev/null +++ b/content/2.basic/16.float.md @@ -0,0 +1,133 @@ +--- +title: 'f32, f64' +description: 'Los Tipos de Punto Flotante `f32` y `f64` en Rust' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: 24 + y: 280 + width: 130 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch03-02-data-types#tipos-de-punto-flotante' +--- +## Los Tipos de Punto Flotante `f32` y `f64` en Rust + +En Rust, los tipos `f32` y `f64` son tipos de datos de punto flotante que representan números de coma flotante de precisión simple y doble, respectivamente. Estos tipos son esenciales para realizar cálculos numéricos que requieren decimales, y cada uno tiene sus propias características y usos. En este post, exploraremos las diferencias entre `f32` y `f64`, sus aplicaciones típicas y algunos ejemplos de uso en Rust. + +### Diferencias entre `f32` y `f64` + +#### 1. Precisión + +La principal diferencia entre `f32` y `f64` es la precisión: +- `f32` utiliza 32 bits para representar un número de coma flotante. +- `f64` utiliza 64 bits para representar un número de coma flotante. + +Debido a esto, `f64` proporciona una mayor precisión y un rango más amplio de valores que `f32`. + +#### 2. Rango de Valores + +- **`f32`:** Tiene un rango de valores aproximado de ±3.40282347e+38 y una precisión de aproximadamente 7 dígitos decimales. +- **`f64`:** Tiene un rango de valores aproximado de ±1.7976931348623157e+308 y una precisión de aproximadamente 15 dígitos decimales. + +#### 3. Rendimiento + +En términos de rendimiento, no existe una diferencia significativa entre `f32` y `f64` en la mayoría de los procesadores modernos, ya que ambos utilizan los mismos algoritmos y métodos de procesamiento. Sin embargo, `f32` puede ser preferible en aplicaciones donde el uso de memoria es una preocupación importante. + +### Usos de `f32` y `f64` + +- **`f32`:** Se utiliza cuando se requiere una menor precisión y se desea ahorrar memoria, como en gráficos de juegos, cálculos científicos con menos precisión o aplicaciones de bajo consumo de recursos. +- **`f64`:** Se utiliza cuando se requiere una mayor precisión, como en cálculos financieros, ingeniería de precisión o aplicaciones científicas donde se necesitan valores muy precisos. + +### Ejemplos de Uso en Rust + +```rust +fn main() { + let x: f32 = 3.14; // Declaración de un valor f32 + let y: f64 = 3.14; // Declaración de un valor f64 + + println!("Valor de x: {}", x); + println!("Valor de y: {}", y); + + // Operaciones aritméticas + let suma = x as f64 + y; + let resta = x as f64 - y; + let multiplicacion = x as f64 * y; + let division = x as f64 / y; + + println!("Suma: {}", suma); + println!("Resta: {}", resta); + println!("Multiplicación: {}", multiplicacion); + println!("División: {}", division); +} +``` + +**Gráfico 1: Ejemplo de Operaciones con `f32` y `f64`** + +```plaintext +let x: f32 = 3.14; // 32-bit float +let y: f64 = 3.14; // 64-bit float + + +-----+ +-------+ + | x |----->| 3.14 | + +-----+ +-------+ + f32 + + +-----+ +-------+ + | y |----->| 3.14 | + +-----+ +-------+ + f64 + + let suma = x as f64 + y; + let resta = x as f64 - y; + let multiplicacion = x as f64 * y; + let division = x as f64 / y; +``` + +### Métodos Más Utilizados + +Los tipos `f32` y `f64` no tienen métodos específicos asociados ya que son tipos primitivos en Rust. Sin embargo, se pueden utilizar funciones y métodos proporcionados por Rust para trabajar con valores de punto flotante. Algunas de las funciones y métodos más comunes son: + +- **`abs()`:** Devuelve el valor absoluto de un número. +- **`ceil()`:** Redondea hacia arriba al número entero más cercano. +- **`floor()`:** Redondea hacia abajo al número entero más cercano. +- **`round()`:** Redondea al número entero más cercano. +- **`sqrt()`:** Calcula la raíz cuadrada del número. + +#### Ejemplo de Uso de Métodos de Punto Flotante + +```rust +fn main() { + let z: f64 = -3.14; + + println!("Valor absoluto: {}", z.abs()); + println!("Redondeo hacia arriba: {}", z.ceil()); + println!("Redondeo hacia abajo: {}", z.floor()); + println!("Redondeo al entero más cercano: {}", z.round()); + println!("Raíz cuadrada de 9.0: {}", 9.0f64.sqrt()); +} +``` + +**Gráfico 2: Métodos Comunes para `f32` y `f64`** + +```plaintext +let z: f64 = -3.14; + + +-----+ +-------+ + | z |----->| -3.14 | + +-----+ +-------+ + f64 + + z.abs() -> 3.14 + z.ceil() -> -3.0 + z.floor() -> -4.0 + z.round() -> -3.0 + 9.0.sqrt() -> 3.0 +``` + +### Conclusión + +Los tipos de punto flotante `f32` y `f64` en Rust son esenciales para realizar cálculos numéricos precisos. La elección entre `f32` y `f64` depende de la precisión requerida y las restricciones de memoria de la aplicación. Con una comprensión clara de las diferencias y usos de cada uno, podemos tomar decisiones informadas al escribir código eficiente y preciso en Rust. diff --git a/content/2.basic/17.char.md b/content/2.basic/17.char.md new file mode 100644 index 0000000..1f7853a --- /dev/null +++ b/content/2.basic/17.char.md @@ -0,0 +1,109 @@ +--- +title: 'char' +description: 'El Tipo `char` en Rust' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: 158 + y: 280 + # width: 320 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch03-02-data-types#el-tipo-de-car%C3%A1cter' + - name: 'Ferrous System' + english: true + link: 'https://github.com/ferrous-systems/rust-training/blob/main/training-slides/src/basic-types.md#character' +--- +## El Tipo `char` en Rust + +El tipo `char` en Rust representa un solo carácter Unicode, que puede ser un carácter alfabético, numérico, un símbolo o un carácter especial. Este tipo es fundamental para la manipulación de texto en Rust. En este post, detallaremos las características del tipo `char`, sus usos, los métodos más comunes y cómo se maneja en memoria. + +### Características del Tipo `char` + +#### 1. Representación Unicode + +En Rust, los valores `char` se representan utilizando el estándar Unicode, lo que permite representar caracteres de cualquier lenguaje humano, así como una amplia variedad de símbolos y caracteres especiales. + +#### 2. Tamaño en Memoria + +Los valores `char` en Rust ocupan 4 bytes de memoria. Esto se debe a que están codificados en UTF-8, donde cada carácter Unicode se representa por hasta 4 bytes. Esta capacidad de almacenamiento permite manejar una amplia gama de caracteres Unicode. + +#### 3. Inmutabilidad + +Los valores `char` son inmutables, lo que significa que una vez que se asigna un valor a una variable `char`, este no se puede cambiar. + +### Usos del Tipo `char` + +- **Representación de Caracteres:** Se utiliza para representar caracteres individuales en texto, como letras, números, símbolos o caracteres especiales. +- **Procesamiento de Texto y Cadenas:** Es útil para manipular y procesar texto en programas, como contar caracteres, extraer subcadenas o realizar operaciones de búsqueda y reemplazo. + +### Métodos Más Comunes + +Aunque el tipo `char` en Rust no tiene métodos específicos asociados, se pueden realizar operaciones comunes utilizando funciones proporcionadas por Rust para trabajar con caracteres. Algunas de las funciones más comunes son: + +- **`is_alphabetic()`:** Devuelve `true` si el carácter es una letra alfabética. +- **`is_numeric()`:** Devuelve `true` si el carácter es un dígito numérico. +- **`to_uppercase()`:** Convierte el carácter a mayúsculas. +- **`to_lowercase()`:** Convierte el carácter a minúsculas. +- **`is_whitespace()`:** Devuelve `true` si el carácter es un espacio en blanco. +- **`is_ascii()`:** Devuelve `true` si el carácter es un carácter ASCII de 7 bits. + +### Ejemplo de Uso en Rust + +```rust +fn main() { + let ch: char = 'a'; + + if ch.is_alphabetic() { + println!("{} es una letra alfabética", ch); + } + + if ch.is_numeric() { + println!("{} es un dígito numérico", ch); + } + + println!("{} en mayúsculas es {}", ch, ch.to_uppercase()); + println!("{} en minúsculas es {}", ch.to_lowercase().next().unwrap(), ch.to_lowercase()); + println!("{} es un espacio en blanco: {}", ch, ch.is_whitespace()); + println!("{} es un carácter ASCII: {}", ch, ch.is_ascii()); +} +``` + +**Gráfico 1: Ejemplo de Uso del Tipo `char`** + +```plaintext +let ch: char = 'a'; + + +-----+ +---+ + | ch |----->| a | + +-----+ +---+ + char + + ch.is_alphabetic() -> true + ch.is_numeric() -> false + ch.to_uppercase() -> 'A' + ch.to_lowercase() -> 'a' + ch.is_whitespace() -> false + ch.is_ascii() -> true +``` + +### Espacio en Memoria + +Como se mencionó anteriormente, los valores `char` en Rust ocupan 4 bytes de memoria, ya que están codificados en UTF-8. Esto permite representar una amplia gama de caracteres Unicode con un solo valor `char`. La codificación UTF-8 también garantiza una compatibilidad completa con los estándares Unicode y la capacidad de representar texto en múltiples idiomas y scripts. + +**Gráfico 2: Espacio en Memoria del Tipo `char`** + +```plaintext ++---------------------+ +| 'a' | +| | +| 4 bytes (UTF-8) | ++---------------------+ +``` + +### Conclusión + +El tipo `char` en Rust es una herramienta poderosa para manejar caracteres individuales y realizar operaciones de procesamiento de texto. Su representación en Unicode y su capacidad para ocupar 4 bytes de memoria le permiten manejar una amplia gama de caracteres de diferentes idiomas y scripts. Comprender cómo utilizar y manipular el tipo `char` es esencial para escribir programas eficientes y robustos en Rust. diff --git a/content/2.basic/18.signed-integer.md b/content/2.basic/18.signed-integer.md new file mode 100644 index 0000000..9fb3e53 --- /dev/null +++ b/content/2.basic/18.signed-integer.md @@ -0,0 +1,144 @@ +--- +title: 'i8, i16, i32, i64, i128, isize' +description: 'Comprendiendo los Tipos Enteros con Signo en Rust' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: -50 + y: 320 + width: 280 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch03-02-data-types.html#tipos-de-enteros' + - name: 'Ferrous System' + english: true + link: 'https://github.com/ferrous-systems/rust-training/blob/main/training-slides/src/basic-types.md#integers' + - name: 'Ferrocene Language Specification' + english: true + link: 'https://public-docs.ferrocene.dev/main/specification/types-and-traits.html#integer-types' +--- +## Comprendiendo los Tipos Enteros con Signo en Rust + +En Rust, los tipos `i8`, `i16`, `i32`, `i64`, `i128` y `isize` son tipos de datos enteros con signo que representan números enteros con diferentes tamaños y rangos. Estos tipos son fundamentales para el manejo eficiente de números enteros en Rust. A continuación, se presenta una explicación detallada de cada uno, sus usos, métodos comunes y su espacio en memoria. + +### Tipos Enteros con Signo en Rust + +#### 1. `i8` +- **Descripción:** Representa números enteros con signo de 8 bits. +- **Rango de valores:** -128 a 127. +- **Espacio en memoria:** 1 byte. + +#### 2. `i16` +- **Descripción:** Representa números enteros con signo de 16 bits. +- **Rango de valores:** -32,768 a 32,767. +- **Espacio en memoria:** 2 bytes. + +#### 3. `i32` +- **Descripción:** Representa números enteros con signo de 32 bits. +- **Rango de valores:** -2,147,483,648 a 2,147,483,647. +- **Espacio en memoria:** 4 bytes. + +#### 4. `i64` +- **Descripción:** Representa números enteros con signo de 64 bits. +- **Rango de valores:** -9,223,372,036,854,775,808 a 9,223,372,036,854,775,807. +- **Espacio en memoria:** 8 bytes. + +#### 5. `i128` +- **Descripción:** Representa números enteros con signo de 128 bits. +- **Rango de valores:** -170,141,183,460,469,231,731,687,303,715,884,105,728 a 170,141,183,460,469,231,731,687,303,715,884,105,727. +- **Espacio en memoria:** 16 bytes. + +#### 6. `isize` +- **Descripción:** Representa números enteros con signo que tienen el tamaño de un puntero. Esto significa que su tamaño puede variar dependiendo de la arquitectura del sistema. +- **Rango de valores:** Depende de la arquitectura. +- **Espacio en memoria:** 4 bytes en sistemas de 32 bits, 8 bytes en sistemas de 64 bits. + +### Usos de los Tipos Enteros con Signo + +- **Almacenamiento de datos:** Se utilizan para almacenar números enteros con signo en diferentes rangos, dependiendo de la precisión requerida por la aplicación. +- **Operaciones aritméticas:** Se utilizan para realizar operaciones matemáticas como suma, resta, multiplicación y división en valores enteros. +- **Índices y tamaños de datos:** `isize` se utiliza comúnmente para índices y tamaños de datos en estructuras de datos, ya que su tamaño se ajusta automáticamente a la arquitectura del sistema. + +### Métodos Más Comunes + +Los tipos enteros en Rust no tienen métodos específicos asociados, pero se pueden utilizar funciones proporcionadas por Rust para realizar operaciones comunes. Algunas de las funciones más utilizadas son: + +- **`abs()`:** Devuelve el valor absoluto del número. +- **`to_string()`:** Convierte el número en una cadena de caracteres. +- **`overflowing_add()`:** Realiza una suma con desbordamiento silencioso, devolviendo una tupla con el resultado y un indicador de desbordamiento. +- **`wrapping_add()`:** Realiza una suma con desbordamiento modular, devolviendo el resultado con un desbordamiento envuelto. + +### Ejemplos de Uso en Rust + +```rust +fn main() { + let a: i32 = -42; + let b: i32 = 56; + + // Operaciones comunes + println!("Valor absoluto de a: {}", a.abs()); + println!("a como cadena: {}", a.to_string()); + println!("Número de bits en 1 en b: {}", b.count_ones()); + println!("Número de bits en 0 en b: {}", b.count_zeros()); + + let (sum, overflowed) = a.overflowing_add(b); + println!("Suma con posible desbordamiento: {} (desbordado: {})", sum, overflowed); + + let wrapped_sum = a.wrapping_add(b); + println!("Suma con desbordamiento envuelto: {}", wrapped_sum); +} +``` + +**Gráfico 1: Ejemplo de Operaciones Comunes con Enteros en Rust** + +```rust +let a: i32 = -42; +let b: i32 = 56; + +a.abs(); // -> 42 +a.to_string(); // -> "-42" +b.count_ones(); // -> 5 +b.count_zeros(); // -> 27 +a.overflowing_add(b) // -> (14, false) +a.wrapping_add(b) // -> 14 +``` + +### Espacio en Memoria + +El espacio en memoria ocupado por los tipos enteros depende del tamaño de cada tipo: + +- **`i8`:** 1 byte +- **`i16`:** 2 bytes +- **`i32`:** 4 bytes +- **`i64`:** 8 bytes +- **`i128`:** 16 bytes +- **`isize`:** 4 bytes en sistemas de 32 bits, 8 bytes en sistemas de 64 bits + +Estos tamaños de memoria son fijos y no dependen de los valores almacenados en las variables. + +**Gráfico 2: Tamaños de Memoria de los Tipos Enteros en Rust** + +```plaintext + +------+ +------+ + | Tipo | | Size | + +------+ +------+ + | i8 |----->| 1 B | + +------+ +------+ + | i16 |----->| 2 B | + +------+ +------+ + | i32 |----->| 4 B | + +------+ +------+ + | i64 |----->| 8 B | + +------+ +------+ + | i128 |----->| 16 B | + +------+ +------+ + | isize|----->| 4 B / 8 B | + +------+ +----------+ +``` + +### Conclusión + +Los tipos enteros con signo en Rust (`i8`, `i16`, `i32`, `i64`, `i128`, `isize`) son fundamentales para manejar números enteros en diversas aplicaciones. Comprender las diferencias en tamaño y rango, así como los métodos comunes para trabajar con ellos, permite a los desarrolladores escribir código eficiente y preciso. Estos tipos proporcionan la flexibilidad necesaria para adaptarse a diferentes necesidades de almacenamiento y procesamiento en Rust. diff --git a/content/2.basic/19.unsigned-integer.md b/content/2.basic/19.unsigned-integer.md new file mode 100644 index 0000000..64bd813 --- /dev/null +++ b/content/2.basic/19.unsigned-integer.md @@ -0,0 +1,151 @@ +--- +title: 'u8, u16, u32, u64, u128, usize' +description: 'Comprendiendo los Tipos Enteros sin Signo en Rust' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: -50 + y: 360 + width: 280 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch03-02-data-types.html#tipos-de-enteros' + - name: 'Ferrous System' + english: true + link: 'https://github.com/ferrous-systems/rust-training/blob/main/training-slides/src/basic-types.md#integers' + - name: 'Ferrocene Language Specification' + english: true + link: 'https://public-docs.ferrocene.dev/main/specification/types-and-traits.html#integer-types' +--- +## Explorando los Tipos Enteros sin Signo en Rust + +En Rust, los tipos `u8`, `u16`, `u32`, `u64`, `u128` y `usize` son tipos de datos enteros sin signo que representan números enteros positivos con diferentes tamaños y rangos. Estos tipos son esenciales para manejar números enteros de manera eficiente y segura en Rust. A continuación, se presenta una explicación detallada de cada uno, sus usos, métodos comunes y su espacio en memoria. + +### Tipos Enteros sin Signo en Rust + +#### 1. `u8` +- **Descripción:** Representa números enteros sin signo de 8 bits. +- **Rango de valores:** 0 a 255. +- **Espacio en memoria:** 1 byte. + +#### 2. `u16` +- **Descripción:** Representa números enteros sin signo de 16 bits. +- **Rango de valores:** 0 a 65,535. +- **Espacio en memoria:** 2 bytes. + +#### 3. `u32` +- **Descripción:** Representa números enteros sin signo de 32 bits. +- **Rango de valores:** 0 a 4,294,967,295. +- **Espacio en memoria:** 4 bytes. + +#### 4. `u64` +- **Descripción:** Representa números enteros sin signo de 64 bits. +- **Rango de valores:** 0 a 18,446,744,073,709,551,615. +- **Espacio en memoria:** 8 bytes. + +#### 5. `u128` +- **Descripción:** Representa números enteros sin signo de 128 bits. +- **Rango de valores:** 0 a 340,282,366,920,938,463,463,374,607,431,768,211,455. +- **Espacio en memoria:** 16 bytes. + +#### 6. `usize` +- **Descripción:** Representa números enteros sin signo que tienen el tamaño de un puntero. Su tamaño puede variar dependiendo de la arquitectura del sistema. +- **Rango de valores:** Depende de la arquitectura. +- **Espacio en memoria:** 4 bytes en sistemas de 32 bits, 8 bytes en sistemas de 64 bits. + +### Usos de los Tipos Enteros sin Signo + +- **Almacenamiento de datos:** Se utilizan para almacenar números enteros positivos en diferentes rangos, dependiendo de la precisión requerida por la aplicación. +- **Operaciones aritméticas:** Se utilizan para realizar operaciones matemáticas como suma, resta, multiplicación y división en valores enteros positivos. +- **Índices y tamaños de datos:** `usize` se utiliza comúnmente para índices y tamaños de datos en estructuras de datos, ya que su tamaño se ajusta automáticamente a la arquitectura del sistema. + +### Métodos Más Comunes + +Al igual que con los tipos enteros con signo, los tipos enteros sin signo en Rust no tienen métodos específicos asociados, pero se pueden utilizar funciones proporcionadas por Rust para realizar operaciones comunes. Algunas de las funciones más utilizadas son: + +- **`to_string()`:** Convierte el número en una cadena de caracteres. +- **`overflowing_add()`:** Realiza una suma con desbordamiento silencioso, devolviendo una tupla con el resultado y un indicador de desbordamiento. +- **`wrapping_add()`:** Realiza una suma con desbordamiento modular, devolviendo el resultado con un desbordamiento envuelto. + +### Ejemplo de Uso en Rust + +```rust +fn main() { + let a: u32 = 42; + let b: u32 = 56; + + // Operaciones comunes + println!("a como cadena: {}", a.to_string()); + println!("Número de bits en 1 en a: {}", a.count_ones()); + println!("Número de bits en 0 en b: {}", b.count_zeros()); + + let (sum, overflowed) = a.overflowing_add(b); + println!("Suma con posible desbordamiento: {} (desbordado: {})", sum, overflowed); + + let wrapped_sum = a.wrapping_add(b); + println!("Suma con desbordamiento envuelto: {}", wrapped_sum); +} +``` + +**Gráfico 1: Ejemplo de Operaciones Comunes con Enteros sin Signo en Rust** + +```plaintext +let a: u32 = 42; +let b: u32 = 56; + + +-----+ +------+ + | a |----->| 42 | + +-----+ +------+ + u32 + + +-----+ +------+ + | b |----->| 56 | + +-----+ +------+ + u32 + + a.to_string() -> "42" + a.count_ones() -> 3 + b.count_zeros() -> 26 + a.overflowing_add(b) -> (98, false) + a.wrapping_add(b) -> 98 +``` + +### Espacio en Memoria + +El espacio en memoria ocupado por los tipos enteros depende del tamaño de cada tipo: + +- **`u8`:** 1 byte +- **`u16`:** 2 bytes +- **`u32`:** 4 bytes +- **`u64`:** 8 bytes +- **`u128`:** 16 bytes +- **`usize`:** 4 bytes en sistemas de 32 bits, 8 bytes en sistemas de 64 bits + +Estos tamaños de memoria son fijos y no dependen de los valores almacenados en las variables. + +**Gráfico 2: Tamaños de Memoria de los Tipos Enteros sin Signo en Rust** + +```plaintext + +------+ +------+ + | Tipo | | Size | + +------+ +------+ + | u8 |----->| 1 B | + +------+ +------+ + | u16 |----->| 2 B | + +------+ +------+ + | u32 |----->| 4 B | + +------+ +------+ + | u64 |----->| 8 B | + +------+ +------+ + | u128 |----->| 16 B | + +------+ +------+ + | usize|----->| 4 B / 8 B | + +------+ +----------+ +``` + +### Conclusión + +Los tipos enteros sin signo en Rust (`u8`, `u16`, `u32`, `u64`, `u128`, `usize`) son esenciales para manejar números enteros positivos de manera eficiente. Comprender las diferencias en tamaño y rango, así como los métodos comunes para trabajar con ellos, permite a los desarrolladores escribir código robusto y eficiente. Estos tipos proporcionan la flexibilidad necesaria para adaptarse a diferentes necesidades de almacenamiento y procesamiento en Rust, garantizando que los programas manejen datos numéricos de manera segura y eficiente. diff --git a/content/2.basic/2.variables.md b/content/2.basic/2.variables.md new file mode 100644 index 0000000..25e373b --- /dev/null +++ b/content/2.basic/2.variables.md @@ -0,0 +1,113 @@ +--- +title: 'Variables y Declaraciones' +description: 'Profundizando en las Variables en Rust: Funcionamiento, Almacenamiento y Gestión de Memoria' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: -700 + y: 240 + width: 320 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch03-01-variables-and-mutability' + - name: 'Variables y declaraciones en Rust' + english: false + link: 'https://blog.rustlang-es.org/articles/variables-y-declaraciones' +--- +## Profundizando en las Variables en Rust: Funcionamiento, Almacenamiento y Gestión de Memoria + +En Rust, las variables juegan un papel crucial en la gestión de datos y la memoria. Este blog post explorará a fondo cómo funcionan las variables en Rust, cómo se almacenan y gestionan en la memoria, y los diferentes tipos de declaración disponibles. + +### 1. ¿Qué es una Variable en Rust? + +En Rust, una variable es un contenedor que puede almacenar un valor de un tipo específico. Las variables permiten a los programas almacenar y manipular datos de manera eficiente. A diferencia de algunos lenguajes, las variables en Rust son **inmutables** por defecto, lo que significa que no pueden ser reasignadas después de su declaración a menos que se especifiquen como mutables. + +### 2. Declaración e Inicialización de Variables + +La declaración de una variable en Rust se realiza utilizando la palabra clave `let`. Aquí hay un ejemplo básico: + +```rust +fn main() { + let x = 5; + println!("El valor de x es: {x}"); +} +``` + +En este ejemplo: +- `let x = 5;` declara una variable `x` y la inicializa con el valor `5`. +- `println!("El valor de x es: {x}");` imprime el valor de `x` en la pantalla. + +### 3. Mutabilidad + +En la programación un problema común es que los desarrolladores crean variables y suele re utilizarlas bastante sin saber su uso en otro momento. + +Por lo que inicialmente se espera que tenga un valor pero en entre un punto y otro se modifica ese valor, eso crea un error, se esperaba que ese valor permanezca con el estado inicial a lo largo de su ejecución. + +En Rust, para solucionar ese problema se tomo la medida de que toda variable sea inmutable por defecto. + +Es decir, a menos que no sea declarada para que sea modificada por futuros programadores esa variable no puede cambiar de valor. + +Para permitir que una variable sea reasignada, debes declararla como mutable usando la palabra clave `mut`: + +```rust +fn main() { + let mut x = 5; + println!("El valor de x es: {x}"); + x = 6; + println!("El valor de x ahora es: {x}"); +} +``` + +En este ejemplo: +- `let mut x = 5;` declara una variable mutable `x` y la inicializa con el valor `5`. +- `x = 6;` reasigna `x` con el valor `6`. + +### 4. Tipos de Datos y Inferencia de Tipos + +Rust es un lenguaje de programación fuertemente tipado, lo que significa que cada variable tiene un tipo específico. Los tipos básicos incluyen: + +- **Enteros** (`i8`, `i16`, `i32`, `i64`, `i128`, `isize`, `u8`, `u16`, `u32`, `u64`, `u128`, `usize`) +- **Flotantes** (`f32`, `f64`) +- **Booleanos** (`bool`) +- **Caracteres** (`char`) +- **Cadenas** (`String`) + +> En otros topicos se detalla mejor cada tipo de dato + +Por lo general Rust puede inferir automáticamente el tipo de una variable basándose en el valor asignado, pero también puedes especificar explícitamente el tipo: + +```rust +fn main() { + let x: i32 = 5; + let y: f64 = 3.14; + let z: bool = true; + println!("x: {}, y: {}, z: {}", x, y, z); +} +``` + +### 5. Gestión de Memoria + +Rust utiliza un sistema de gestión de memoria basado en la propiedad y el préstamo. Cada variable en Rust tiene un propietario, y la memoria se libera automáticamente cuando el propietario sale del alcance. Esto elimina la necesidad de un recolector de basura (GC) y reduce la sobrecarga de memoria. + +#### Almacenamiento en Stack y Heap + +- **Stack:** Las variables de tipos primitivos (como enteros y booleanos) se almacenan en la pila (stack). El acceso a la memoria de la pila es rápido y eficiente. +- **Heap:** Las estructuras de datos complejas (como `String` y `Vec`) se almacenan en el montón (heap). El acceso a la memoria del montón es más lento, pero permite una mayor flexibilidad en la gestión de la memoria. + +```rust +fn main() { + let s1 = String::from("Hello"); + let s2 = s1; // s1 se mueve a s2 + // println!("{}", s1); // Error: s1 ya no es válido + println!("{}", s2); +} +``` + +En este ejemplo, `s1` se mueve a `s2`, y `s1` ya no es válido. Rust previene errores comunes relacionados con la memoria mediante estas reglas estrictas de propiedad. + +#### Conclusión + +Las variables en Rust son fundamentales para la gestión de datos y la memoria. La combinación de mutabilidad controlada, tipos estrictos, y un sistema de propiedad robusto hace que Rust sea seguro y eficiente. Entender cómo funcionan las variables y cómo se gestionan en memoria es esencial para escribir programas efectivos en Rust. Al dominar estos conceptos, estarás bien encaminado para aprovechar al máximo este potente lenguaje. ¡Feliz codificación! diff --git a/content/2.basic/20.str.md b/content/2.basic/20.str.md new file mode 100644 index 0000000..41722b2 --- /dev/null +++ b/content/2.basic/20.str.md @@ -0,0 +1,139 @@ +--- +title: 'str' +description: 'El Tipo de Dato `str` en Rust' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: -50 + y: 400 + width: 280 + externalLinks: + - name: 'Libro Oficial' + english: false + link: '' + - name: 'Ferrous System' + english: true + link: '' + - name: 'Ferrocene Language Specification' + english: true + link: 'https://public-docs.ferrocene.dev/main/specification/types-and-traits.html#str-type' +--- +## El Tipo de Dato `str` en Rust + +En Rust, el tipo de dato `str` es fundamental para trabajar con cadenas de caracteres. A diferencia de muchos otros lenguajes, Rust trata las cadenas de forma única, lo que puede parecer complicado al principio, pero proporciona una gran eficiencia y seguridad. Este artículo detalla las características del tipo `str`, sus usos, métodos comunes y cómo manejarlo en memoria. + +### Características del Tipo `str` + +#### 1. Inmutabilidad +- **Descripción:** En Rust, las cadenas de tipo `str` son inmutables. Esto significa que una vez creada, una cadena `str` no puede ser modificada. +- **Ventaja:** La inmutabilidad garantiza la seguridad y coherencia de los datos a lo largo de la ejecución del programa. + +#### 2. Almacenamiento en Memoria +- **Descripción:** El tipo `str` en Rust se almacena como una secuencia contigua de bytes en memoria. +- **Codificación:** Las cadenas `str` están codificadas en UTF-8, lo que permite representar una amplia variedad de caracteres Unicode. +- **Referencia:** En general, las cadenas `str` se manipulan mediante referencias (`&str`) para evitar copias innecesarias y mejorar el rendimiento. + +#### 3. Tamaño Dinámico +- **Descripción:** El tamaño de un `str` está determinado en tiempo de ejecución, lo que lo convierte en un tipo de tamaño dinámico (DST, por sus siglas en inglés). +- **Manipulación:** Para trabajar con `str`, generalmente utilizamos una referencia (`&str`) que incluye un puntero al inicio de la cadena y una longitud que indica su tamaño. + +### Usos del Tipo `str` + +- **Texto Inmutable:** Se utiliza para representar texto que no necesita ser modificado, como mensajes de error, etiquetas y textos constantes. +- **Comparaciones y Búsquedas:** Es útil para realizar comparaciones y búsquedas dentro de cadenas de texto, asegurando eficiencia y seguridad. +- **Datos Multilingües:** Gracias a su codificación en UTF-8, `str` es ideal para manejar texto en múltiples idiomas, incluidos caracteres especiales y emojis. + +### Métodos Más Comunes + +Aunque el tipo `str` no tiene métodos asociados directamente, Rust proporciona una serie de funciones y métodos a través del tipo de referencia `&str` para manipular y trabajar con cadenas de manera efectiva. Aquí algunos de los métodos más utilizados: + +#### 1. `len()` +- **Descripción:** Devuelve la longitud de la cadena en bytes. +- **Ejemplo:** + +```rust +let s = "Hola, mundo!"; +println!("La longitud de s es: {}", s.len()); +``` + +#### 2. `is_empty()` +- **Descripción:** Verifica si la cadena está vacía. +- **Ejemplo:** + +```rust +let s = ""; +println!("¿Está s vacía?: {}", s.is_empty()); +``` + +#### 3. `contains()` +- **Descripción:** Comprueba si una subcadena está presente en la cadena. +- **Ejemplo:** + +```rust +let s = "Hola, mundo!"; +println!("¿Contiene 'mundo'?: {}", s.contains("mundo")); +``` + +#### 4. `split()` +- **Descripción:** Divide la cadena en subcadenas basadas en un delimitador. +- **Ejemplo:** + +```rust +let s = "Hola, mundo!"; +for part in s.split(", ") { + println!("{}", part); +} +``` + +#### 5. `to_uppercase()` +- **Descripción:** Convierte la cadena a mayúsculas. +- **Ejemplo:** + +```rust +let s = "Hola, mundo!"; +println!("En mayúsculas: {}", s.to_uppercase()); +``` + +### Ejemplo de Uso en Rust + +```rust +fn main() { + let saludo: &str = "Hola, mundo!"; + + // Métodos comunes + println!("Longitud: {}", saludo.len()); + println!("¿Está vacío?: {}", saludo.is_empty()); + println!("¿Contiene 'mundo'?: {}", saludo.contains("mundo")); + + // División de la cadena + for parte in saludo.split(", ") { + println!("Parte: {}", parte); + } + + // Conversión a mayúsculas + println!("En mayúsculas: {}", saludo.to_uppercase()); +} +``` + +### Espacio en Memoria + +En Rust, el tipo `str` es almacenado en memoria de manera contigua y codificado en UTF-8, permitiendo una representación eficiente y compacta de texto Unicode. A continuación, se ilustra cómo se gestiona en memoria: + +**Gráfico: Almacenamiento de una Cadena `str` en Memoria** + +```plaintext ++---+---+---+---+---+---+---+---+---+---+---+---+ +| H | o | l | a | , | | m | u | n | d | o | ! | ++---+---+---+---+---+---+---+---+---+---+---+---+ +0 1 2 3 4 5 6 7 8 9 10 11 + +Dirección de memoria contigua: + Puntero al inicio -> 0 + Longitud en bytes -> 12 +``` + +### Conclusión + +El tipo `str` en Rust es una poderosa herramienta para trabajar con cadenas de texto de manera segura y eficiente. Su inmutabilidad, codificación en UTF-8 y manejo dinámico de tamaños lo hacen ideal para aplicaciones que requieren un manejo robusto de texto. Comprender cómo utilizar `str` y sus métodos asociados permite a los desarrolladores escribir código más seguro y eficiente en Rust. diff --git a/content/2.basic/21.complex-types.yml b/content/2.basic/21.complex-types.yml new file mode 100644 index 0000000..8ac460f --- /dev/null +++ b/content/2.basic/21.complex-types.yml @@ -0,0 +1,14 @@ +title: 'Tipos de Datos Complejos' +description: '' +data: + type: 'transparent' + topicLevel: 'start' + position: + x: -50 + y: 480 + width: 320 + align: 'left' + sourcePosition: + none: 'top' + targetPosition: + basic: 'left' diff --git a/content/2.basic/22.tuples.md b/content/2.basic/22.tuples.md new file mode 100644 index 0000000..b7e7593 --- /dev/null +++ b/content/2.basic/22.tuples.md @@ -0,0 +1,134 @@ +--- +title: 'tupla' +description: 'Las Tuplas en Rust: Una Introducción' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: -50 + y: 520 + # width: 320 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch03-02-data-types#el-tipo-tupla' + - name: 'Libro de Referencia Rust' + english: true + link: 'https://doc.rust-lang.org/reference/types/tuple.html#tuple-types' +--- +## Las Tuplas en Rust: Una Introducción + +En Rust, las tuplas son un tipo de dato versátil y poderoso que permite agrupar múltiples valores de diferentes tipos en una única entidad. Esta funcionalidad es especialmente útil para retornar múltiples valores de una función o para agrupar datos relacionados de manera estructurada. En este blog post, exploraremos en detalle las características, usos y métodos comunes de las tuplas en Rust. + +### Características de las Tuplas + +#### 1. Heterogeneidad +- **Descripción:** Las tuplas pueden contener valores de diferentes tipos. Esto las diferencia de las arrays y vectors, que solo pueden contener valores del mismo tipo. +- **Ejemplo:** + +```rust +let tupla: (i32, f64, char) = (42, 3.14, 'a'); +``` + +#### 2. Tamaño Fijo +- **Descripción:** El tamaño de una tupla se define en el momento de su creación y no puede cambiar. Cada tupla tiene un número fijo de elementos, y este número se debe conocer en tiempo de compilación. +- **Ejemplo:** + +```rust +let tupla: (i32, i32) = (1, 2); // Tamaño fijo de 2 elementos +``` + +#### 3. Acceso por Índice +- **Descripción:** Los elementos de una tupla se pueden acceder por su índice, comenzando desde cero. +- **Ejemplo:** + +```rust +let tupla: (i32, f64, char) = (42, 3.14, 'a'); +println!("El primer elemento es: {}", tupla.0); +println!("El segundo elemento es: {}", tupla.1); +println!("El tercer elemento es: {}", tupla.2); +``` + +### Usos de las Tuplas + +- **Retorno de Múltiples Valores:** Las tuplas son ideales para funciones que necesitan retornar múltiples valores. +- **Agrupación de Datos Relacionados:** Permiten agrupar datos relacionados de diferentes tipos en una sola estructura. +- **Desestructuración:** Facilita la extracción de valores individuales de una tupla. + +### Ejemplo de Uso en Rust + +```rust +fn main() { + let coordenadas: (f64, f64) = (6.0, 8.0); + + // Desestructuración de la tupla + let (x, y) = coordenadas; + println!("Coordenadas: x = {}, y = {}", x, y); + + // Acceso por índice + println!("El primer elemento es: {}", coordenadas.0); + println!("El segundo elemento es: {}", coordenadas.1); + + // Función que retorna una tupla + let resultado = sumar_y_multiplicar(3, 4); + println!("Suma: {}, Multiplicación: {}", resultado.0, resultado.1); +} + +fn sumar_y_multiplicar(a: i32, b: i32) -> (i32, i32) { + (a + b, a * b) +} +``` + +### Métodos Más Comunes + +Aunque las tuplas no tienen métodos específicos asociados como otros tipos en Rust, se pueden manipular mediante desestructuración y acceso por índice. Sin embargo, algunas operaciones comunes incluyen: + +- **Desestructuración:** Permite asignar los valores de una tupla a variables individuales. +- **Acceso por Índice:** Acceso directo a los elementos individuales de una tupla usando su índice. + +### Desestructuración de Tuplas + +La desestructuración permite desempaquetar una tupla en sus componentes individuales. Esto es útil cuando se necesitan trabajar con los valores por separado. + +```rust +fn main() { + let persona: (&str, i32) = ("Alice", 30); + + // Desestructuración + let (nombre, edad) = persona; + println!("Nombre: {}, Edad: {}", nombre, edad); +} +``` + +### Acceso por Índice + +Cada elemento de una tupla se puede acceder utilizando el índice correspondiente, que empieza desde cero. + +```rust +fn main() { + let colores: (&str, &str, &str) = ("rojo", "verde", "azul"); + + println!("Primer color: {}", colores.0); + println!("Segundo color: {}", colores.1); + println!("Tercer color: {}", colores.2); +} +``` + +### Espacio en Memoria + +Las tuplas ocupan un tamaño en memoria que es la suma del tamaño de cada uno de sus elementos. Esto garantiza que no haya desperdicio de memoria y que las operaciones con tuplas sean eficientes. + +**Gráfico: Representación de una Tupla en Memoria** + +```plaintext ++-----------+-----------+-----------+ +| i32 | f64 | char | ++-----------+-----------+-----------+ +| 4 bytes | 8 bytes | 4 bytes | ++-----------+-----------+-----------+ +``` + +### Conclusión + +Las tuplas en Rust son una herramienta poderosa para agrupar múltiples valores de diferentes tipos en una única entidad. Su capacidad para ser desestructuradas y acceder a sus elementos por índice las hace extremadamente útiles para una variedad de aplicaciones, desde retornar múltiples valores de funciones hasta agrupar datos relacionados. Al entender cómo funcionan las tuplas y cómo utilizarlas de manera efectiva, los desarrolladores pueden escribir código más limpio y eficiente en Rust. diff --git a/content/2.basic/23.array.md b/content/2.basic/23.array.md new file mode 100644 index 0000000..80867e8 --- /dev/null +++ b/content/2.basic/23.array.md @@ -0,0 +1,138 @@ +--- +title: 'Arreglos' +description: 'Los Arreglos en Rust' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: 32 + y: 520 + width: 118 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch03-02-data-types#el-tipo-arreglo' + - name: 'Libro de Referencia Rust' + english: true + link: 'https://doc.rust-lang.org/reference/types/array.html' +--- +## Los Arreglos en Rust + +En Rust, los arreglos (arrays) son una de las estructuras de datos más básicas y esenciales. Los arreglos permiten almacenar múltiples valores del mismo tipo en una secuencia contigua de elementos. Este blog post explora en detalle las características, usos, métodos comunes y manejo en memoria de los arreglos en Rust. + +### Características de los Arreglos en Rust + +#### 1. Homogeneidad +- **Descripción:** Todos los elementos de un arreglo deben ser del mismo tipo. +- **Ejemplo:** + +```rust +let numeros: [i32; 5] = [1, 2, 3, 4, 5]; +``` + +#### 2. Tamaño Fijo +- **Descripción:** El tamaño de un arreglo en Rust se debe conocer en tiempo de compilación y no puede cambiar. +- **Ejemplo:** + +```rust +let letras: [char; 3] = ['a', 'b', 'c']; +``` + +#### 3. Acceso por Índice +- **Descripción:** Los elementos de un arreglo se pueden acceder mediante índices, comenzando desde cero. +- **Ejemplo:** + +```rust +let dias: [&str; 7] = ["Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado", "Domingo"]; +println!("El primer día es: {}", dias[0]); +``` + +### Usos de los Arreglos + +- **Almacenamiento de Datos:** Los arreglos son útiles para almacenar colecciones de datos del mismo tipo, como números, caracteres o strings. +- **Iteraciones y Operaciones:** Facilitan la realización de operaciones repetitivas sobre una colección de elementos. +- **Acceso Rápido:** Proporcionan acceso rápido a los elementos a través de índices. + +### Ejemplo de Uso en Rust + +```rust +fn main() { + let numeros: [i32; 5] = [1, 2, 3, 4, 5]; + + // Acceso por índice + println!("El primer número es: {}", numeros[0]); + + // Iteración sobre el arreglo + for num in numeros.iter() { + println!("Número: {}", num); + } + + // Modificación de un elemento + let mut mutable_numeros = [1, 2, 3, 4, 5]; + mutable_numeros[0] = 10; + println!("El primer número modificado es: {}", mutable_numeros[0]); +} +``` + +### Métodos Comunes + +Aunque los arreglos en Rust no tienen métodos asociados como las colecciones más complejas, se pueden utilizar en combinación con slices y otros tipos para realizar diversas operaciones. Aquí algunos de los métodos y usos más comunes: + +#### 1. `len()` +- **Descripción:** Devuelve la longitud del arreglo. +- **Ejemplo:** + +```rust +let numeros: [i32; 5] = [1, 2, 3, 4, 5]; +println!("La longitud del arreglo es: {}", numeros.len()); +``` + +#### 2. Iteración con `iter()` +- **Descripción:** Devuelve un iterador sobre el arreglo. +- **Ejemplo:** + +```rust +let frutas: [&str; 3] = ["Manzana", "Banana", "Cereza"]; +for fruta in frutas.iter() { + println!("Fruta: {}", fruta); +} +``` + +#### 3. Acceso Mutable +- **Descripción:** Permite modificar los elementos del arreglo. +- **Ejemplo:** + +```rust +let mut letras: [char; 3] = ['a', 'b', 'c']; +letras[0] = 'z'; +println!("La primera letra modificada es: {}", letras[0]); +``` + +### Espacio en Memoria + +El espacio en memoria ocupado por un arreglo es contiguo y depende del tipo y la cantidad de elementos que contiene. A continuación, se ilustra cómo se gestiona en memoria un arreglo: + +**Gráfico: Representación de un Arreglo en Memoria** + +```plaintext ++-----------+-----------+-----------+-----------+-----------+ +| Elemento1 | Elemento2 | Elemento3 | Elemento4 | Elemento5 | ++-----------+-----------+-----------+-----------+-----------+ +| i32 | i32 | i32 | i32 | i32 | ++-----------+-----------+-----------+-----------+-----------+ +``` + +### Ventajas y Desventajas de los Arreglos + +#### Ventajas +- **Acceso Rápido:** Proporcionan acceso constante a los elementos mediante índices. +- **Eficiencia de Memoria:** Ocupan un bloque contiguo de memoria, lo que mejora la eficiencia de acceso y manipulación. + +#### Desventajas +- **Tamaño Fijo:** No se pueden redimensionar una vez creados, lo que puede ser limitante en algunos casos. +- **Homogeneidad:** Solo pueden almacenar elementos del mismo tipo. + +### Conclusión + +Los arreglos en Rust son una herramienta fundamental para almacenar y manipular colecciones de datos del mismo tipo de manera eficiente y segura. Aunque tienen un tamaño fijo y solo pueden contener elementos del mismo tipo, su simplicidad y rapidez de acceso los hacen ideales para muchas aplicaciones comunes. Al comprender cómo utilizar arreglos y sus métodos asociados, los desarrolladores pueden escribir código más eficiente y organizado en Rust. diff --git a/content/2.basic/24.slices.md b/content/2.basic/24.slices.md new file mode 100644 index 0000000..4016d23 --- /dev/null +++ b/content/2.basic/24.slices.md @@ -0,0 +1,133 @@ +--- +title: 'Slice' +description: 'Comprendiendo los Slices en Rust: Rendimiento, Uso y Comparación con Arreglos' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: 155 + y: 520 + # width: 320 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch04-03-slices#string-slices' + - name: 'Libro de Referencia Rust' + english: true + link: '' +--- +## Comprendiendo los Slices en Rust: Rendimiento, Uso y Comparación con Arreglos + +Los slices en Rust son una de las características más poderosas y flexibles del lenguaje. A diferencia de los arreglos, los slices permiten trabajar con subsecciones de datos sin necesidad de copiar o duplicar los datos originales. En este blog post, exploraremos en detalle las características, el rendimiento, la representación en memoria y las diferencias entre los slices y los arreglos. + +### ¿Qué son los Slices? + +Los slices son vistas de datos que permiten acceder a una porción de una colección, como un arreglo o una cadena (`str`). Son especialmente útiles cuando se necesita trabajar con subsecciones de datos sin crear copias adicionales. Los slices se representan mediante referencias y tienen un tamaño dinámico determinado en tiempo de ejecución. + +### Características de los Slices + +#### 1. Tamaño Dinámico +- **Descripción:** A diferencia de los arreglos, el tamaño de un slice no se conoce en tiempo de compilación y puede cambiar. +- **Ejemplo:** + +```rust +let arreglo = [1, 2, 3, 4, 5]; +let slice = &arreglo[1..4]; // Slice que incluye los elementos de índice 1 a 3 +``` + +#### 2. Inmutabilidad por Defecto +- **Descripción:** Los slices son inmutables por defecto, pero se pueden crear slices mutables. +- **Ejemplo:** + +```rust +let mut arreglo = [1, 2, 3, 4, 5]; +let slice_mut = &mut arreglo[1..4]; // Slice mutable +``` + +#### 3. Representación en Memoria +- **Descripción:** Los slices almacenan una referencia al primer elemento y la longitud del slice. Esta estructura les permite ser eficientes en términos de memoria y rendimiento. +- **Ejemplo:** + +```plaintext +Slice: &arreglo[1..3] +Memoria: ++-----------+-----------+-----------+-----------+-----------+ +| Elemento0 | Elemento1 | Elemento2 | Elemento3 | Elemento4 | ++-----------+-----------+-----------+-----------+-----------+ +| 1 | 2 | 3 | 4 | 5 | ++-----------+-----------+-----------+-----------+-----------+ + ^ ^ + | | + Referencia Longitud + 1 ... 3 +``` + +### Diferencias entre Slices y Arreglos + +| Característica | Arreglo | Slice | +|----------------|---------|-------| +| Tamaño | Fijo | Dinámico | +| Memoria | Contigua y estática | Referencia a un segmento de datos | +| Flexibilidad | Menor | Mayor (permite trabajar con subsecciones) | +| Mutabilidad | Puede ser mutable o inmutable | Inmutable por defecto, puede ser mutable | +| Coste de Copia | Copia completa | No requiere copia, trabaja con referencias | + +### Ventajas de los Slices + +- **Eficiencia de Memoria:** No requieren copiar datos, lo que los hace eficientes en términos de uso de memoria. +- **Flexibilidad:** Permiten trabajar con partes de colecciones sin la necesidad de manipular la colección completa. +- **Seguridad:** Ofrecen seguridad en tiempo de compilación, garantizando que no se acceda fuera de los límites del slice. + +### Usos Comunes de los Slices + +- **Subsecciones de Arreglos:** Extraer y manipular partes específicas de un arreglo. +- **Procesamiento de Datos:** Pasar partes de colecciones a funciones para su procesamiento. +- **Manejo de Cadenas:** Trabajar con partes de cadenas sin duplicar los datos. + +### Ejemplo de Uso en Rust + +```rust +fn main() { + let arreglo = [10, 20, 30, 40, 50]; + + // Crear un slice inmutable + let slice = &arreglo[1..4]; + println!("Slice: {:?}", slice); + + // Crear un slice mutable + let mut arreglo_mut = [1, 2, 3, 4, 5]; + let slice_mut = &mut arreglo_mut[2..4]; + slice_mut[0] = 10; // Modifica el elemento en el slice + println!("Arreglo modificado: {:?}", arreglo_mut); +} + +``` + +### Representación en Memoria de los Slices + +Los slices se representan en memoria como una referencia al primer elemento del slice y la longitud del slice. Esta representación permite un acceso eficiente y seguro a los datos subyacentes. + +**Gráfico: Representación de un Slice en Memoria** + +```plaintext +Slice: &arreglo[1..3] +Memoria: ++-----------+-----------+-----------+-----------+-----------+ +| Elemento0 | Elemento1 | Elemento2 | Elemento3 | Elemento4 | ++-----------+-----------+-----------+-----------+-----------+ +| 10 | 20 | 30 | 40 | 50 | ++-----------+-----------+-----------+-----------+-----------+ + ^ ^ + | | + Referencia Longitud + 1 ... 3 +``` + +### Rendimiento de los Slices + +El uso de slices es altamente eficiente porque evita la necesidad de copiar datos. Los slices simplemente referencian una porción de una colección existente, lo que reduce el coste de operaciones de memoria y mejora el rendimiento en operaciones de acceso y manipulación de datos. + +### Conclusión + +Los slices en Rust son una herramienta poderosa para trabajar con subsecciones de datos de manera eficiente y segura. Al entender cómo funcionan los slices, cómo se representan en memoria y cómo se diferencian de los arreglos, los desarrolladores pueden aprovechar mejor las capacidades de Rust para escribir código más limpio, eficiente y seguro. Los slices no solo mejoran el rendimiento de las operaciones de manipulación de datos, sino que también proporcionan una mayor flexibilidad en el diseño de programas. diff --git a/content/2.basic/25.struct.md b/content/2.basic/25.struct.md new file mode 100644 index 0000000..ef6022e --- /dev/null +++ b/content/2.basic/25.struct.md @@ -0,0 +1,249 @@ +--- +title: 'struct' +description: 'Estructuras en Rust: Un Análisis Completo' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: -50 + y: 560 + # width: 320 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch05-00-structs' + - name: 'Libro Oficial - Definiendo e Instanciando Structs' + english: false + link: 'https://book.rustlang-es.org/ch05-01-defining-structs' + - name: 'Libro Oficial - Un Programa de Ejemplo' + english: false + link: 'https://book.rustlang-es.org/ch05-02-example-structs' + - name: 'Libro de Referencia Rust' + english: true + link: 'https://doc.rust-lang.org/reference/types/struct.html' +--- +## Estructuras en Rust + +Las estructuras (structs) en Rust son una forma fundamental de organizar y agrupar datos. Aunque se asemejan a las clases en lenguajes orientados a objetos como Java y C#, tienen diferencias importantes que influencian su uso y comportamiento en Rust. En este blog post, exploraremos en profundidad las estructuras en Rust, cómo funcionan, sus tipos, convenciones y curiosidades. + +### Comparación con Clases en Java y C# + +En lenguajes como Java y C#, las clases son el núcleo de la programación orientada a objetos. Las clases permiten encapsular datos y comportamientos en un solo lugar, facilitando la modularidad y la reutilización del código. Sin embargo, Rust no tiene un sistema de clases tradicional. En lugar de ello, Rust usa estructuras (structs) para agrupar datos y traits para definir comportamientos. + +### Tipos de Estructuras en Rust + +Rust ofrece tres tipos principales de estructuras: + +1. **Structs Clásicas (Classic Structs):** Definen una estructura con campos nombrados. + + ```rust + struct Persona { + nombre: String, + edad: u32, + } + ``` + +2. **Tupla Structs (Tuple Structs):** Definen una estructura sin nombres de campo, solo tipos. + + ```rust + struct Punto(f32, f32); + ``` + +3. **Unit Structs:** Son estructuras sin campos, útiles para ciertos casos de traits y patrones. + + ```rust + struct Unidad; + ``` + +### Definición y Uso de Estructuras + +#### Creación de Estructuras + +Para definir y usar una estructura clásica: + +```rust +struct Persona { + nombre: String, + edad: u32, +} + +fn main() { + let persona = Persona { + nombre: String::from("Alice"), + edad: 30, + }; + println!("Nombre: {}, Edad: {}", persona.nombre, persona.edad); +} +``` + +### Desestructuración de Estructuras + +La desestructuración permite extraer campos de una estructura en variables individuales: + +```rust +fn main() { + let persona = Persona { + nombre: String::from("Bob"), + edad: 25, + }; + + let Persona { nombre, edad } = persona; + println!("Nombre: {}, Edad: {}", nombre, edad); +} +``` + +### Convención de la Función `new` + +En Rust, es común definir una función `new` asociada a la estructura para crear instancias de la misma. Esto mejora la legibilidad y usabilidad del código. + +```rust +impl Persona { + fn new(nombre: String, edad: u32) -> Persona { + Persona { nombre, edad } + } +} + +fn main() { + let persona = Persona::new(String::from("Charlie"), 28); + println!("Nombre: {}, Edad: {}", persona.nombre, persona.edad); +} +``` + +### El Operador `..` para Rellenar Campos + +El operador `..` se utiliza para completar los campos restantes de una estructura con los valores de otra instancia. + +```rust +fn main() { + let persona1 = Persona { + nombre: String::from("Dave"), + edad: 40, + }; + + let persona2 = Persona { + nombre: String::from("Eve"), + ..persona1 + }; + + println!("Nombre: {}, Edad: {}", persona2.nombre, persona2.edad); +} +``` + +### Funcionamiento en Memoria + +Las estructuras en Rust se almacenan en memoria de manera contigua. Esto significa que todos los campos de una estructura se almacenan uno después del otro, lo que mejora la eficiencia en el acceso a los datos. + +#### **Gráfico: Representación en Memoria de una Estructura** + +```plaintext +Persona { nombre: "Alice", edad: 30 } +Memoria: ++----------------+------+------+------+------+------+------+------+------+ +| nombre | A | l | i | c | e | \0 | edad | ++----------------+------+------+------+------+------+------+------+------+ +| Memoria (bytes)| 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | ++----------------+------+------+------+------+------+------+------+------+ +``` + +En este gráfico, podemos ver que cada carácter de la cadena `nombre` y el valor de `edad` se almacenan en memoria de manera contigua. + +### Pattern Matching con Structs + +El pattern matching es una característica poderosa en Rust que permite hacer coincidir estructuras completas o solo algunos de sus campos con patrones específicos. Esto se puede utilizar para realizar operaciones condicionales basadas en la forma y el contenido de los datos. + +#### Ejemplo de Pattern Matching Completo + +```rust +fn main() { + let persona = Persona { + nombre: String::from("George"), + edad: 22, + }; + + match persona { + Persona { nombre, edad: 22 } => println!("¡Hola, George de 22 años!"), + Persona { nombre, .. } => println!("Hola, {}!", nombre), + } +} +``` + +En este ejemplo, el primer patrón coincide con una `Persona` cuyo `edad` es 22, mientras que el segundo patrón coincide con cualquier `Persona`, ignorando los demás campos gracias al operador `..`. + +#### Desestructuración Parcial + +También es posible desestructurar parcialmente una estructura para acceder solo a ciertos campos: + +```rust +fn main() { + let persona = Persona { + nombre: String::from("Henry"), + edad: 30, + }; + + let Persona { nombre, .. } = persona; + println!("Nombre: {}", nombre); +} +``` + +### Replicando el Comportamiento de Clases + +Aunque Rust no tiene clases, se pueden usar estructuras y traits para replicar comportamientos similares a los de las clases en lenguajes orientados a objetos. + +> [!NOTE] +> Para replicar este comportamiento en Rust necesitaras saber de traits, por lo que tendras mas detalles en ese topico + +### Constantes en Estructuras + +Las estructuras en Rust también pueden incluir constantes. + +```rust +struct Circulo { + radio: f64, +} + +impl Circulo { + const PI: f64 = 3.141592653589793; + + fn area(&self) -> f64 { + Circulo::PI * self.radio * self.radio + } +} + +fn main() { + let circulo = Circulo { radio: 5.0 }; + println!("El área del círculo es: {}", circulo.area()); +} +``` + +### Curiosidades y Usos Adicionales + +- **Derivación Automática de Traits:** Rust permite derivar automáticamente traits comunes como `Debug` y `Clone`. + + ```rust + #[derive(Debug, Clone)] + struct Persona { + nombre: String, + edad: u32, + } + ``` + +- **Pattern Matching con Structs:** Los patrones se pueden utilizar para hacer coincidir estructuras completas o solo algunos de sus campos. + + ```rust + fn main() { + let persona = Persona { + nombre: String::from("George"), + edad: 22, + }; + + match persona { + Persona { nombre, edad: 22 } => println!("¡Hola, George de 22 años!"), + _ => println!("Persona desconocida"), + } + } + ``` + +### Conclusión + +Las estructuras en Rust son una herramienta poderosa para organizar datos y definir comportamientos mediante traits. Aunque no son clases en el sentido tradicional, proporcionan gran flexibilidad y seguridad en tiempo de compilación. Al aprovechar las características únicas de Rust, como la desestructuración y el operador `..`, los desarrolladores pueden escribir código eficiente y mantenible. Las constantes dentro de estructuras y la derivación automática de traits son solo algunas de las características que hacen que las estructuras en Rust sean versátiles y prácticas para una amplia variedad de aplicaciones. diff --git a/content/2.basic/26.enum.md b/content/2.basic/26.enum.md new file mode 100644 index 0000000..68d450c --- /dev/null +++ b/content/2.basic/26.enum.md @@ -0,0 +1,201 @@ +--- +title: 'enum' +description: 'Enums en Rust: Un Análisis Completo' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: 35 + y: 560 + width: 105 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch06-01-defining-an-enum' + - name: 'Libro Oficial - Match con Enums' + english: false + link: 'https://book.rustlang-es.org/ch06-02-match' + - name: 'Libro Oficial - if-let con Enums' + english: false + link: 'https://book.rustlang-es.org/ch06-03-if-let' + - name: 'Post acerca la Representación en memoria de los Enums' + english: false + link: 'https://blog.rustlang-es.org/articles/como-almacena-rust-los-enum-en-memoria' + - name: 'Libro de Referencia Rust' + english: true + link: 'https://doc.rust-lang.org/reference/items/enumerations.html' +--- +## Enums en Rust + +Los enumerados (enums) en Rust son una poderosa herramienta para definir tipos que pueden representar múltiples variantes, cada una con datos asociados. Este enfoque es diferente al de los enums en lenguajes como Java y C#, y proporciona una flexibilidad y seguridad adicionales. En este blog post, exploraremos en detalle los enums en Rust, su uso, sus características avanzadas como el pattern matching, su funcionamiento en memoria y cómo se diferencian de otros lenguajes. + +### Comparación con Enums en Java y C# + +En lenguajes como Java y C#, los enums son tipos de datos que consisten en un conjunto fijo de constantes. Sin embargo, en Rust, los enums son más poderosos y flexibles, ya que cada variante del enum puede tener datos asociados. Esto permite crear estructuras de datos complejas y seguras en tiempo de compilación. + +### Definición y Uso de Enums + +#### Creación de Enums + +Para definir un enum en Rust, se usa la palabra clave `enum`. Aquí hay un ejemplo simple: + +```rust +enum Color { + Rojo, + Verde, + Azul, +} +``` + +Podemos usar este enum de la siguiente manera: + +```rust +fn main() { + let color_favorito = Color::Verde; + match color_favorito { + Color::Rojo => println!("Rojo!"), + Color::Verde => println!("Verde!"), + Color::Azul => println!("Azul!"), + } +} +``` + +#### Enums con Datos Asociados + +Rust permite que las variantes de un enum tengan datos asociados. Esto es útil para representar diferentes tipos de datos bajo un solo tipo. + +```rust +enum Mensaje { + Saludar(String), + Mover { x: i32, y: i32 }, + CambiarColor(i32, i32, i32), +} + +fn main() { + let mensaje = Mensaje::Mover { x: 10, y: 20 }; + match mensaje { + Mensaje::Saludar(nombre) => println!("Hola, {}!", nombre), + Mensaje::Mover { x, y } => println!("Mover a ({}, {})", x, y), + Mensaje::CambiarColor(r, g, b) => println!("Cambiar color a RGB({}, {}, {})", r, g, b), + } +} +``` + +### Pattern Matching con Enums + +El pattern matching es una característica fundamental en Rust que se utiliza en combinación con enums para escribir código más expresivo y seguro. Permite desestructurar y hacer coincidir variantes de enums, extrayendo datos asociados en el proceso. + +#### Ejemplo de Pattern Matching + +```rust +fn procesar_mensaje(mensaje: Mensaje) { + match mensaje { + Mensaje::Saludar(nombre) => println!("Hola, {}!", nombre), + Mensaje::Mover { x, y } => println!("Mover a ({}, {})", x, y), + Mensaje::CambiarColor(r, g, b) => println!("Cambiar color a RGB({}, {}, {})", r, g, b), + } +} + +fn main() { + let mensaje = Mensaje::Saludar(String::from("Alice")); + procesar_mensaje(mensaje); +} +``` + +### Ventajas del Uso de Enums en Rust + +1. **Seguridad en Tiempo de Compilación:** Los enums proporcionan seguridad adicional al garantizar que todas las variantes se manejen adecuadamente en tiempo de compilación. +2. **Flexibilidad:** Los enums con datos asociados permiten crear tipos de datos flexibles que pueden contener diferentes tipos de información. +3. **Clareza y Mantenimiento:** El uso de enums y pattern matching hace que el código sea más claro y fácil de mantener. + +### Funcionamiento en Memoria + +Los enums en Rust se almacenan en memoria de manera eficiente. Rust asigna suficiente espacio para almacenar la variante más grande del enum y una discriminante que indica cuál variante está activa. Esto asegura que el uso de memoria sea óptimo y que las operaciones sean rápidas. + +#### **Gráfico: Representación en Memoria de un Enum** + +```plaintext +enum Mensaje { + Saludar(String), // Variante con un String + Mover { x: i32, y: i32 }, // Variante con dos i32 + CambiarColor(i32, i32, i32), // Variante con tres i32 +} + +Memoria: ++-----------------+--------------------------------+ +| Discriminante | Datos | ++-----------------+--------------------------------+ +| 0 (Saludar) | Puntero a String | ++-----------------+--------------------------------+ +| 1 (Mover) | x: i32, y: i32 | ++-----------------+--------------------------------+ +| 2 (CambiarColor)| r: i32, g: i32, b: i32 | ++-----------------+--------------------------------+ +``` + +En este gráfico, el discriminante es un valor que indica cuál variante está activa, y los datos son los valores asociados con esa variante. + +> [!NOTE] +> Para mas detalles sobre como funcionan los enum en memoria revisa los recursos adjuntos + +### Comparación con Clases en Java y C# + +A diferencia de las clases en Java y C#, los enums en Rust no pueden tener métodos directamente asociados. Sin embargo, se pueden implementar métodos para un enum usando el bloque `impl`. + +```rust +impl Mensaje { + fn procesar(&self) { + match self { + Mensaje::Saludar(nombre) => println!("Hola, {}!", nombre), + Mensaje::Mover { x, y } => println!("Mover a ({}, {})", x, y), + Mensaje::CambiarColor(r, g, b) => println!("Cambiar color a RGB({}, {}, {})", r, g, b), + } + } +} + +fn main() { + let mensaje = Mensaje::Saludar(String::from("Alice")); + mensaje.procesar(); +} +``` + +### Constantes en Enums + +Rust permite definir constantes dentro de los enums, lo que puede ser útil para definir valores constantes asociados con las variantes. + +```rust +enum Color { + Rojo, + Verde, + Azul, +} + +impl Color { + const FAVORITO: Color = Color::Verde; +} + +fn main() { + let color = Color::FAVORITO; + match color { + Color::Rojo => println!("Rojo!"), + Color::Verde => println!("Verde!"), + Color::Azul => println!("Azul!"), + } +} +``` + +### Curiosidades y Usos Adicionales + +- **Enums Recursivos:** Los enums pueden ser recursivos, lo que es útil para definir estructuras de datos como árboles o listas enlazadas. + + ```rust + enum Lista { + Nodo(i32, Box), + Vacia, + } + ``` + +### Conclusión + +Los enums en Rust son una herramienta poderosa y flexible para representar datos que pueden tener múltiples variantes. Su integración con el pattern matching permite escribir código más seguro y expresivo, y su eficiencia en memoria asegura un rendimiento óptimo. Al comprender y utilizar los enums, los desarrolladores pueden aprovechar al máximo las capacidades de Rust para crear programas robustos y eficientes. diff --git a/content/2.basic/27.string.md b/content/2.basic/27.string.md new file mode 100644 index 0000000..fdd86c2 --- /dev/null +++ b/content/2.basic/27.string.md @@ -0,0 +1,136 @@ +--- +title: 'String' +description: 'El Tipo de Dato `String` en Rust: Un Análisis Completo' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: 145 + y: 560 + # width: 320 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch08-02-strings' + - name: 'Post acerca del manejo correcto de Strings en Rust' + english: false + link: 'https://blog.rustlang-es.org/articles/strings' + - name: 'Comprehensive Rust' + english: false + link: 'https://google.github.io/comprehensive-rust/es/std-types/string.html' + - name: 'Documentacion Oficial sobre el Formateo de texto' + english: true + link: 'https://doc.rust-lang.org/std/fmt' +--- +## El Tipo de Dato `String` en Rust + +El tipo de dato `String` en Rust es una representación dinámica de una cadena de texto, gestionada en heap, lo que le permite crecer y reducirse en tamaño en tiempo de ejecución. En este post, exploraremos cómo funciona `String` en memoria, cuándo es recomendable usarlo, por qué no tiene la trait `Copy` implementada, el concepto de `Cow`, el uso del macro `format!`, y el coste computacional de su uso, destacando la eficiencia de Rust. + +### ¿Qué es `String` en Rust? + +En Rust, `String` es un tipo de dato dinámico que permite almacenar y manipular cadenas de texto. A diferencia del tipo `&str`, que es una referencia a una cadena de texto inmutable y con una longitud fija, `String` es mutable y puede crecer o reducirse en tamaño. + +### Funcionamiento en Memoria + +Una instancia de `String` en Rust se compone de tres partes principales: + +1. **Puntero a los datos:** Apunta al comienzo del bloque de memoria donde se almacenan los datos de la cadena en el heap. +2. **Capacidad:** Indica cuánta memoria ha sido reservada para la cadena, lo que puede ser más que la longitud actual de la cadena. +3. **Longitud:** Representa el número de bytes utilizados actualmente en el bloque de memoria. + +#### Gráfico: Representación en Memoria de un `String` + +```plaintext ++-----------------+ +| Puntero | --> [ H e l l o ] ++-----------------+ +| Capacidad: 10 | ++-----------------+ +| Longitud: 5 | ++-----------------+ +``` + +### ¿Cuándo Usar `String`? + +Es recomendable usar `String` cuando: + +- Necesitas una cadena de texto mutable. +- La longitud de la cadena no es conocida en tiempo de compilación. +- La cadena de texto necesita ser modificada, concatenada o manipulada dinámicamente. + +Para cadenas de texto estáticas y de longitud conocida, es preferible usar `&str`, ya que es más eficiente en términos de memoria y rendimiento. + +### Por Qué `String` No Implementa `Copy` + +La trait `Copy` en Rust indica que una variable puede ser copiada simplemente haciendo una copia bit a bit de su valor. Sin embargo, `String` no implementa `Copy` debido a que copiar un `String` implicaría duplicar el bloque de memoria en el heap al que apunta. Esto sería costoso en términos de rendimiento y podría causar problemas de administración de memoria, como fugas de memoria si no se maneja correctamente. + +### El Concepto de `Cow` + +`Cow` (Clone on Write) es un tipo en Rust que permite optimizar las operaciones que pueden requerir clonación de datos. `Cow` puede ser una referencia inmutable a los datos o una copia de los datos si se necesita una versión mutable. + +#### Ejemplo de Uso de `Cow` + +```rust +use std::borrow::Cow; + +fn procesar_texto(texto: &str) -> Cow { + if texto.contains("rust") { + let mut owned = texto.to_owned(); + owned.push_str(" is awesome!"); + Cow::Owned(owned) + } else { + Cow::Borrowed(texto) + } +} + +fn main() { + let texto = "I love rust"; + let resultado = procesar_texto(texto); + println!("{}", resultado); +} +``` + +### El Macro `format!` + +El macro `format!` en Rust es similar a `printf` en C y otros lenguajes. Permite construir cadenas de texto a partir de variables y literales. + +#### Ejemplo de Uso de `format!` + +```rust +fn main() { + let nombre = "Alice"; + let edad = 30; + let mensaje = format!("Hola, {nombre}. Tienes {edad} años."); + println!("{mensaje}"); +} +``` + +### Coste Computacional de Usar `String` + +Manipular cadenas de texto dinámicas con `String` implica ciertas operaciones de gestión de memoria que pueden ser costosas: + +- **Asignación en el Heap:** Crear y expandir una `String` implica asignar y posiblemente realocar memoria en el heap. +- **Copia de Datos:** Operaciones que duplican o concatenan `String`s pueden ser costosas si se requiere copiar grandes cantidades de datos. + +Sin embargo, Rust es extremadamente eficiente en la gestión de memoria y rendimiento gracias a su sistema de préstamos y propiedades de tiempo de vida, que minimizan las asignaciones y copias innecesarias. + +### Optimización y Eficiencia en Rust + +A pesar de los posibles costes computacionales, Rust proporciona varias características que optimizan el uso de `String`: + +- **Reserva Anticipada:** Usar `String::with_capacity` permite reservar memoria anticipadamente, reduciendo la necesidad de realocaciones. + + ```rust + fn main() { + let mut s = String::with_capacity(10); + s.push_str("Hola"); + println!("{}", s); + } + ``` + +- **Métodos Eficientes:** Los métodos de `String` están optimizados para operaciones comunes, como concatenación y búsqueda, aprovechando las características de bajo nivel de Rust. + +### Conclusión + +El tipo de dato `String` en Rust es una herramienta poderosa para trabajar con cadenas de texto dinámicas. Aunque puede implicar costes computacionales debido a la gestión de memoria, Rust optimiza estas operaciones para asegurar eficiencia. Al entender cómo funciona `String` en memoria, cuándo usarlo y cómo optimizar su uso, los desarrolladores pueden aprovechar al máximo las capacidades de Rust para manejar cadenas de texto de manera efectiva y segura. diff --git a/content/2.basic/28.range.md b/content/2.basic/28.range.md new file mode 100644 index 0000000..60e65d4 --- /dev/null +++ b/content/2.basic/28.range.md @@ -0,0 +1,135 @@ +--- +title: 'Rangos' +description: 'Explorando los Rangos en Rust: Uso en Slices, Vectores, Iteraciones y Más' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: -50 + y: 600 + # width: 320 + externalLinks: + - name: 'Libro Oficial - Match Binding' + english: false + link: 'https://book.rustlang-es.org/ch18-03-pattern-syntax#-bindings' + - name: 'Libro de Referencia Rust' + english: true + link: 'https://doc.rust-lang.org/reference/expressions/range-expr.html' + - name: 'Documentacion Oficial sobre los Rangos' + english: true + link: 'https://doc.rust-lang.org/std/ops/struct.Range.html' +--- +## Rangos en Rust + +En Rust, los rangos son una característica poderosa y versátil que facilita la manipulación de secuencias y colecciones de datos. Los rangos pueden ser utilizados en diversas situaciones, incluyendo slices, vectores, iteraciones, validaciones, y más. Este blog post ofrece una exploración detallada de los rangos en Rust, mostrando ejemplos prácticos y destacando su utilidad en diferentes contextos. + +> [!IMPORTANT] +> Los rangos son una característica mas que un Tipo. + +### Concepto Básico de Rangos en Rust + +Un rango en Rust se define utilizando la sintaxis `start..end` o `start..=end`, donde `start` es el valor inicial del rango y `end` es el valor final. La diferencia clave entre estas dos sintaxis es que `..` excluye el valor final, mientras que `..=` lo incluye. + +#### Ejemplo: + +```rust +fn main() { + let rango_exclusivo = 0..5; // 0, 1, 2, 3, 4 + let rango_inclusivo = 0..=5; // 0, 1, 2, 3, 4, 5 + + println!("Rango exclusivo: {:?}", rango_exclusivo); + println!("Rango inclusivo: {:?}", rango_inclusivo); +} +``` + +### Uso de Rangos en Slices + +Los rangos son particularmente útiles cuando se trabaja con slices de arreglos. Permiten seleccionar una subsección de un arreglo de manera simple y eficiente. + +#### Ejemplo: + +```rust +fn main() { + let arreglo = [1, 2, 3, 4, 5]; + let slice = &arreglo[1..4]; // Selecciona los elementos en las posiciones 1, 2 y 3 + + println!("Slice: {:?}", slice); // Output: [2, 3, 4] +} +``` + +### Rangos en Vectores + +De manera similar a los slices, los rangos también pueden ser utilizados para obtener subvecotres de un vector. Esto es útil cuando se necesita manipular o acceder a una parte específica del vector. + +#### Ejemplo: + +```rust +fn main() { + let vector = vec![10, 20, 30, 40, 50]; + let subvector = &vector[2..]; // Selecciona los elementos desde la posición 2 hasta el final + + println!("Subvector: {:?}", subvector); // Output: [30, 40, 50] +} +``` + +### Iteraciones con Rangos + +Los rangos son frecuentemente utilizados para iterar sobre secuencias de números. Esto es muy útil en bucles `for`, donde se necesita ejecutar un bloque de código un número específico de veces. + +#### Ejemplo: + +```rust +fn main() { + for i in 0..5 { + println!("Número: {}", i); // Output: 0, 1, 2, 3, 4 + } + + for j in 0..=5 { + println!("Número: {}", j); // Output: 0, 1, 2, 3, 4, 5 + } +} +``` + +### Validaciones con Rangos + +Los rangos también se pueden utilizar para realizar validaciones, como verificar si un valor está dentro de un rango específico. + +#### Ejemplo: + +```rust +fn main() { + let edad = 25; + if (18..=30).contains(&edad) { + println!("La edad está dentro del rango permitido."); + } else { + println!("La edad está fuera del rango permitido."); + } +} +``` + +### Otras Situaciones Donde los Rangos Pueden Destacar + +1. **Generación de Números:** Los rangos pueden ser utilizados para generar secuencias de números fácilmente. +2. **Operaciones en Colecciones:** Facilitan la selección y manipulación de subconjuntos de datos en colecciones. +3. **Particionamiento de Datos:** Permiten dividir datos en partes más pequeñas de manera eficiente. + +### Gráficos: Visualizando Rangos + +#### Representación de Rangos en Slices + +```plaintext +Arreglo: [1, 2, 3, 4, 5] +Slice (1..4): [2, 3, 4] + ++---+---+---+---+---+ +| 1 | 2 | 3 | 4 | 5 | ++---+---+---+---+---+ + ^ ^ ^ ^ + | | | | + |---|---|---| +``` + +### Conclusión + +Los rangos en Rust son una herramienta versátil que facilita la manipulación y acceso a secuencias de datos. Desde slices y vectores hasta iteraciones y validaciones, los rangos ofrecen una manera simple y eficiente de trabajar con subconjuntos de datos. Al comprender y aprovechar las capacidades de los rangos, los desarrolladores pueden escribir código más limpio, eficiente y expresivo en Rust. diff --git a/content/2.basic/29.vector.md b/content/2.basic/29.vector.md new file mode 100644 index 0000000..845c9fd --- /dev/null +++ b/content/2.basic/29.vector.md @@ -0,0 +1,153 @@ +--- +title: 'Vec' +description: 'Explorando Vec en Rust: La Potente Colección de Vectores' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: 50 + y: 600 + # width: 320 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch08-01-vectors' + - name: 'Comprehensive Rust' + english: false + link: 'https://google.github.io/comprehensive-rust/es/std-types/vec.html' + - name: 'Documentacion Oficial' + english: true + link: 'https://doc.rust-lang.org/std/vec/struct.Vec.html' +--- +## Vec en Rust: La Potente Colección de Vectores + +El tipo `Vec` en Rust es una colección dinámica y flexible que permite almacenar una secuencia de elementos del mismo tipo. `Vec` es una parte fundamental de la programación en Rust, proporcionando una manera eficiente de manejar listas de datos que pueden crecer y reducirse dinámicamente. En este blog post, exploraremos el funcionamiento de `Vec`, cómo se utiliza, su representación en memoria, sus ventajas, y algunos patrones de uso comunes. + +### ¿Qué es `Vec`? + +`Vec`, abreviatura de "vector", es un tipo de dato dinámico en Rust que permite almacenar una colección ordenada de elementos del mismo tipo. A diferencia de los arrays, que tienen un tamaño fijo, los vectores pueden cambiar de tamaño en tiempo de ejecución, lo que los hace extremadamente útiles para muchas aplicaciones. + +### Funcionamiento en Memoria + +Un `Vec` en Rust se compone de tres partes principales: + +1. **Puntero a los Datos:** Apunta al bloque de memoria en el heap donde se almacenan los elementos del vector. +2. **Capacidad:** Indica cuánta memoria ha sido reservada para el vector, lo que puede ser más que el número de elementos actualmente almacenados. +3. **Longitud:** Representa el número de elementos actualmente almacenados en el vector. + +#### Gráfico: Representación en Memoria de un `Vec` + +```plaintext ++-----------------+ +| Puntero | --> [ 1, 2, 3, 4, 5 ] ++-----------------+ +| Capacidad: 10 | ++-----------------+ +| Longitud: 5 | ++-----------------+ +``` + +### Creación y Uso de `Vec` + +Crear y manipular un `Vec` en Rust es sencillo y directo. Aquí hay algunos ejemplos básicos para ilustrar su uso: + +#### Ejemplo 1: Creación y Adición de Elementos + +```rust +fn main() { + let mut vec = Vec::new(); // Crear un nuevo vector vacío + vec.push(1); // Añadir elementos al vector + vec.push(2); + vec.push(3); + + println!("{:?}", vec); // Output: [1, 2, 3] +} +``` + +#### Ejemplo 2: Acceso a Elementos + +```rust +fn main() { + let vec = vec![1, 2, 3, 4, 5]; + let primero = vec[0]; // Acceso directo a un elemento + let ultimo = vec[vec.len() - 1]; // Acceso al último elemento + + // Acceder a elementos utilizando métodos seguros + if let Some(primer_numero) = numeros.get(0) { + println!("Primer número: {}", primer_numero); + } + + // Modificar un elemento + if let Some(segundo_numero) = numeros.get_mut(1) { + *segundo_numero = 25; + } + + println!("Primer elemento: {}", primero); // Output: 1 + println!("Último elemento: {}", ultimo); // Output: 5 +} +``` + +#### Ejemplo 3: Iteración sobre un `Vec` + +```rust +fn main() { + let vec = vec![10, 20, 30, 40]; + for valor in &vec { + println!("{}", valor); + } +} +``` + +### Ventajas de Usar `Vec` + +- **Flexibilidad Dinámica:** Los vectores pueden crecer y reducirse en tamaño dinámicamente, lo que los hace adecuados para situaciones donde la cantidad de datos no es conocida de antemano. +- **Eficiencia de Memoria:** `Vec` gestiona eficientemente la memoria al reservar bloques de memoria y redimensionarlos según sea necesario. +- **Compatibilidad con Iteradores:** Los vectores se integran bien con el sistema de iteradores de Rust, permitiendo un manejo eficiente y seguro de colecciones de datos. + + +### Métodos Seguros para Acceder a Elementos + +En Rust, acceder a elementos utilizando `vector[n]` es inseguro porque puede conducir a errores de tiempo de ejecución si el índice `n` está fuera del rango válido del `Vec`. Es preferible utilizar métodos seguros proporcionados por Rust, como `get()`, `get_mut()`, y otros, que garantizan que no se produzcan desbordamientos de índice. + +### Métodos Comunes de `Vec` + +- `get(index)`: Devuelve una referencia al elemento en la posición `index`, o `None` si el índice está fuera del rango. +- `get_mut(index)`: Devuelve una referencia mutable al elemento en la posición `index`, o `None` si el índice está fuera del rango. +- `first()`: Devuelve una referencia al primer elemento del `Vec`, o `None` si el `Vec` está vacío. +- `last()`: Devuelve una referencia al último elemento del `Vec`, o `None` si el `Vec` está vacío. +- `push()`: Añade un elemento al final del vector. +- `pop()`: Elimina y devuelve el último elemento del vector. +- `len()`: Devuelve el número de elementos en el vector. +- `is_empty()`: Verifica si el vector está vacío. +- `insert()`: Inserta un elemento en una posición específica. +- `remove()`: Elimina un elemento en una posición específica. + +#### Ejemplo: Métodos Comunes + +```rust +fn main() { + let mut vec = vec![1, 2, 3]; + vec.push(4); // Añadir un elemento + vec.insert(1, 5); // Insertar 5 en la posición 1 + vec.remove(2); // Eliminar el elemento en la posición 2 (que es el 2) + + println!("{:?}", vec); // Output: [1, 5, 3, 4] +} +``` + +### Seguridad y Prevención de Errores + +El uso de métodos seguros como `get()` y `get_mut()` ayuda a prevenir errores de acceso a memoria no válida que podrían ocurrir en otros lenguajes donde acceder a elementos por índice no está tan controlado. Rust enfatiza la seguridad y la prevención de errores en tiempo de compilación, lo que es fundamental para escribir código robusto y confiable. + +### Diferencia con Arrays + +Los arrays en Rust tienen un tamaño fijo determinado en tiempo de compilación, mientras que los vectores pueden cambiar de tamaño en tiempo de ejecución. Esto hace que los vectores sean más adecuados para colecciones de datos dinámicos. + +### Coste Computacional de `Vec` + +Manipular vectores implica ciertas operaciones de gestión de memoria, especialmente cuando se redimensionan. Sin embargo, Rust optimiza estas operaciones para minimizar el coste computacional, reservando bloques de memoria de manera eficiente y evitando realocaciones innecesarias. + +### Conclusión + +`Vec` es una herramienta esencial en Rust para trabajar con colecciones de datos dinámicas. Su flexibilidad, eficiencia y compatibilidad con el sistema de iteradores de Rust lo hacen adecuado para una amplia gama de aplicaciones. Al comprender cómo funciona `Vec` en memoria, sus ventajas y cómo utilizar sus métodos comunes, los desarrolladores pueden aprovechar al máximo las capacidades de Rust para manejar colecciones de datos de manera efectiva y segura. diff --git a/content/2.basic/3.const-and-statics.md b/content/2.basic/3.const-and-statics.md new file mode 100644 index 0000000..6571680 --- /dev/null +++ b/content/2.basic/3.const-and-statics.md @@ -0,0 +1,106 @@ +--- +title: 'Constantes y Variables Estáticas' +description: 'Constantes y variables Estáticas en Rust' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: -700 + y: 280 + width: 320 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch03-01-variables-and-mutability#constantes' + - name: 'Variables y declaraciones en Rust' + english: false + link: 'https://blog.rustlang-es.org/articles/variables-y-declaraciones' + - name: 'Libro de Referencia - Constants' + english: true + link: 'https://doc.rust-lang.org/reference/items/constant-items.html' + - name: 'Libro de Referencia - Statics' + english: true + link: 'https://doc.rust-lang.org/reference/items/static-items.html' +--- +# Constantes y Variables Estáticas en Rust + +Rust es un lenguaje de programación de sistemas conocido por su seguridad y rendimiento. Una de las características importantes que Rust ofrece para manejar valores que no cambian son las **constantes** y las **variables estáticas**. En este post, exploraremos qué son, cómo se declaran, cómo se almacenan en memoria y qué implicancias tienen, especialmente las constantes evaluadas en tiempo de compilación. + +## Constantes en Rust + +### Declaración y Uso + +Las constantes en Rust se declaran usando la palabra clave `const`. A diferencia de las variables normales, las constantes deben ser siempre inmutables y su tipo debe ser anotado explícitamente. Se pueden declarar en cualquier ámbito, incluyendo dentro de funciones. + +```rust +const MAX_POINTS: u32 = 100_000; +``` + +En este ejemplo, `MAX_POINTS` es una constante de tipo `u32` con un valor de `100,000`. Las constantes pueden ser de cualquier tipo primitivo o compuesto (como arreglos y tuplas), siempre y cuando su valor pueda ser evaluado en tiempo de compilación. + +### Evaluación en Tiempo de Compilación + +Una de las grandes ventajas de las constantes en Rust es que son evaluadas en tiempo de compilación. Esto significa que el valor de la constante se calcula y se inserta directamente en el binario generado, eliminando la necesidad de calcular el valor en tiempo de ejecución, lo cual puede mejorar el rendimiento. + +```rust +const SECONDS_IN_A_DAY: u32 = 24 * 60 * 60; +``` + +Aquí, `SECONDS_IN_A_DAY` se calcula durante la compilación, asegurando que el cálculo no tenga que realizarse repetidamente en tiempo de ejecución. + +## Variables Estáticas en Rust + +### Declaración y Uso + +Las variables estáticas se declaran con la palabra clave `static`. A diferencia de las constantes, las variables estáticas pueden ser mutables (aunque esto es raramente recomendado debido a cuestiones de seguridad y concurrencia). + +```rust +static HELLO_WORLD: &str = "Hello, world!"; +``` + +En este ejemplo, `HELLO_WORLD` es una variable estática de tipo `&str`. + +### Variables Estáticas Mutables + +Para declarar una variable estática mutable, se usa la palabra clave `mut` después de `static`. Es importante destacar que el acceso a variables estáticas mutables no es seguro por defecto y requiere el uso de bloques `unsafe`. + +```rust +static mut COUNTER: u32 = 0; + +unsafe { + COUNTER += 1; +} +``` + +> El uso de `unsafe` indica al compilador que el programador asegura manualmente que el acceso concurrente está controlado, lo que puede llevar a errores si no se maneja adecuadamente. + +## Almacenamiento en Memoria + +### Constantes + +Las constantes, debido a su naturaleza inmutable y evaluada en tiempo de compilación, generalmente se almacenan directamente en el segmento de texto del binario, que es una sección de solo lectura de la memoria. Esto permite un acceso rápido y eficiente a estos valores en tiempo de ejecución. + +### Variables Estáticas + +Las variables estáticas, tanto mutables como inmutables, se almacenan en el segmento de datos estáticos del binario. Este segmento es parte de la memoria que se mantiene durante toda la ejecución del programa, lo que significa que las variables estáticas existen durante toda la vida del programa. + +## Implicancias de Uso + +### Seguridad y Rendimiento + +El uso de constantes es muy seguro y eficiente en Rust debido a su evaluación en tiempo de compilación y su almacenamiento en el segmento de solo lectura. Esto reduce el riesgo de errores y mejora el rendimiento. + +Las variables estáticas, aunque útiles para ciertos casos, deben usarse con precaución, especialmente las mutables, debido a los posibles problemas de concurrencia y seguridad que pueden surgir. El uso de `unsafe` para acceder a variables estáticas mutables subraya la necesidad de manejar estos accesos con extremo cuidado. + +### Mejores Prácticas + +- **Prefiere constantes sobre variables estáticas siempre que sea posible**: Las constantes son más seguras y más eficientes. +- **Minimiza el uso de variables estáticas mutables**: Si necesitas mutabilidad global, considera otras alternativas como el uso de estructuras de datos seguras para concurrencia (`Mutex`, `RwLock`, etc.). +- **Aprovecha la evaluación en tiempo de compilación**: Utiliza constantes para valores que pueden ser computados en tiempo de compilación para aprovechar al máximo las optimizaciones del compilador. + +## Conclusión + +Entender la diferencia entre constantes y variables estáticas en Rust, así como su almacenamiento y uso adecuado, es crucial para escribir código eficiente y seguro. Las constantes, con su evaluación en tiempo de compilación, proporcionan una forma poderosa de definir valores inmutables, mientras que las variables estáticas ofrecen una solución para la persistencia de datos a nivel global, aunque con ciertas advertencias de uso. + +Rust nos ofrece herramientas poderosas y flexibles para manejar inmutabilidad y persistencia de datos, y aprender a usarlas correctamente es esencial para aprovechar todo el potencial del lenguaje. diff --git a/content/2.basic/30.hashmap.md b/content/2.basic/30.hashmap.md new file mode 100644 index 0000000..56704d7 --- /dev/null +++ b/content/2.basic/30.hashmap.md @@ -0,0 +1,146 @@ +--- +title: 'HashMap' +description: 'Explorando HashMap en Rust: Almacenamiento y Manipulación Eficiente de Datos Asociativos' +draft: true +data: + type: 'custom' + topicLevel: 'medium' + position: + x: 120 + y: 600 + # width: 320 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch08-03-hash-maps' + - name: 'Comprehensive Rust' + english: true + link: 'https://google.github.io/comprehensive-rust/es/std-types/hashmap.html' + - name: 'Documentacion Oficial' + english: true + link: 'https://doc.rust-lang.org/std/collections/struct.HashMap.html' +--- +## HashMap en Rust: Almacenamiento y Manipulación Eficiente de Datos Asociativos + +En Rust, el tipo `HashMap` es una colección poderosa que permite almacenar pares clave-valor de manera eficiente. Los `HashMap` son ampliamente utilizados cuando se necesita un acceso rápido y eficiente a los datos basados en claves únicas. En este blog post, exploraremos el funcionamiento de los `HashMap` en Rust, cómo se utilizan, su representación en memoria, sus ventajas, y algunos patrones de uso comunes. + +### ¿Qué es un `HashMap`? + +Un `HashMap` es una colección que almacena pares clave-valor, donde cada clave es única y se asocia con un valor. Este tipo de datos es ideal para situaciones en las que se necesita acceder, insertar o eliminar elementos rápidamente mediante una clave. + +### Funcionamiento en Memoria + +Internamente, un `HashMap` se implementa utilizando una tabla hash. La clave se pasa a través de una función hash para determinar la ubicación en la tabla donde se almacenará el valor correspondiente. Esto permite operaciones de búsqueda, inserción y eliminación muy eficientes. + +#### Gráfico: Representación en Memoria de un `HashMap` + +```plaintext +HashMap: ++-------+------+-------+------+ +| Clave | Hash | Índice | Valor | ++-------+------+-------+------+ +| "a" | 1234 | 0 | 10 | +| "b" | 5678 | 1 | 20 | +| "c" | 9101 | 2 | 30 | ++-------+------+-------+------+ +``` + +### Creación y Uso de `HashMap` + +Crear y manipular un `HashMap` en Rust es sencillo. A continuación, se muestran algunos ejemplos básicos: + +#### Ejemplo 1: Creación y Adición de Elementos + +```rust +use std::collections::HashMap; + +fn main() { + let mut mapa = HashMap::new(); // Crear un nuevo HashMap vacío + mapa.insert("clave1", 10); // Insertar pares clave-valor + mapa.insert("clave2", 20); + mapa.insert("clave3", 30); + + println!("{:?}", mapa); // Output: {"clave1": 10, "clave2": 20, "clave3": 30} +} +``` + +#### Ejemplo 2: Acceso a Elementos + +```rust +use std::collections::HashMap; + +fn main() { + let mut mapa = HashMap::new(); + mapa.insert("clave1", 10); + mapa.insert("clave2", 20); + + if let Some(valor) = mapa.get("clave1") { + println!("El valor para 'clave1' es: {}", valor); // Output: El valor para 'clave1' es: 10 + } +} +``` + +#### Ejemplo 3: Iteración sobre un `HashMap` + +```rust +use std::collections::HashMap; + +fn main() { + let mut mapa = HashMap::new(); + mapa.insert("clave1", 10); + mapa.insert("clave2", 20); + mapa.insert("clave3", 30); + + for (clave, valor) in &mapa { + println!("Clave: {}, Valor: {}", clave, valor); + } +} +``` + +### Ventajas de Usar `HashMap` + +- **Acceso Rápido:** Los `HashMap` proporcionan un acceso constante promedio O(1) a los valores mediante sus claves, lo que los hace muy eficientes para búsquedas rápidas. +- **Flexibilidad:** Permiten almacenar pares clave-valor de cualquier tipo, siempre que las claves implementen las traits `Eq` y `Hash`. +- **Versatilidad:** Son útiles en una amplia gama de aplicaciones, desde bases de datos simples hasta sistemas de caché y configuraciones. + +### Métodos Comunes de `HashMap` + +- `insert()`: Añade un par clave-valor al `HashMap`. +- `get()`: Recupera un valor asociado con una clave específica. +- `remove()`: Elimina un par clave-valor del `HashMap`. +- `contains_key()`: Verifica si una clave está presente en el `HashMap`. +- `entry()`: Proporciona una manera eficiente de insertar o modificar un valor basado en una clave. + +#### Ejemplo: Métodos Comunes + +```rust +use std::collections::HashMap; + +fn main() { + let mut mapa = HashMap::new(); + mapa.insert("clave1", 10); + + // Verificar si una clave existe + if mapa.contains_key("clave1") { + println!("'clave1' existe en el mapa."); + } + + // Uso de entry para insertar o modificar un valor + mapa.entry("clave2").or_insert(20); + mapa.entry("clave1").and_modify(|v| *v += 10); + + println!("{:?}", mapa); // Output: {"clave1": 20, "clave2": 20} +} +``` + +### Diferencia con Otros Tipos de Colección + +A diferencia de los vectores (`Vec`), que almacenan elementos en un orden específico y permiten el acceso mediante índices, los `HashMap` almacenan elementos en base a claves únicas y permiten el acceso mediante estas claves. Esto los hace más adecuados para aplicaciones donde se necesita acceso rápido a valores específicos basados en claves, en lugar de operaciones de secuencia ordenada. + +### Coste Computacional de `HashMap` + +El uso de `HashMap` implica un coste computacional para calcular las funciones hash y manejar posibles colisiones. Sin embargo, Rust optimiza estas operaciones para mantener el acceso a los datos lo más eficiente posible. La mayoría de las operaciones tienen un coste promedio de O(1), lo que las hace muy eficientes en la práctica. + +### Conclusión + +`HashMap` es una herramienta esencial en Rust para trabajar con colecciones de datos asociativos. Su flexibilidad, eficiencia y compatibilidad con las claves únicas lo hacen adecuado para una amplia gama de aplicaciones. Al comprender cómo funciona `HashMap` en memoria, sus ventajas y cómo utilizar sus métodos comunes, los desarrolladores pueden aprovechar al máximo las capacidades de Rust para manejar datos de manera efectiva y segura. diff --git a/content/2.basic/4.shadowing.md b/content/2.basic/4.shadowing.md new file mode 100644 index 0000000..e50d1c2 --- /dev/null +++ b/content/2.basic/4.shadowing.md @@ -0,0 +1,112 @@ +--- +title: 'Shadowing' +description: 'Shadowing de Variables en Rust: Concepto, Casos de Uso y Gestión en Memoria' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: -700 + y: 320 + width: 320 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch03-01-variables-and-mutability#shadowing' + - name: 'Comprehensive Rust' + english: false + link: 'https://google.github.io/comprehensive-rust/es/control-flow-basics/blocks-and-scopes.html?highlight=shado#%C3%81mbitos-y--shadowing' +--- +# Shadowing de Variables en Rust: Concepto, Casos de Uso y Gestión en Memoria + +Rust, conocido por su seguridad y eficiencia, también ofrece flexibilidad en la gestión de variables a través de una característica llamada **shadowing**. El shadowing permite declarar una nueva variable con el mismo nombre que una anterior en el mismo contexto, "sombreando" la variable original. En este artículo, exploraremos qué es el shadowing, cuándo y cómo se utiliza, y cómo se gestiona en memoria. + +## ¿Qué es el Shadowing? + +El shadowing en Rust ocurre cuando una nueva variable se declara con el mismo nombre que una anterior en el mismo contexto. Esto no debe confundirse con la mutabilidad; más bien, se trata de crear una nueva variable que oculta a la anterior, de alli el nombre `shadowing` ya que sombreamos la variable anterior. + +```rust +fn main() { + let x = 5; + let x = x + 1; + { + let x = x * 2; + println!("El valor de x en el contexto interno es: {}", x); // 12 + } + println!("El valor de x en el contexto externo es: {}", x); // 6 +} +``` + +En este ejemplo, la variable `x` es "sombreada" dos veces: una vez en el contexto principal y otra dentro del contexto interno. Cada declaración de `x` crea una nueva variable, dejando intactas las versiones anteriores dentro de su propio contexto. + +## Casos de Uso del Shadowing + +### Refinamiento de Tipos + +Uno de los usos comunes del shadowing es cambiar el tipo de una variable en una nueva declaración. + +```rust +let spaces = " "; +let spaces = spaces.len(); +``` + +Aquí, `spaces` primero es una cadena de texto, y luego se sombrea con una variable de tipo `usize`, que representa la longitud de la cadena. + +### Transformaciones Intermedias + +El shadowing es útil para aplicar transformaciones intermedias a una variable sin necesitar nombres adicionales. + +```rust +let price = 100; +let price = price * 2; +let price = price - 10; +``` + +En este ejemplo, `price` se actualiza en cada paso sin la necesidad de crear nuevas variables como `price1`, `price2`, etc. + +### Evitar la Mutabilidad + +El shadowing puede evitar la necesidad de usar variables mutables, lo cual es preferido en Rust por razones de seguridad. + +```rust +let mut count = 1; +count += 1; +``` + +se puede escribir como: + +```rust +let count = 1; +let count = count + 1; +``` + +## Interpretación en Memoria + +Cada vez que una variable es sombreada, Rust crea una nueva variable en el stack en lugar de modificar la existente. Esto implica que cada versión sombreada de la variable tiene su propia ubicación en memoria. + +> Esto es importante a tener en cuenta ya que no estamos reutilizando el espacio de memoria anteriormente reservado. + +### Gestión de Memoria + +1. **Contexto Principal**: Cuando `x` se declara por primera vez en el contexto principal, se asigna memoria en el stack. +2. **Shadowing en el Mismo Contexto**: Al sombrear `x` en el mismo contexto, se asigna una nueva ubicación en memoria para la nueva `x`, dejando intacta la primera. +3. **Contexto Interno**: En un contexto interno, otra nueva `x` se crea en el stack y se elimina al salir del contexto. + +Este comportamiento asegura que cada versión de la variable mantenga su valor original dentro de su propio contexto, y que las variables anteriores no sean modificadas accidentalmente. + +## Implicaciones y Mejores Prácticas + +### Ventajas del Shadowing + +- **Inmutabilidad**: Permite trabajar con variables inmutables, mejorando la seguridad y legibilidad del código. +- **Claridad**: Facilita la actualización de variables sin introducir nombres adicionales, lo cual puede hacer el código más claro y conciso. +- **Tipo Seguro**: Permite cambiar tipos de variables de manera segura y explícita. + +### Precauciones + +- **Confusión Potencial**: Un uso excesivo puede llevar a confusión sobre cuál es la variable actual. +- **Uso Intencionado**: Debe ser utilizado de manera intencionada y clara para mejorar la legibilidad y mantenimiento del código. + +## Conclusión + +El shadowing de variables en Rust es una característica poderosa que permite redefinir variables dentro del mismo contexto, proporcionando flexibilidad sin sacrificar la seguridad. Al comprender cómo funciona el shadowing y cómo se gestionan las variables en memoria, los desarrolladores pueden aprovechar esta herramienta para escribir código más limpio y eficiente. Utilizado correctamente, el shadowing puede simplificar transformaciones de variables y mejorar la inmutabilidad del código, haciendo que los programas en Rust sean más seguros y fáciles de mantener. diff --git a/content/2.basic/5.control-flow.md b/content/2.basic/5.control-flow.md new file mode 100644 index 0000000..cb56ca0 --- /dev/null +++ b/content/2.basic/5.control-flow.md @@ -0,0 +1,188 @@ +--- +title: 'Control de Flujo' +description: 'Control de Flujo en Rust, como usar if, if-else, match, loop, while y for' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: -700 + y: 360 + width: 320 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch03-05-control-flow' + - name: 'Comprehensive Rust' + english: false + link: 'https://google.github.io/comprehensive-rust/es/control-flow-basics.html' +--- +# Control de Flujo en Rust: if, if-else, match, loop, while y for + +Rust es un lenguaje de programación que combina seguridad y rendimiento con una sintaxis clara y poderosa. El control de flujo en Rust se maneja a través de estructuras conocidas como `if`, `if-else`, `match`, y diversos tipos de bucles (`loop`, `while`, `for`). En este artículo, exploraremos cada una de estas construcciones y cómo se utilizan para controlar el flujo de ejecución en un programa Rust, incluyendo los `named loops`. + +## Condicionales: if y if-else + +Las estructuras condicionales en Rust se manejan mediante `if` y `if-else`. Estas permiten ejecutar bloques de código basados en condiciones booleanas. + +### if + +La declaración `if` se utiliza para ejecutar un bloque de código solo si una condición es verdadera. + +```rust +let number = 7; + +if number < 10 { + println!("El número es menor que 10"); +} +``` + +En este ejemplo, el mensaje se imprimirá porque la condición `number < 10` es verdadera. + +### if-else + +La declaración `if-else` se utiliza cuando se desea ejecutar un bloque de código si la condición es verdadera y otro bloque si es falsa. + +```rust +let number = 7; + +if number < 10 { + println!("El número es menor que 10"); +} else { + println!("El número es mayor o igual a 10"); +} +``` + +Aquí, se imprime el primer mensaje porque la condición es verdadera. + +### if-else if-else + +Para manejar múltiples condiciones, se puede usar `else if`. + +```rust +let number = 15; + +if number < 10 { + println!("El número es menor que 10"); +} else if number < 20 { + println!("El número está entre 10 y 19"); +} else { + println!("El número es 20 o mayor"); +} +``` + +Este ejemplo imprimirá el segundo mensaje porque la condición `number < 20` es verdadera. + +## match + +La declaración `match` en Rust es una potente estructura de control de flujo que permite manejar múltiples condiciones de manera más clara y concisa que múltiples `if-else`. + +```rust +let number = 3; + +match number { + 1 => println!("Uno"), + 2 => println!("Dos"), + 3 => println!("Tres"), + _ => println!("Otro número"), +} +``` + +En este ejemplo, se imprimirá "Tres" porque `number` es igual a 3. El carácter `_` actúa como un comodín que coincide con cualquier valor no especificado anteriormente. + +## Bucles + +Rust proporciona varias maneras de repetir bloques de código: `loop`, `while`, y `for`. + +### loop + +El bucle `loop` ejecuta un bloque de código indefinidamente hasta que se encuentre una declaración `break`. + +```rust +let mut counter = 0; + +loop { + counter += 1; + if counter == 10 { + break; + } +} + +println!("El contador llegó a: {}", counter); +``` + +Este bucle incrementa `counter` hasta que alcanza 10, momento en el cual se sale del bucle con `break`. + +#### Named Loops + +Rust permite etiquetar bucles usando `'label_name` para poder referirse a ellos específicamente, lo que es útil para romper o continuar bucles externos desde un bucle interno. + +```rust +let mut count = 0; +'outer: loop { + let mut remaining = 10; + + loop { + if remaining == 9 { + break; + } + if count == 2 { + break 'outer; + } + remaining -= 1; + } + + count += 1; +} + +println!("Count terminó en: {}", count); +``` + +En este ejemplo, `break 'outer` rompe el bucle etiquetado como `'outer`, terminando ambos bucles cuando `count` es igual a 2. + +### while + +El bucle `while` ejecuta un bloque de código mientras una condición sea verdadera. + +```rust +let mut number = 3; + +while number != 0 { + println!("{}!", number); + number -= 1; +} + +println!("¡Despegue!"); +``` + +En este ejemplo, el bucle `while` decrece `number` hasta que llega a 0, imprimiendo cada número en el proceso. + +### for + +El bucle `for` en Rust se utiliza para iterar sobre una colección, como un rango o una lista. + +#### Iterando sobre un rango + +```rust +for number in 1..4 { + println!("El número es: {}", number); +} +``` + +Este bucle imprimirá "El número es: 1", "El número es: 2" y "El número es: 3". El rango `1..4` incluye 1 pero excluye 4. + +#### Iterando sobre una colección + +```rust +let array = [10, 20, 30, 40, 50]; + +for element in array.iter() { + println!("El valor es: {}", element); +} +``` + +Aquí, el bucle `for` itera sobre los elementos del arreglo `array`, imprimiendo cada uno de ellos. + +## Conclusión + +El control de flujo en Rust es versátil y poderoso, permitiendo a los desarrolladores manejar condiciones y bucles de manera clara y eficiente. Las estructuras `if`, `if-else` y `match` facilitan la toma de decisiones, mientras que los bucles `loop`, `while` y `for` permiten la repetición de bloques de código con gran flexibilidad. Los named loops añaden una capa adicional de control para manejar bucles anidados de manera más precisa. Comprender estas construcciones es esencial para escribir programas Rust robustos y eficientes. diff --git a/content/2.basic/6.function-clousures.md b/content/2.basic/6.function-clousures.md new file mode 100644 index 0000000..f70f231 --- /dev/null +++ b/content/2.basic/6.function-clousures.md @@ -0,0 +1,171 @@ +--- +title: 'Funciones y Closures en Rust' +description: 'Funciones y Closures en Rust' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: -700 + y: 400 + width: 320 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch03-03-how-functions-work' +--- +### Entendiendo Funciones y Closures en Rust + +Rust es un lenguaje de programación que se destaca por su seguridad en la memoria sin necesidad de un recolector de basura. Uno de los componentes fundamentales en Rust son las funciones y closures (o clausuras). Este post explora cómo funcionan, cómo manejan la memoria, cómo se mueven las variables, y las estrategias de optimización para su uso correcto. + +#### Funciones en Rust + +Las funciones en Rust se definen utilizando la palabra clave `fn`, seguida del nombre de la función, los parámetros entre paréntesis y el cuerpo de la función entre llaves. Aquí hay un ejemplo simple: + +```rust +fn suma(a: i32, b: i32) -> i32 { + a + b +} +``` + +Las funciones en Rust son bastante similares a las de otros lenguajes, pero con algunas diferencias importantes debido a las garantías de seguridad en la memoria de Rust. Cuando se pasan parámetros a una función, se siguen las reglas de propiedad y préstamo de Rust, lo que asegura que no haya condiciones de carrera ni desreferencias nulas. + +#### Closures en Rust + +Las closures son funciones anónimas que pueden capturar variables del entorno en el que se definen. Se definen utilizando una sintaxis similar a las funciones, pero pueden inferir los tipos de sus parámetros y su valor de retorno: + +```rust +let closure = |a, b| a + b; +``` + +##### Captura de Variables + +Las closures pueden capturar variables de tres formas: por copia, por referencia mutable y por toma de propiedad. + +- **Por copia:** La closure toma una copia de la variable. Esto es posible si la variable implementa el trait `Copy`. + + ```rust + let x = 5; + let closure = move |a| a + x; + ``` + +- **Por referencia:** La closure toma una referencia de la variable, permitiendo su uso sin transferir la propiedad. + + ```rust + let x = 5; + let closure = |a| a + x; + ``` + +- **Por referencia mutable:** La closure toma una referencia mutable, permitiendo modificar la variable. + + ```rust + let mut x = 5; + let closure = |a| { + x += a; + x + }; + ``` + +##### Funcionamiento en Memoria + +Cuando se crea una closure, Rust decide cómo capturar las variables en función de cómo se utilizan dentro de la closure. Esto implica una estrategia de optimización que maximiza la eficiencia sin sacrificar la seguridad. + +- **Captura por valor (move):** La closure se convierte en una estructura que contiene las variables capturadas. Esto es útil cuando la closure se mueve a otro contexto, como un hilo. + +- **Captura por referencia:** La closure almacena referencias a las variables. Esto es más eficiente en términos de memoria, pero las referencias deben ser válidas durante la vida útil de la closure. + +##### Ejecución y Almacenamiento + +Las closures pueden almacenarse en variables, pasarse como parámetros a funciones y almacenarse en estructuras. Internamente, Rust puede optimizar closures pequeñas e inlinarlas en el lugar donde se usan, evitando la sobrecarga de una llamada de función. + +#### Estrategias de Optimización + +Rust realiza varias optimizaciones para mejorar la eficiencia de las funciones y closures: + +- **Inlining:** El compilador puede inlinar funciones y closures pequeñas, eliminando la sobrecarga de la llamada a función. + ```rs +#[inline(always)] + fn suma(a: i32, b: i32) -> i32 { + a + b + } + + fn main() { + let result = suma(5, 10); + println!("Resultado: {}", result); + } + ``` + +- **Monomorfización:** Para funciones genéricas y closures, Rust genera versiones específicas para cada tipo concreto que se utiliza, optimizando el código resultante. + ```rs + fn duplicar + Copy>(x: T) -> T { + x + x + } + + fn main() { + let entero = duplicar(5); // Genera una versión específica para i32 + let flotante = duplicar(5.0); // Genera una versión específica para f64 + println!("Duplicar entero: {}", entero); + println!("Duplicar flotante: {}", flotante); + } + ``` + + +- **Eliminación de código muerto:** Rust elimina las funciones y closures no utilizadas, reduciendo el tamaño del binario. + ```rs + fn funcion_utilizada() { + println!("Esta función se utiliza."); + } + + fn funcion_no_utilizada() { + println!("Esta función no se utiliza."); + } + + fn main() { + funcion_utilizada(); + } + ``` + + +#### Buenas Prácticas + +- **Usar referencias cuando sea posible:** Esto evita movimientos innecesarios de datos, mejorando el rendimiento. + ```rs + fn longitud(s: &String) -> usize { + s.len() + } + + fn main() { + let cadena = String::from("Hola, mundo!"); + let len = longitud(&cadena); // Se pasa una referencia a la función + println!("La longitud es: {}", len); + } + ``` + +- **Minimizar el alcance de las closures:** Limita el contexto capturado para evitar retener referencias más tiempo del necesario. + ```rs + fn main() { + let mut valores = vec![1, 2, 3, 4]; + { + let mut suma = 0; + valores.iter().for_each(|&x| suma += x); + println!("La suma es: {}", suma); + } + // La closure ha terminado y no retiene la referencia a `valores` más allá de este punto + valores.push(5); + println!("Valores actualizados: {:?}", valores); + } + ``` + +- **Preferir closures a funciones cuando se necesita capturar contexto:** Las closures permiten una mayor flexibilidad en la captura de variables locales. + ```rs + fn main() { + let factor = 10; + let multiplicar = |x| x * factor; // Captura `factor` del entorno + let resultado = multiplicar(5); + println!("El resultado es: {}", resultado); + } + ``` + +### Conclusión + +Las funciones y closures en Rust ofrecen un potente conjunto de herramientas para escribir código seguro y eficiente. Comprender cómo funcionan en memoria y cómo se mueven las variables es crucial para aprovechar al máximo estas características. Siguiendo las mejores prácticas y aprovechando las optimizaciones del compilador, se pueden escribir programas en Rust que sean tanto robustos como rápidos. diff --git a/content/2.basic/7.pattern-matching.md b/content/2.basic/7.pattern-matching.md new file mode 100644 index 0000000..9a77e10 --- /dev/null +++ b/content/2.basic/7.pattern-matching.md @@ -0,0 +1,174 @@ +--- +title: 'Pattern Matching/Desestruct' +description: 'Entendiendo Pattern Matching y Desestructuración en Rust: Exhaustividad y Seguridad' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: -700 + y: 440 + width: 320 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch06-02-match' + - name: 'Comprehensive Rust' + english: false + link: 'https://google.github.io/comprehensive-rust/es/pattern-matching/destructuring.html' +--- +### Entendiendo Pattern Matching y Desestructuración en Rust: Exhaustividad y Seguridad + +Rust es un lenguaje de programación que combina eficiencia y seguridad sin sacrificar el control sobre los detalles de bajo nivel. Una de las características más poderosas de Rust es su sistema de pattern matching y desestructuración. Estas herramientas permiten escribir código más expresivo y manejable, garantizando al mismo tiempo la exhaustividad en el control de flujo. En este blog post, exploraremos cómo funcionan el pattern matching y la desestructuración en Rust, así como su exhaustividad y beneficios. + +#### ¿Qué es Pattern Matching? + +Pattern matching es una característica que permite comparar una estructura de datos contra varios patrones y, si hay una coincidencia, descomponer la estructura en sus componentes. Rust utiliza la palabra clave `match` para pattern matching, lo que hace que el código sea más legible y manejable. + +##### Ejemplo Básico + +```rust +enum Color { + Red, + Green, + Blue, +} + +fn main() { + let color = Color::Green; + + match color { + Color::Red => println!("Color rojo"), + Color::Green => println!("Color verde"), + Color::Blue => println!("Color azul"), + } +} +``` + +En este ejemplo, `match` se utiliza para determinar el valor de la variable `color` y ejecutar el código correspondiente a cada variante del enum `Color`. + +#### Exhaustividad en Pattern Matching + +Una de las características más importantes del pattern matching en Rust es su exhaustividad. Esto significa que todos los casos posibles deben ser manejados, lo que garantiza que no se pase por alto ningún caso. Si se olvida un caso, el compilador emitirá un error, asegurando que se aborden todas las posibilidades. + +##### Ejemplo de Exhaustividad + +```rust +enum Animal { + Perro, + Gato, +} + +fn main() { + let mascota = Animal::Perro; + + match mascota { + Animal::Perro => println!("Es un perro"), + Animal::Gato => println!("Es un gato"), + // Falta un caso: Rust emitirá un error si se agrega una variante nueva sin manejarla + } +} +``` + +En este ejemplo, si se añadiera una nueva variante al enum `Animal`, como `Animal::Pez`, el compilador emitiría un error indicando que el match no es exhaustivo, obligando al desarrollador a manejar el nuevo caso. + +#### Desestructuración + +La desestructuración permite dividir una estructura de datos en sus componentes individuales. Rust facilita la desestructuración de arrays, tuplas, structs y enums, lo cual es útil para trabajar con datos complejos. + +##### Desestructuración de Tuplas + +```rust +fn main() { + let tupla = (1, "hola", 3.5); + + let (a, b, c) = tupla; + + println!("a: {}, b: {}, c: {}", a, b, c); +} +``` + +En este ejemplo, la tupla `(1, "hola", 3.5)` se desestructura en las variables `a`, `b` y `c`. + +##### Desestructuración de Structs + +```rust +struct Punto { + x: i32, + y: i32, +} + +fn main() { + let punto = Punto { x: 5, y: 10 }; + + let Punto { x, y } = punto; + + println!("x: {}, y: {}", x, y); +} +``` + +Aquí, el struct `Punto` se desestructura en sus campos `x` e `y`. + +#### Uso Avanzado de Pattern Matching + +Pattern matching en Rust no se limita a estructuras de datos simples, sino que puede manejar patrones complejos, incluyendo guardas, valores anidados y opciones. + +##### Match con Guardas + +```rust +fn main() { + let numero = Some(4); + + match numero { + Some(x) if x < 5 => println!("Menos que 5: {}", x), + Some(x) => println!("Mayor o igual a 5: {}", x), + None => println!("Sin valor"), + } +} +``` + +Las guardas (`if x < 5`) permiten añadir condiciones adicionales a los patrones. + +##### Patrones Anidados + +```rust +enum Mensaje { + Saludo { id: i32, contenido: String }, +} + +fn main() { + let mensaje = Mensaje::Saludo { + id: 1, + contenido: String::from("Hola"), + }; + + match mensaje { + Mensaje::Saludo { id, contenido } => { + println!("ID: {}, Contenido: {}", id, contenido); + } + } +} +``` + +Aquí se desestructura un enum con campos nombrados, extrayendo `id` y `contenido`. + +#### Pattern Matching en Funciones + +Pattern matching también se puede usar directamente en las firmas de las funciones para hacer el código más limpio. + +```rust +fn procesar_punto(Punto { x, y }: Punto) { + println!("x: {}, y: {}", x, y); +} + +fn main() { + let punto = Punto { x: 5, y: 10 }; + procesar_punto(punto); +} +``` + +En este ejemplo, el patrón se aplica directamente en los parámetros de la función `procesar_punto`. + +#### Conclusión + +El pattern matching y la desestructuración en Rust son herramientas poderosas que permiten manejar estructuras de datos complejas de manera más expresiva y eficiente. La exhaustividad del pattern matching asegura que todos los posibles casos se manejen adecuadamente, lo que contribuye a la seguridad y robustez del código. Comprender y utilizar estas características efectivamente puede mejorar significativamente la calidad y la mantenibilidad del código en Rust. diff --git a/content/2.basic/8.ownership-borrowing.yml b/content/2.basic/8.ownership-borrowing.yml new file mode 100644 index 0000000..44dde85 --- /dev/null +++ b/content/2.basic/8.ownership-borrowing.yml @@ -0,0 +1,14 @@ +title: 'Ownership y Borrwing' +description: '' +data: + type: 'transparent' + topicLevel: 'start' + position: + x: -700 + y: 520 + width: 320 + align: 'center' + sourcePosition: + none: 'top' + targetPosition: + basic: 'right' diff --git a/content/2.basic/9.ownership-borrowing-concept.md b/content/2.basic/9.ownership-borrowing-concept.md new file mode 100644 index 0000000..6869b0f --- /dev/null +++ b/content/2.basic/9.ownership-borrowing-concept.md @@ -0,0 +1,84 @@ +--- +title: 'Concepto Basico' +description: 'Entendiendo el Paradigma de Ownership y Borrowing en Rust' +draft: true +data: + type: 'custom' + topicLevel: 'start' + position: + x: -700 + y: 560 + width: 320 + externalLinks: + - name: 'Libro Oficial' + english: false + link: 'https://book.rustlang-es.org/ch04-00-understanding-ownership' + - name: 'Comprehensive Rust' + english: false + link: 'https://google.github.io/comprehensive-rust/es/borrowing.html' +--- +## Entendiendo el Paradigma de Ownership y Borrowing en Rust + +### Introducción + +Rust es un lenguaje de programación que ha ganado mucha popularidad por su enfoque en la seguridad y el rendimiento. Uno de los conceptos más revolucionarios y distintivos de Rust es su sistema de **ownership (propiedad)** y **borrowing (préstamo)**. Estos conceptos no solo representan un cambio de paradigma en la gestión de memoria y recursos, sino que también ofrecen garantías de seguridad que se validan en tiempo de compilación, eliminando muchos errores comunes en lenguajes como C y C++. + +### Ownership: Propiedad y Control Absoluto + +En Rust, cada valor tiene un único propietario, una variable que se encarga de gestionar ese valor. Cuando el propietario sale de su alcance (scope), el valor es automáticamente liberado. Este mecanismo se conoce como **RAII (Resource Acquisition Is Initialization)**, que asegura que los recursos se liberen correctamente sin necesidad de una intervención explícita del programador. + +Ejemplo básico: +```rust +fn main() { + let x = String::from("Hello"); + println!("{}", x); // x es el propietario de la cadena "Hello" +} // x sale del alcance y "Hello" es liberada automáticamente +``` + +### Borrowing: Préstamo y Compartición Controlada + +A veces es necesario compartir datos entre diferentes partes del programa sin transferir la propiedad. Aquí es donde entra el concepto de **borrowing**. En Rust, se puede pedir prestado un valor de dos formas: **prestamos inmutables** y **prestamos mutables**. + +- **Préstamos Inmutables:** Permiten múltiples accesos de solo lectura. + ```rust + fn main() { + let s = String::from("Hello"); + let len = calculate_length(&s); // Se presta de manera inmutable + println!("La longitud de '{}' es {}.", s, len); + } + + fn calculate_length(s: &String) -> usize { + s.len() // Solo se permite leer s + } + ``` + +- **Préstamos Mutables:** Permiten un único acceso de lectura/escritura. + ```rust + fn main() { + let mut s = String::from("Hello"); + change(&mut s); // Se presta de manera mutable + println!("{}", s); + } + + fn change(s: &mut String) { + s.push_str(", world"); + } + ``` + +### Cambio de Paradigma y Pensamiento + +El sistema de ownership y borrowing de Rust introduce un cambio significativo en la manera de pensar y escribir código. A diferencia de otros lenguajes donde la gestión de memoria se realiza manualmente o mediante un recolector de basura (garbage collector), Rust hace estas validaciones en tiempo de compilación. Esto significa que muchos errores comunes, como los accesos a memoria no válida, fugas de memoria y condiciones de carrera, se detectan antes de que el programa se ejecute. + +### Beneficios de las Validaciones en Compilación + +1. **Seguridad en Tiempo de Compilación:** El compilador de Rust garantiza que todas las reglas de ownership y borrowing se cumplan antes de que el código se ejecute. Esto elimina una gran clase de errores de programación que en otros lenguajes solo se detectarían en tiempo de ejecución. + +2. **Eliminación de Fugas de Memoria:** Al asegurar que cada recurso tiene un único propietario responsable de su liberación, Rust previene las fugas de memoria. + +3. **Prevención de Condiciones de Carrera:** Al restringir los accesos concurrentes a los datos mediante préstamos mutables, Rust evita las condiciones de carrera, un problema común en la programación concurrente. + +4. **Rendimiento Predecible:** Sin la sobrecarga de un recolector de basura, Rust puede ofrecer un rendimiento más predecible y eficiente, crucial en sistemas de tiempo real y aplicaciones de alto rendimiento. + +### Conclusión + +El sistema de ownership y borrowing de Rust no es solo una característica más; es un cambio de paradigma que redefine cómo pensamos y manejamos la memoria y los recursos en nuestros programas. Aunque puede requerir un tiempo de adaptación, las garantías y beneficios que ofrece en términos de seguridad y rendimiento hacen que valga la pena el esfuerzo. Rust demuestra que es posible tener un lenguaje que sea tanto seguro como de alto rendimiento, y lo hace mediante la validación de sus estrictas reglas en tiempo de compilación.