From 6c33cfb63488f5a692cfb30404c4263a2201287f Mon Sep 17 00:00:00 2001 From: Ivan Lynch Date: Sat, 16 Mar 2024 15:43:51 -0300 Subject: [PATCH] feat: improve chapter 10 readability and added missing code example --- src/ch10-00-generics.md | 30 ++++----- src/ch10-01-syntax.md | 43 ++++++------- src/ch10-02-traits.md | 30 +++++---- src/ch10-03-lifetime-syntax.md | 114 ++++++++++++++++----------------- 4 files changed, 108 insertions(+), 109 deletions(-) diff --git a/src/ch10-00-generics.md b/src/ch10-00-generics.md index 391e1dc6d..d0279cdd7 100644 --- a/src/ch10-00-generics.md +++ b/src/ch10-00-generics.md @@ -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. @@ -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 @@ -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. Filename: src/main.rs @@ -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}} ``` -Listing 10-1: Encontrando el mayor número en una lista de +Listado 10-1: Encontrando el mayor número en una lista de números Almacenamos una lista de enteros en la variable `number_list` y colocamos una @@ -66,9 +66,9 @@ 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. Filename: src/main.rs @@ -76,8 +76,8 @@ Listado 10-2. {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-02/src/main.rs}} ``` -Listing 10-2: Código para encontrar el mayor número en -*dos* listas de números +Listado 10-2: Código para encontrar el mayor número en +_dos_ listas de números 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 @@ -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. Filename: src/main.rs @@ -99,7 +99,7 @@ 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}} ``` -Listing 10-3: Código abstracto para encontrar el número +Listado 10-3: Código abstracto para encontrar el número mayor en dos listas La función `largest` tiene un parámetro llamado `list`, que representa cualquier @@ -107,8 +107,8 @@ slice de valores `ì32` que podríamos pasar a la función. Como resultado, cuan 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 @@ -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! diff --git a/src/ch10-01-syntax.md b/src/ch10-01-syntax.md index 42d843cc4..3bad3b7a0 100644 --- a/src/ch10-01-syntax.md +++ b/src/ch10-01-syntax.md @@ -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. Filename: src/main.rs @@ -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}} ``` -Listing 10-4: Dos funciones que difieren solo en sus +Listado 10-4: Dos funciones que difieren solo en sus nombres y los tipos en sus firmas -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 @@ -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 @@ -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` para +usando la sintaxis `<>`. El listado 10-6 define un struct `Point` para contener valores `x` e `y` de cualquier tipo `T`. Filename: src/main.rs @@ -116,7 +116,7 @@ Ten en cuenta que porque hemos usado un solo tipo genérico para definir `Point`, esta definición dice que el struct `Point` 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` 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á. Filename: src/main.rs @@ -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`. @@ -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}} ``` -Listing 10-8: A `Point` genérico over two types so -that `x` and `y` can be values of different types - -Listing 10-8: un genérico `Point` sobre dos tipos -que `x` e `y` pueden ser valores de diferentes tipos +Listado 10-8: Un `Point` genérico sobre dos tipos +para que `x` e `y` puedan ser valores de tipos diferentes ¡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 @@ -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. @@ -208,8 +205,8 @@ 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` 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` que +definimos en el listado 10-6 con un método llamado `x` implementado en él. Filename: src/main.rs @@ -217,7 +214,7 @@ definimos en el Listado 10-6 con un método llamado `x` implementado en él. {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-09/src/main.rs}} ``` -Listing 10-9: Implementando un método llamado `x` en el +Listado 10-9: Implementando un método llamado `x` en el struct `Point` que devolverá una referencia al campo `x` de tipo `T` Aquí, hemos definido un método llamado `x` en `Point` que devuelve una @@ -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` con un tipo `f32` concreto en lugar de en instancias -de `Point` con cualquier tipo genérico. En el Listado 10-10 usamos el tipo +de `Point` 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`. @@ -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}} ``` -Listing 10-10: Un bloque `impl` que solo aplica a un +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` @@ -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`). @@ -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}} ``` -Listing 10-11: Un método que usa diferentes tipos genérico +Listado 10-11: Un método que usa diferentes tipos genérico de la definición de su struct En `main`, hemos definido un `Point` que tiene un `i32` para `x` (con valor `5`) @@ -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. @@ -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` e identifica dos tipos de `Option`: uno es `i32` y el otro es `f64`. Como tal, expande la definición genérica de `Option` en dos diff --git a/src/ch10-02-traits.md b/src/ch10-02-traits.md index 25b331975..7009cf937 100644 --- a/src/ch10-02-traits.md +++ b/src/ch10-02-traits.md @@ -30,7 +30,7 @@ Queremos hacer una biblioteca de agregación de medios llamada `aggregator` que puede mostrar resúmenes de datos que podrían estar almacenados en una instancia de `NewsArticle` o `Tweet`. Para hacer esto, necesitamos un resumen de cada tipo, y solicitaremos ese resumen llamando un método `summarize` en -una instancia. El Listado 10-12 muestra la definición de un trait `Summary` +una instancia. El listado 10-12 muestra la definición de un trait `Summary` público que expresa este comportamiento. Filename: src/lib.rs @@ -39,7 +39,7 @@ público que expresa este comportamiento. {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-12/src/lib.rs}} ``` -Listing 10-12: Un trait `Summary` que consiste en el +Listado 10-12: Un trait `Summary` que consiste en el comportamiento proporcionado por un método `summarize` Aquí, declaramos un trait usando la palabra clave `trait` y luego el nombre @@ -62,7 +62,7 @@ se enumeran una por línea y cada línea termina en un punto y coma. ### Implementando un Trait en un Tipo Ahora que hemos definido el trait `Summary`, podemos implementarlo en los -tipos en nuestro agregador de medios. El Listado 10-13 muestra una +tipos en nuestro agregador de medios. El listado 10-13 muestra una implementación del trait `Summary` en el struct `NewsArticle` que usa el encabezado, el autor y la ubicación para crear el valor de retorno de `summarize`. Para el struct `Tweet`, definimos `summarize` como el nombre de @@ -75,7 +75,7 @@ tweet ya está limitado a 280 caracteres. {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-13/src/lib.rs:here}} ``` -Listing 10-13: Implementación del trait `Summary` en los +Listado 10-13: Implementación del trait `Summary` en los tipos `NewsArticle` y `Tweet` Implementar un trait en un tipo es similar a implementar métodos regulares. @@ -91,7 +91,7 @@ Ahora que la biblioteca ha implementado el trait `Summary` en `NewsArticle` y `Tweet`, los usuarios de la biblioteca pueden llamar a los métodos de trait en las instancias de `NewsArticle` y `Tweet` en la misma forma en que llamamos a los métodos regulares. La única diferencia es que el usuario debe traer el -trait al scope, así como los tipos. Aquí hay un ejemplo de cómo un crate +trait al ámbito, así como los tipos. Aquí hay un ejemplo de cómo un crate binario podría usar nuestra biblioteca de `aggregator`: ```rust,ignore @@ -102,7 +102,7 @@ Este código imprime `New article available! horse_ebooks: of course, as you probably already know, people`. Otros crates que dependen de nuestro crate `aggregator` pueden usar el trait -`Summary` en el scope para implementar `Summary` en sus propios tipos. Una +`Summary` en el ámbito para implementar `Summary` en sus propios tipos. Una restricción a tener en cuenta es que podemos implementar un trait en un tipo solo si al menos uno de los trait o el tipo es local a nuestro crate. Por ejemplo, podemos implementar traits de la biblioteca estándar como `Display` @@ -132,9 +132,9 @@ métodos en cada tipo. Luego, a medida que implementamos el trait en un tipo particular, podemos mantener o anular el comportamiento predeterminado para cada método. -En el Listado 10-14, especificamos un string predeterminado para el método +En el listado 10-14, especificamos un string predeterminado para el método `summarize` del trait `Summary` en lugar de solo definir la firma del método, -como hicimos en el Listado 10-12. +como hicimos en el listado 10-12. Filename: src/lib.rs @@ -142,7 +142,7 @@ como hicimos en el Listado 10-12. {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-14/src/lib.rs:here}} ``` -Listing 10-14: Definición de un trait `Summary` con un +Listado 10-14: Definición de un trait `Summary` con un valor predeterminado implementado del método `summarize` Para usar una implementación predeterminada para resumir instancias de @@ -162,7 +162,7 @@ como esto: Este código imprime `New article available! (Read more...)`. Crear una implementación predeterminada no requiere que cambiemos nada sobre -la implementación de `Summary` en `Tweet` en el Listado 10-13. La razón es que +la implementación de `Summary` en `Tweet` en el listado 10-13. La razón es que la sintaxis para anular una implementación predeterminada es la misma que la sintaxis para implementar un método de trait que no tiene una implementación predeterminada. @@ -208,12 +208,14 @@ una implementación primordial de ese mismo método. Ahora que sabes cómo definir y implementar traits, podemos explorar cómo usar traits para definir funciones que aceptan muchos tipos diferentes. Usaremos el trait `Summary` que implementamos en los tipos `NewsArticle` y `Tweet` en el -Listado 10-13 para definir una función `notify` que llama al método `summarize` +listado 10-13 para definir una función `notify` que llama al método `summarize` en su parámetro `item`, que es de algún tipo que implementa el trait `Summary`. Para hacer esto, usamos la sintaxis `impl Trait`, como esto: ```rust,ignore - +pub fn notify(item: &impl Summary) { + println!("Breaking news! {}", item.summarize()); +} ``` En lugar de un tipo concreto para el parámetro `item`, especificamos el @@ -352,7 +354,7 @@ ignore --> del Capítulo 17. Al usar un trait bound con un bloque `impl` que usa parámetros de tipo generic, podemos implementar métodos condicionalmente para tipos que implementan los -traits especificados. Por ejemplo, el tipo `Pair` en el Listado 10-15 siempre +traits especificados. Por ejemplo, el tipo `Pair` en el listado 10-15 siempre implementa la función `new` para devolver una nueva instancia de `Pair` (recuerda de la sección [“Definiendo métodos”][methods] del Capítulo 5 que `Self` es un alias de tipo para el tipo del bloque `impl`, que en @@ -367,7 +369,7 @@ impresión. {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-15/src/lib.rs}} ``` -Listing 10-15: Implementación condicional de métodos en un +Listado 10-15: Implementación condicional de métodos en un tipo generic dependiendo de los trait bounds También podemos implementar condicionalmente un trait para cualquier tipo que diff --git a/src/ch10-03-lifetime-syntax.md b/src/ch10-03-lifetime-syntax.md index a6b73681f..5ba970535 100644 --- a/src/ch10-03-lifetime-syntax.md +++ b/src/ch10-03-lifetime-syntax.md @@ -27,54 +27,54 @@ lifetimes para que pueda familiarizarse con el concepto. El objetivo principal de los lifetimes es prevenir _referencias colgantes_, que hacen que un programa haga referencia a datos que no son los que se -pretende referenciar. Considere el programa en el Listado 10-16, que tiene un -scope externo y un scope interno. +pretende referenciar. Considere el programa en el listado 10-16, que tiene un +ámbito externo y un ámbito interno. ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-16/src/main.rs}} ``` -Listing 10-16: Un intento de usar una referencia cuyo -valor ha quedado fuera del scope +Listado 10-16: Un intento de usar una referencia cuyo +valor ha quedado fuera del ámbito > Nota: Los ejemplos en los Listados 10-16, 10-17 y 10-23 declaran variables > sin darles un valor inicial, por lo que el nombre de la variable existe en el -> scope externo. A primera vista, esto podría parecer estar en conflicto con el +> ámbito externo. A primera vista, esto podría parecer estar en conflicto con el > hecho de que Rust no tiene valores nulos. Sin embargo, si intentamos usar una > variable antes de darle un valor, obtendremos un error en tiempo de > compilación, lo que muestra que Rust de hecho no permite valores nulos. -El scope externo declara una variable llamada `r` sin valor inicial, y el scope +El ámbito externo declara una variable llamada `r` sin valor inicial, y el ámbito interno declara una variable llamada `x` con el valor inicial de 5. Dentro del -scope interno, intentamos establecer el valor de `r` como una referencia a `x`. -Luego, el scope interno termina, e intentamos imprimir el valor en `r`. Este +ámbito interno, intentamos establecer el valor de `r` como una referencia a `x`. +Luego, el ámbito interno termina, e intentamos imprimir el valor en `r`. Este código no se compilará porque el valor al que se refiere `r` ha quedado fuera -del scope antes de que intentemos usarlo. Aquí está el mensaje de error: +del ámbito antes de que intentemos usarlo. Aquí está el mensaje de error: ```console {{#include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-16/output.txt}} ``` La variable `x` no “vive lo suficiente”. La razón es que `x` estará fuera del -scope cuando el scope interno termine en la línea 7. Pero `r` todavía es -válido para el scope externo; porque su scope es más grande, decimos que +ámbito cuando el ámbito interno termine en la línea 7. Pero `r` todavía es +válido para el ámbito externo; porque su ámbito es más grande, decimos que “vive más tiempo”. Si Rust permitiera que este código funcionara, `r` estaría -referenciando memoria que se desasignó cuando `x` quedó fuera del scope, y +referenciando memoria que se desasignó cuando `x` quedó fuera del ámbito, y cualquier cosa que intentemos hacer con `r` no funcionaría correctamente. ¿Cómo determina Rust que este código es inválido? Utiliza el _borrow checker_. ### El Borrow Checker El compilador de Rust tiene un _borrow checker_ que compara scopes para -determinar si todos los _borrows_ son válidos. El Listado 10-17 muestra el -mismo código que el Listado 10-16, pero con anotaciones que muestran los +determinar si todos los _borrows_ son válidos. El listado 10-17 muestra el +mismo código que el listado 10-16, pero con anotaciones que muestran los lifetimes de las variables. ```rust,ignore,does_not_compile {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-17/src/main.rs}} ``` -Listing 10-17: Anotaciones de los lifetimes de `r` y +Listado 10-17: Anotaciones de los lifetimes de `r` y `x`, denominados `'a` y `'b`, respectivamente Aquí, hemos anotado el lifetime de `r` con `'a` y el lifetime de `x` con `'b`. @@ -84,14 +84,14 @@ lifetimes y ve que `r` tiene un lifetime de `'a` pero que se refiere a la memoria con un lifetime de `'b`. El programa es rechazado porque `'b` es más corto que `'a`: el sujeto de la referencia no vive tanto como la referencia. -El Listado 10-18 corrige el código para que no tenga una referencia pendiente y +El listado 10-18 corrige el código para que no tenga una referencia pendiente y se compile sin errores. ```rust {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-18/src/main.rs}} ``` -Listing 10-18: Una referencia válida porque los datos +Listado 10-18: Una referencia válida porque los datos tienen un lifetime más largo que la referencia Aquí, `x` tiene el lifetime `'b` que en este caso es más grande que `'a`. Esto @@ -107,7 +107,7 @@ el contexto de las funciones. Escribiremos una función que devuelva el más largo de dos _string slices_. Esta función tomará dos _string slices_ y devolverá un solo _string slice_. -Después de haber implementado la función `longest`, el código en el Listado +Después de haber implementado la función `longest`, el código en el listado 10-19 debería imprimir `The longest string is abcd`. Filename: src/main.rs @@ -116,17 +116,17 @@ Después de haber implementado la función `longest`, el código en el Listado {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-19/src/main.rs}} ``` -Listing 10-19: Una función `main` que llama a la +Listado 10-19: Una función `main` que llama a la función `longest` para encontrar el más largo de dos string slices Ten en cuenta que queremos que la función tome _string slices_, que son referencias, en lugar de _strings_, porque no queremos que la función `longest` tome posesión de sus parámetros. Consulta la sección [“String Slices as Parameters”][string-slices-as-parameters] en el Capítulo 4 para -obtener más información sobre por qué los parámetros que usamos en el Listado +obtener más información sobre por qué los parámetros que usamos en el listado 10-19 son los que queremos. -Si intentamos implementar la función `longest` como se muestra en el Listado +Si intentamos implementar la función `longest` como se muestra en el listado 10-20, no se compilará. Filename: src/main.rs @@ -135,7 +135,7 @@ Si intentamos implementar la función `longest` como se muestra en el Listado {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-20/src/main.rs:here}} ``` -Listing 10-20: Una implementación de la función `longest` +Listado 10-20: Una implementación de la función `longest` que devuelve el más largo de dos string slices pero aún no compila En su lugar, obtenemos el siguiente error que habla sobre lifetimes: @@ -205,7 +205,7 @@ Queremos que la firma exprese la siguiente restricción: la referencia devuelta será válida siempre que ambos parámetros sean válidos. Esta es la relación entre los lifetimes de los parámetros y el valor de retorno. Nombraremos al lifetime `'a` y luego lo agregaremos a cada referencia, como se muestra en el -Listado 10-21. +listado 10-21. Filename: src/main.rs @@ -213,12 +213,12 @@ Listado 10-21. {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-21/src/main.rs:here}} ``` -Listing 10-21: La definición de la función `longest` +Listado 10-21: La definición de la función `longest` que especifica que todas las referencias en la firma deben tener el mismo lifetime `'a` Este código debe compilar y producir el resultado que queremos cuando lo -usamos con la función `main` en el Listado 10-19. +usamos con la función `main` en el listado 10-19. La firma de la función ahora le dice a Rust que durante el lifetime `'a`, la función toma dos parámetros, ambos los cuales son string slices que viven al @@ -234,7 +234,7 @@ esta función, no estamos cambiando los lifetimes de ninguna de las referencias que se pasan en o se devuelven. En cambio, estamos especificando que el _borrow checker_ debería rechazar cualquier valor que no cumpla con estas restricciones. Ten en cuenta que la función `longest` no necesita saber -exactamente cuánto tiempo vivirán `x` e `y`, solo que algún scope puede +exactamente cuánto tiempo vivirán `x` e `y`, solo que algún ámbito puede sustituirse por `'a` que satisfará esta firma. Cuando anotamos lifetimes en funciones, las anotaciones van en la firma de la @@ -250,15 +250,15 @@ fueran las relaciones de los lifetimes, el compilador solo podría señalar el uso de nuestro código muchas etapas después de la causa del problema. Cuando pasamos referencias concretas a `longest`, se sustituye un lifetime -concreto por `'a`. Este lifetime concreto corresponde a la parte del scope de `x` -que se superpone con el scope de y. En otras palabras, el lifetime +concreto por `'a`. Este lifetime concreto corresponde a la parte del ámbito de `x` +que se superpone con el ámbito de y. En otras palabras, el lifetime genérico `'a` adquirirá el lifetime concreto que sea menor entre los lifetimes de `x` e `y`. Debido a que hemos anotado la referencia devuelta con el mismo parámetro de lifetime `'a`, la referencia devuelta también será válida por la duración del lifetime más corta entre `x` e `y`. Veamos cómo las anotaciones de los lifetimes restringen la función `longest` -pasando referencias que tienen diferentes lifetimes concretos. El Listado +pasando referencias que tienen diferentes lifetimes concretos. El listado 10-22 es un ejemplo sencillo. Filename: src/main.rs @@ -267,21 +267,21 @@ pasando referencias que tienen diferentes lifetimes concretos. El Listado {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-22/src/main.rs:here}} ``` -Listing 10-22: Usando la función `longest` con referencias +Listado 10-22: Usando la función `longest` con referencias a valores `String` que tienen diferentes lifetimes concretos -En este ejemplo, `string1` es válida hasta el final del scope externo, `string2` -es válida hasta el final del scope interno, y `result` referencia algo que es -válido hasta el final del scope interno. Ejecuta este código, y verás que el +En este ejemplo, `string1` es válida hasta el final del ámbito externo, `string2` +es válida hasta el final del ámbito interno, y `result` referencia algo que es +válido hasta el final del ámbito interno. Ejecuta este código, y verás que el _borrow checker_ lo aprueba; se compilará e imprimirá `The longest string is long string is long`. A continuación, intentemos un ejemplo que muestre que el lifetime de la referencia en `result` debe ser el más pequeño de los dos argumentos. -Moveremos la declaración de la variable `result` fuera del scope interno, pero -dejaremos la asignación del valor a `result` dentro del scope interno. Luego -moveremos la llamada a `println!` que usa `result` fuera del scope interno, -después de que el scope interno haya terminado. El código del Listado 10-23 no +Moveremos la declaración de la variable `result` fuera del ámbito interno, pero +dejaremos la asignación del valor a `result` dentro del ámbito interno. Luego +moveremos la llamada a `println!` que usa `result` fuera del ámbito interno, +después de que el ámbito interno haya terminado. El código del listado 10-23 no compilará. Filename: src/main.rs @@ -290,8 +290,8 @@ compilará. {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-23/src/main.rs:here}} ``` -Listing 10-23: Intentando utilizar `result` después de que -`string2` haya quedado fuera del scope +Listado 10-23: Intentando utilizar `result` después de que +`string2` haya quedado fuera del ámbito Cuando intentamos compilar este código, obtenemos este error: @@ -300,18 +300,18 @@ Cuando intentamos compilar este código, obtenemos este error: ``` El error muestra que para que `result` sea válido para la instrucción -`println!`, `string2` tendría que ser válido hasta el final del scope externo. +`println!`, `string2` tendría que ser válido hasta el final del ámbito externo. Rust sabe esto porque anotamos los lifetimes de los parámetros de la función y los valores de retorno usando el mismo parámetro de lifetime `'a`. Como humanos, podemos mirar este código y ver que `string1` es más larga que `string2` y por lo tanto `result` contendrá una referencia a `string1`. Debido a -que `string1` aún no ha quedado fuera del scope, una referencia a `string1` +que `string1` aún no ha quedado fuera del ámbito, una referencia a `string1` todavía será válida para la instrucción `println!`. Sin embargo, el compilador no puede ver que la referencia sea válida en este caso. Le hemos dicho a Rust que el lifetime de la referencia devuelta por la función `longest` es el mismo que el más pequeño de los lifetimes de las referencias pasadas. Por lo tanto, -el _borrow checker_ rechaza el código del Listado 10-23 como posiblemente +el _borrow checker_ rechaza el código del listado 10-23 como posiblemente conteniendo una referencia no válida. Intenta diseñar más experimentos que varíen los valores y los lifetimes de las @@ -341,7 +341,7 @@ Cuando se devuelve una referencia desde una función, el parámetro del lifetime para el tipo de retorno debe coincidir con el parámetro del lifetime de uno de los parámetros. Si la referencia devuelta no se refiere a uno de los parámetros, debe referirse a un valor creado dentro de esa función. Sin embargo, esto sería -una referencia colgante porque el valor quedará fuera del scope al final de la +una referencia colgante porque el valor quedará fuera del ámbito al final de la función. Considera esta implementación intentada de la función `longest` que no se compilará: @@ -360,7 +360,7 @@ Este es el mensaje de error que obtenemos: {{#include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-09-unrelated-lifetime/output.txt}} ``` -El problema es que `result` sale del scope y se limpia al final de la función +El problema es que `result` sale del ámbito y se limpia al final de la función `longest`. También estamos tratando de devolver una referencia a `result` desde la función. No hay forma de especificar parámetros de lifetime que cambien la referencia colgante, y Rust no nos permitirá crear una referencia colgante. En @@ -379,7 +379,7 @@ modo violen la seguridad de la memoria. Hasta ahora, los structs que hemos definido contienen tipos de ownership. Podemos definir structs que contengan referencias, pero en ese caso necesitamos agregar una anotación de lifetime en cada referencia en la definición del -struct. El Listado 10-24 tiene un struct llamado `ImportantExcerpt` que contiene +struct. El listado 10-24 tiene un struct llamado `ImportantExcerpt` que contiene una string slice. Filename: src/main.rs @@ -388,7 +388,7 @@ una string slice. {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-24/src/main.rs}} ``` -Listing 10-24: Un struct que contiene una referencia, +Listado 10-24: Un struct que contiene una referencia, lo que requiere una annotation de lifetime Este struct tiene el campo `part` que contiene un string slice, que es una @@ -402,16 +402,16 @@ en su campo `part`. La función `main` aquí crea una instancia del struct `ImportantExcerpt` que contiene una referencia a la primera oración de la variable `novel`. La data en `novel` existe antes de que se cree la instancia de `ImportantExcerpt`. Además, -`novel` no sale del scope hasta después de que la instancia de `ImportantExcerpt` -sale del scope, por lo que la referencia en la instancia de `ImportantExcerpt` +`novel` no sale del ámbito hasta después de que la instancia de `ImportantExcerpt` +sale del ámbito, por lo que la referencia en la instancia de `ImportantExcerpt` es válida. ### Omisión de lifetime Has aprendido que cada referencia tiene un lifetime y que debes especificar parámetros de lifetime para las funciones o structs que usan referencias. Sin -embargo, en el Capítulo 4, tuvimos una función en el Listado 4-9, que se muestra -nuevamente en el Listado 10-25, que se compiló sin anotaciones de lifetime. +embargo, en el Capítulo 4, tuvimos una función en el listado 4-9, que se muestra +nuevamente en el listado 10-25, que se compiló sin anotaciones de lifetime. Filename: src/lib.rs @@ -419,7 +419,7 @@ nuevamente en el Listado 10-25, que se compiló sin anotaciones de lifetime. {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-25/src/main.rs:here}} ``` -Listing 10-25: Una función que definimos en el Listado 4-9 +Listado 10-25: Una función que definimos en el listado 4-9 que compiló sin anotaciones de lifetime, a pesar de que el parámetro y el tipo de retorno son referencias @@ -483,7 +483,7 @@ símbolos. Imaginemos que somos el compilador. Aplicaremos estas reglas para descubrir los lifetime de las referencias en la firma de la función `first_word` en el -Listado 10-25. La firma comienza sin ningún lifetime asociado con las +listado 10-25. La firma comienza sin ningún lifetime asociado con las referencias: ```rust,ignore @@ -512,7 +512,7 @@ compilador puede continuar su análisis sin necesidad de que el programador anote los lifetime en esta firma de función. Veamos otro ejemplo, esta vez usando la función `longest` que no tenía -parámetros de lifetime cuando comenzamos a trabajar con ella en el Listado +parámetros de lifetime cuando comenzamos a trabajar con ella en el listado 10-20: ```rust,ignore @@ -531,7 +531,7 @@ lifetime. La tercera regla tampoco se aplica porque `longest` es una función en lugar de un método, por lo que no hay un parámetro de `self`. Después de trabajar a través de las tres reglas, todavía no hemos descubierto cuál es el lifetime de retorno. Es por eso que obtuvimos un error al intentar compilar el -código en el Listado 10-20: el compilador trabajó a través de las reglas de +código en el listado 10-20: el compilador trabajó a través de las reglas de omisión de lifetime, pero aún no pudo descubrir todos los lifetime de las referencias en la firma. @@ -543,7 +543,7 @@ métodos con mucha frecuencia. ### Anotaciones de lifetime en las definiciones de métodos Cuando implementamos métodos en un struct con lifetimes, usamos la misma -sintaxis que la de los parámetros de tipo generic que se muestra en el Listado +sintaxis que la de los parámetros de tipo generic que se muestra en el listado 10-11. Donde declaramos y usamos los parámetros de lifetime depende de si están relacionados con los campos del struct o con los parámetros y valores de retorno del método. @@ -557,7 +557,7 @@ estar vinculadas a los lifetime de los campos del struct, o pueden ser independientes. Además, las reglas de omisión de lifetime a menudo hacen que no sean necesarias las anotaciones de lifetime en las firmas de los métodos. Veamos algunos ejemplos usando el struct llamado `ImportantExcerpt` que definimos en el -Listado 10-24. +listado 10-24. En primer lugar, usaremos un método llamado `level` cuyo parámetro es una referencia a `self`, y cuyo valor de retorno es un `i32`, que no es una @@ -615,7 +615,7 @@ bounds y lifetimes todo en una función! {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/no-listing-11-generics-traits-and-lifetimes/src/main.rs:here}} ``` -Esta es la función `longest` del Listado 10-21 que devuelve el string más largo +Esta es la función `longest` del listado 10-21 que devuelve el string más largo de dos string slices. Pero ahora tiene un parámetro adicional llamado `ann` del tipo generic `T`, que puede llenarse con cualquier tipo que implemente el trait `Display` como se especifica en la cláusula `where`. Este parámetro adicional