Skip to content

Commit

Permalink
feat: improve chapter 10 readability and added missing code example (#63
Browse files Browse the repository at this point in the history
)

Co-authored-by: Phosphorus Moscu <[email protected]>
  • Loading branch information
ivanlynch and Phosphorus-M authored May 14, 2024
1 parent bcba4d4 commit c96cae8
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 82 deletions.
30 changes: 15 additions & 15 deletions src/ch10-00-generics.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Tipos Genéricos, Traits y Lifetimes

Cada lenguaje de programación tiene herramientas para manejar eficazmente la
duplicación de conceptos. En Rust, una de esas herramientas son los *genéricos*:
duplicación de conceptos. En Rust, una de esas herramientas son los _genéricos_:
sustitutos abstractos de tipos concretos u otras propiedades. Podemos expresar
el comportamiento de los genéricos o cómo se relacionan con otros genéricos sin
saber qué estará en su lugar cuando se compile y ejecute el código.
Expand All @@ -20,12 +20,12 @@ partir de dos funciones que difieren solo en los tipos de sus parámetros.
También explicaremos cómo usar tipos genéricos en definiciones de structs y
enums.

Entonces aprenderás cómo usar *traits* para definir el comportamiento de una
Entonces aprenderás cómo usar _traits_ para definir el comportamiento de una
manera genérica. Puedes combinar traits con tipos genéricos para restringir un
tipo genérico para que acepte solo aquellos tipos que tienen un comportamiento
particular, en lugar de cualquier tipo.

Finalmente, discutiremos *lifetimes*: una variedad de genéricos que le dan al
Finalmente, discutiremos _lifetimes_: una variedad de genéricos que le dan al
compilador información sobre cómo se relacionan las referencias entre sí.
Lifetimes nos permiten darle al compilador suficiente información sobre los
valores prestados para que pueda garantizar que las referencias serán válidas
Expand All @@ -43,7 +43,7 @@ misma técnica para extraer una función genérica! Al observar cómo reconocer
código duplicado que puede usar en una función, comenzará a reconocer el
código duplicado que puede usar en los genéricos.

Comenzamos con un corto programa en el Listado 10-1 que encuentra el número
Comenzamos con un corto programa en el listado 10-1 que encuentra el número
más grande en una lista.

<span class="filename">Filename: src/main.rs</span>
Expand All @@ -52,7 +52,7 @@ más grande en una lista.
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-01/src/main.rs:here}}
```

<span class="caption">Listing 10-1: Encontrando el mayor número en una lista de
<span class="caption">Listado 10-1: Encontrando el mayor número en una lista de
números</span>

Almacenamos una lista de enteros en la variable `number_list` y colocamos una
Expand All @@ -66,18 +66,18 @@ de la lista, `largest` debería hacer referencia al número más grande, que en
este caso es 100.

Ahora se nos ha encargado encontrar el número más grande en dos listas de
números. Para hacerlo, podemos duplicar el código en el Listado 10-1 y usar la
números. Para hacerlo, podemos duplicar el código en el listado 10-1 y usar la
misma lógica en dos lugares diferentes en el programa, como se muestra en el
Listado 10-2.
listado 10-2.

<span class="filename">Filename: src/main.rs</span>

```rust
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-02/src/main.rs}}
```

<span class="caption">Listing 10-2: Código para encontrar el mayor número en
*dos* listas de números</span>
<span class="caption">Listado 10-2: Código para encontrar el mayor número en
_dos_ listas de números</span>

Aunque este código funciona, duplicar el código es tedioso y propenso a errores.
También tenemos que recordar actualizar el código en varios lugares cuando
Expand All @@ -88,9 +88,9 @@ función que opera en cualquier lista de enteros que se pase en un parámetro.
Esta solución hace que nuestro código sea más claro y nos permite expresar el
concepto de encontrar el número más grande en una lista de forma abstracta.

En el Listado 10-3, extraemos el código que encuentra el mayor número en una
En el listado 10-3, extraemos el código que encuentra el mayor número en una
función llamada `largest`. Luego llamamos a la función para encontrar el mayor
número en las dos listas del Listado 10-2. También podríamos usar la función
número en las dos listas del listado 10-2. También podríamos usar la función
en cualquier otra lista de valores `i32` que podríamos tener en el futuro.

<span class="filename">Filename: src/main.rs</span>
Expand All @@ -99,16 +99,16 @@ en cualquier otra lista de valores `i32` que podríamos tener en el futuro.
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-03/src/main.rs:here}}
```

<span class="caption">Listing 10-3: Código abstracto para encontrar el número
<span class="caption">Listado 10-3: Código abstracto para encontrar el número
mayor en dos listas</span>

La función `largest` tiene un parámetro llamado `list`, que representa cualquier
slice de valores `ì32` que podríamos pasar a la función. Como resultado, cuando
llamamos a la función, el código se ejecuta en los valores específicos que
pasamos.

En resumen, estos son los pasos que tomamos para cambiar el código del Listado
10-2 al Listado 10-3:
En resumen, estos son los pasos que tomamos para cambiar el código del listado
10-2 al listado 10-3:

1. Identificar código duplicado.
2. Extraer el código duplicado en el cuerpo de la función y especificar las
Expand All @@ -123,5 +123,5 @@ permiten que el código opere en tipos abstractos.

Por ejemplo, digamos que teníamos dos funciones: una que encuentra el mayor
elemento en un slices de valores `i32` y otra que encuentra el mayor elemento
en un slice de valores `char`. ¿Cómo eliminaríamos esa duplicación?
en un slice de valores `char`. ¿Cómo eliminaríamos esa duplicación?
¡Averigüémoslo!
43 changes: 20 additions & 23 deletions src/ch10-01-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ parámetros y el valor de retorno. Hacerlo hace que nuestro código sea más
flexible y brinda más funcionalidad a los llamadores de nuestra función al
tiempo que evita la duplicación de código.

Continuando con nuestra función `largest`, el Listado 10-4 muestra dos
funciones que encuentran el valor más grande en una rebanada. Luego
Continuando con nuestra función `largest`, el listado 10-4 muestra dos
funciones que encuentran el valor más grande en un slice. Luego
combinaremos estos en una sola función que usa genéricos.

<span class="filename">Filename: src/main.rs</span>
Expand All @@ -24,10 +24,10 @@ combinaremos estos en una sola función que usa genéricos.
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-04/src/main.rs:here}}
```

<span class="caption">Listing 10-4: Dos funciones que difieren solo en sus
<span class="caption">Listado 10-4: Dos funciones que difieren solo en sus
nombres y los tipos en sus firmas</span>

La función `largest_i32` es la que extrajimos en el Listado 10-3 que encuentra
La función `largest_i32` es la que extrajimos en el listado 10-3 que encuentra
el `i32` más grande en un slice. La función `largest_char` encuentra el
`char` más grande en un slice. Los cuerpos de las funciones tienen el mismo
código, así que eliminemos la duplicación introduciendo un parámetro de tipo
Expand Down Expand Up @@ -58,7 +58,7 @@ Leemos esta definición como: la función `largest` es genérico sobre algún ti
de tipo `T`. La función `largest` devolverá una referencia a un valor del mismo
tipo `T`.

El Listado 10-5 muestra la definición de la función `largest` combinada usando
El listado 10-5 muestra la definición de la función `largest` combinada usando
el tipo de datos genérico en su firma. La lista también muestra cómo podemos
llamar a la función con un slice de valores `i32` o valores `char`. Tenga en
cuenta que este código aún no se compilará, pero lo arreglaremos más adelante
Expand Down Expand Up @@ -94,7 +94,7 @@ implementa `PartialOrd` tanto en `i32` como en `char`.
### Definiciones In Struct

También podemos definir structs para usar tipos genéricos en uno o más campos
usando la sintaxis `<>`. El Listado 10-6 define un struct `Point<T>` para
usando la sintaxis `<>`. El listado 10-6 define un struct `Point<T>` para
contener valores `x` e `y` de cualquier tipo `T`.

<span class="filename">Filename: src/main.rs</span>
Expand All @@ -116,7 +116,7 @@ Ten en cuenta que porque hemos usado un solo tipo genérico para definir
`Point<T>`, esta definición dice que el struct `Point<T>` es genérico sobre algún
tipo `T`, y los campos `x` e `y` son _ambos_ ese mismo tipo, sea cual sea ese
tipo. Si creamos una instancia de un `Point<T>` que tenga valores de diferentes
tipos, como en el Listado 10-7, nuestro código no se compilará.
tipos, como en el listado 10-7, nuestro código no se compilará.

<span class="filename">Filename: src/main.rs</span>

Expand All @@ -139,7 +139,7 @@ este:

Para definir un struct `Point` donde `x` e `y` son ambos genéricos pero podrían
tener diferentes tipos, podemos usar múltiples parámetros de tipo genérico. Por
ejemplo, en el Listado 10-8, cambiamos la definición de `Point` para que sea
ejemplo, en el listado 10-8, cambiamos la definición de `Point` para que sea
generic sobre los tipos `T` y `U` donde `x` es de tipo `T` y `y` es de tipo
`U`.

Expand All @@ -149,11 +149,8 @@ generic sobre los tipos `T` y `U` donde `x` es de tipo `T` y `y` es de tipo
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-08/src/main.rs}}
```

<span class="caption">Listing 10-8: A `Point<T, U>` genérico over two types so
that `x` and `y` can be values of different types</span>

<span class="caption">Listing 10-8: un genérico `Point<T, U>` sobre dos tipos
que `x` e `y` pueden ser valores de diferentes tipos</span>
<span class="caption">Listado 10-8: Un `Point<T, U>` genérico sobre dos tipos
para que `x` e `y` puedan ser valores de tipos diferentes</span>

¡Ahora todas las instancias de `Point` que se muestran se permiten! Puede usar
tantos parámetros de tipo genérico en una definición como desee, pero usar más
Expand Down Expand Up @@ -197,7 +194,7 @@ El enum `Result` es un genérico en dos tipos, `T` y `E`. Tiene dos variantes:
de estos dos tipos, `T` o `E`, será el tipo del valor que se devuelve cuando se
produce un error o cuando se tiene éxito (devolviendo un valor de tipo `T`) o
falla (devolviendo un valor de tipo `E`). De hecho, esta es la definición que
usamos para abrir un archivo en el Listado 9-3, donde `T` se llenó con el tipo
usamos para abrir un archivo en el listado 9-3, donde `T` se llenó con el tipo
`std::fs::File` cuando el archivo se abrió con éxito y `E` se llenó con el tipo
`std::io::Error` cuando hubo problemas para abrir el archivo.

Expand All @@ -208,16 +205,16 @@ evitar la duplicación usando tipos genérico en su lugar.
### Definiciones In Method

Podemos implementar métodos en structs y enums y usar tipos genérico en sus
definiciones también. El Listado 10-9 muestra el struct `Point<T>` que
definimos en el Listado 10-6 con un método llamado `x` implementado en él.
definiciones también. El listado 10-9 muestra el struct `Point<T>` que
definimos en el listado 10-6 con un método llamado `x` implementado en él.

<span class="filename">Filename: src/main.rs</span>

```rust
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-09/src/main.rs}}
```

<span class="caption">Listing 10-9: Implementando un método llamado `x` en el
<span class="caption">Listado 10-9: Implementando un método llamado `x` en el
struct `Point<T>` que devolverá una referencia al campo `x` de tipo `T`</span>

Aquí, hemos definido un método llamado `x` en `Point<T>` que devuelve una
Expand All @@ -237,7 +234,7 @@ sustituyendo al tipo genérico.
También podemos especificar restricciones en los tipos genérico al definir
métodos en el tipo. Por ejemplo, podríamos implementar métodos solo en
instancias de `Point<T>` con un tipo `f32` concreto en lugar de en instancias
de `Point<T>` con cualquier tipo genérico. En el Listado 10-10 usamos el tipo
de `Point<T>` con cualquier tipo genérico. En el listado 10-10 usamos el tipo
concreto `f32`, lo que significa que no declaramos ningún tipo después de
`impl`.

Expand All @@ -247,7 +244,7 @@ concreto `f32`, lo que significa que no declaramos ningún tipo después de
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-10/src/main.rs:here}}
```

<span class="caption">Listing 10-10: Un bloque `impl` que solo aplica a un
<span class="caption">Listado 10-10: Un bloque `impl` que solo aplica a un
struct con un tipo concreto particular para el parámetro del tipo genérico
`T`</span>

Expand All @@ -259,7 +256,7 @@ matemáticas que solo están disponibles para tipos de punto flotante.

Los parámetros de tipo genérico en una definición de struct no siempre son los
mismos que los que usas en las firmas de métodos de ese mismo struct. El
Listado 10-11 usa los tipos genérico `X1` e `Y1` para el struct `Point` y `X2`
listado 10-11 usa los tipos genérico `X1` e `Y1` para el struct `Point` y `X2`
`Y2` para la firma del método `mixup` para hacer el ejemplo más claro. El
método crea una nueva instancia de `Point` con el valor `x` del `self` `Point`
(de tipo `X1`) y el valor `y` del `Point` pasado (de tipo `Y2`).
Expand All @@ -270,7 +267,7 @@ método crea una nueva instancia de `Point` con el valor `x` del `self` `Point`
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-11/src/main.rs}}
```

<span class="caption">Listing 10-11: Un método que usa diferentes tipos genérico
<span class="caption">Listado 10-11: Un método que usa diferentes tipos genérico
de la definición de su struct</span>

En `main`, hemos definido un `Point` que tiene un `i32` para `x` (con valor `5`)
Expand Down Expand Up @@ -298,7 +295,7 @@ Rust logra esto realizando _monomorfización_ del código usando genéricos en
tiempo de compilación. _Monomorfización_ es el proceso de convertir código
genérico en código específico llenando los tipos concretos que se usan cuando
se compila. En este proceso, el compilador hace lo contrario de los pasos que
usamos para crear la función genérica en el Listado 10-5: el compilador mira
usamos para crear la función genérica en el listado 10-5: el compilador mira
todos los lugares donde se llama el código genérico y genera código para los
tipos concretos con los que se llama el código genérico.

Expand All @@ -310,7 +307,7 @@ let integer = Some(5);
let float = Some(5.0);
```

Cuando Rust compilas este código, realiza monomorfización. Durante ese
Cuando Rust compila este código, realiza monomorfización. Durante ese
proceso, el compilador lee los valores que se han usado en las instancias de
`Option<T>` e identifica dos tipos de `Option<T>`: uno es `i32` y el otro es
`f64`. Como tal, expande la definición genérica de `Option<T>` en dos
Expand Down
Loading

0 comments on commit c96cae8

Please sign in to comment.