¡Hola habitantes! La Guía oficial del lenguaje de programación de Rust lo ayudará a crear software más rápido y confiable. La ergonomía de alto nivel y los controles de bajo nivel a menudo entran en conflicto entre sí, pero Rust desafía este conflicto.
Los autores del libro forman parte del equipo de desarrollo del lenguaje, lo que significa que recibirás toda la información de primera mano, desde la instalación del lenguaje hasta la creación de programas fiables y escalables. Desde crear funciones, elegir tipos de datos y vincular variables, pasará a conceptos más complejos:
- Propiedad y endeudamiento, ciclo de vida y tipos.
- Seguridad de software garantizada.
- Pruebas, manejo de errores y refactorización efectiva.
- Genéricos, punteros inteligentes, subprocesos múltiples, objetos intercambiables y asignaciones.
- Trabaje con el administrador de paquetes integrado Cargo para crear, probar, documentar el código y administrar las dependencias.
- Herramientas avanzadas para trabajar con Unsafe Rust.
Encontrará muchos ejemplos de código, así como tres capítulos sobre la creación de proyectos completos para solidificar el conocimiento: juegos de adivinanzas, creación de una herramienta de línea de comandos y un servidor de subprocesos múltiples.
Para quién es este libro
Suponemos que ha escrito su código en un lenguaje de programación diferente, pero no hacemos suposiciones sobre cuál. Hemos tratado de hacer que este material sea accesible para aquellos con una amplia gama de habilidades de programación. No perderemos el tiempo hablando de qué es la programación. Si eres un principiante absoluto en programación, primero lee la introducción a la programación.
Como usar este libro
-, , , . , ; .
: . . , , . 2, 12 20 , — .
1 , Rust, «Hello, World!» Cargo. 2 Rust. , . , . 3, Rust, , 4 Rust. , , 2, 3, 2, . , .
5 , 6 , match if let. Rust .
7 (API). 8 , , , -. 9 .
10 , , , . 11 , Rust . 12 grep, . , .
13 — , . 14 Cargo . 15 , , , .
16 , Rust . 17 Rust - , , , .
18 , Rust. 19 , , Rust, , , .
20 , !
, . Rust, Rust, , , , Rust.
: -, ! - , , , . , , .
Rust — , : . , , . , , ! , , , , . , .
: . . , , . 2, 12 20 , — .
1 , Rust, «Hello, World!» Cargo. 2 Rust. , . , . 3, Rust, , 4 Rust. , , 2, 3, 2, . , .
5 , 6 , match if let. Rust .
7 (API). 8 , , , -. 9 .
10 , , , . 11 , Rust . 12 grep, . , .
13 — , . 14 Cargo . 15 , , , .
16 , Rust . 17 Rust - , , , .
18 , Rust. 19 , , Rust, , , .
20 , !
, . Rust, Rust, , , , Rust.
: -, ! - , , , . , , .
Rust — , : . , , . , , ! , , , , . , .
Donde se pueden usar patrones
En Rust, los patrones aparecen en muchos lugares, ¡y los has usado mucho sin siquiera darte cuenta! Esta sección analiza situaciones en las que los patrones son válidos.
Coincidir con ramas de expresión
Como se discutió en el Capítulo 6, usamos patrones en las ramas de las expresiones de coincidencia. Formalmente, las expresiones de concordancia se definen como la concordancia de palabras clave, luego el valor para concordar y una o más ramas de la concordancia que consisten en el patrón y la expresión que se ejecutará si el valor coincide con el patrón de esa rama, por ejemplo:
match {
=> ,
=> ,
=> ,
}
Uno de los requisitos para las expresiones de coincidencia es que deben ser exhaustivas en el sentido de que todos los valores posibles deben considerarse en coincidencia. Para que pueda considerar todas las opciones posibles, debe tener un patrón que lo abarque todo en la última rama: por ejemplo, un nombre de variable que coincida con cualquier valor siempre se activará y, por lo tanto, cubrirá todos los casos restantes.
El patrón especial _ coincidirá con cualquier cosa, pero no se vincula a una variable y, por lo tanto, se usa a menudo en la última manga de la coincidencia. El patrón _ es útil, por ejemplo, si desea ignorar cualquier valor no especificado. Consideraremos el patrón con más detalle en la sección "Ignorar valores en un patrón".
Si deja condicionales
En el Capítulo 6, discutimos las declaraciones if let principalmente como una forma más corta de escribir el equivalente de una expresión de coincidencia que solo coincide con un caso. Alternativamente, if let puede tener un else coincidente que contenga el código para ejecutar si el patrón en if let no coincide.
El listado 18.1 muestra que también es posible mezclar y unir declaraciones if let, else if y else if let. Esto nos da más flexibilidad que usar una expresión de coincidencia, que solo puede expresar un valor para compararlo con los patrones. Además, las condiciones en una serie de sentencias if let, else if y else if let no necesitan referirse entre sí.
El código del listado 18.1 muestra una serie de pruebas para varias condiciones que deciden cuál debe ser el color de fondo. En este ejemplo, creamos variables con valores codificados que un programa real podría recuperar de la entrada del usuario.
Listado 18.1. Mezclar if let, else if, else if let y declaraciones else
src/main.rs
fn main() {
let favorite_color: Option<&str> = None;
let is_tuesday = false;
let age: Result<u8, _> = "34".parse();
(1) if let Some(color) = favorite_color {
(2) println!(" , {}, ", color);
(3) } else if is_tuesday {
(4) println!(" - !");
(5) } else if let Ok(age) = age {
(6) if age > 30 {
(7) println!(" ");
} else {
(8) println!(" ");
}
(9) } else {
(10) println!(" ");
}
}
Si el usuario especifica un color favorito (1), este es el color de fondo (2). Si hoy es martes (3), entonces el color de fondo es verde (4). Si el usuario especifica su edad como una cadena y podemos analizarla correctamente como un número (5), entonces el color será morado (7) o naranja (8) según el valor del número (6). Si no se aplica ninguna de estas condiciones (9), entonces el color de fondo es azul (10).
Esta estructura condicional permite requisitos complejos. Con los valores codificados aquí, este ejemplo daría como resultado
.
Puede ver que una expresión if let también puede introducir variables sombreadas de la misma manera que las mangas de una expresión coincidente: la línea de código if let Ok (edad) = edad (5) introduce una nueva variable sombreada age que contiene el valor dentro de la variante Ok. Esto significa que debemos poner la condición si edad> 30 en este bloque (6): no podemos combinar estas dos condiciones en la declaración si deja Ok (edad) = edad && edad> 30. La variable sombreada edad que queremos comparar con 30 , no será válido hasta que el nuevo alcance comience con una llave.
La desventaja de utilizar sentencias if let es que el compilador no comprueba la exhaustividad, mientras que sí lo hace para las sentencias de coincidencia. Si nos hubiéramos saltado el último bloque else (9) y, por tanto, el manejo de algunos casos, el compilador no nos habría advertido sobre un posible error lógico.
Mientras deja bucles condicionales
Similar en diseño a la instrucción if let, el ciclo condicional while let permite que el ciclo while se ejecute mientras el patrón coincida. El ejemplo del Listado 18.2 muestra un bucle while let que usa un vector como pila y genera los valores en el vector en el orden inverso al orden en que fueron agregados.
Listado 18.2. El uso de un bucle while let para imprimir valores mientras stack.pop () devuelve Some
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(top) = stack.pop() {
println!("{}", top);
}
Este ejemplo imprime 3, 2 y luego 1. El método pop toma el último elemento del Vector y devuelve Some (value). Si el vector está vacío, entonces pop devuelve Ninguno. El ciclo while continúa ejecutando el código en su bloque hasta que pop devuelve Some. Cuando pop devuelve None, el bucle se detiene. Podemos usar un bucle condicional while let para eliminar cada elemento de la pila.
Para bucles
En el Capítulo 3 mencionamos que el bucle for es la construcción de bucle más común en el código de Rust, pero aún no hemos discutido el patrón que toma. En un bucle for, el patrón es el valor que sigue inmediatamente a la palabra clave for, por lo que en for x en y, el patrón es x.
El listado 18.3 muestra el uso de un patrón en un bucle for para desestructurar o descomponer una tupla dentro de un for.
Listado 18.3. Usar un patrón en un bucle for para desestructurar una tupla
let v = vec!['a', 'b', 'c'];
for (index, value) in v.iter().enumerate() {
println!("{} {}", value, index);
}
El código del Listado 18.3 muestra lo siguiente:
0
b 1
2
Usamos el método enumerate para reescribir el iterador para producir el valor y el índice de ese valor en el iterador, colocado en una tupla. La primera llamada al método enumerate produce una tupla (0, 'a'). Cuando este valor se combina con el patrón (índice, valor), entonces el índice es 0 y el valor es 'a', se genera la primera fila de datos.
Deja declaraciones
Antes de este capítulo, discutimos directamente el uso de patrones solo con declaraciones match y if let, pero de hecho hemos usado patrones en otros lugares, incluso en declaraciones let. Considere una forma simple de pasar el valor de una variable usando let:
let x = 5;
Hemos utilizado este tipo de declaraciones permitidas cientos de veces a lo largo de este libro, y aunque es posible que no se haya dado cuenta, ¡ha estado usando patrones! Más formalmente, una declaración let se ve así:
let = ;
En declaraciones como let x = 5; con un nombre de variable en la ranura PATTERN, el nombre de la variable es solo una forma simple del patrón. Rust compara la expresión con el patrón y asigna los nombres que encuentra. Por lo tanto, en el ejemplo, sea x = 5; el patrón es x, que significa "asociar lo que coincide aquí con la variable x". Dado que el nombre x representa el patrón completo, este patrón significa efectivamente "vincular todo a la variable x, sea cual sea el valor".
Para ver el mapeo al patrón de instrucción let más claramente, considere el Listado 18.4, que usa el patrón let para desestructurar una tupla.
Listado 18.4. Usar un patrón para desestructurar una tupla y crear tres variables a la vez
let (x, y, z) = (1, 2, 3);
Aquí estamos mapeando una tupla a un patrón. Rust compara (1, 2, 3) con (x, y, z) y ve que este valor coincide con el patrón, por lo que Rust asocia 1 con x, 2 con y y 3 con z. Puede pensar en este patrón de tupla como si anidara tres patrones variables separados en él.
Si el número de elementos en el patrón no coincide con el número de elementos en la tupla, entonces el tipo de agregado no coincidirá y obtendremos un error del compilador. Por ejemplo, el listado 18.5 muestra un intento de desestructurar una tupla de tres en dos variables, lo que no funcionará.
Listado 18.5. Construcción incorrecta del patrón, cuyas variables no coinciden con el número de elementos en la tupla
let (x, y) = (1, 2, 3);
Intentar compilar este código da como resultado un error como:
error[E0308]: mismatched types
--> src/main.rs:2:9
|
2 | let (x, y) = (1, 2, 3);
| ^^^^^^ expected a tuple with 3 elements, found one with 2 elements
|
= note: expected type `({integer}, {integer}, {integer})`
found type `(_, _)`
Si quisiéramos ignorar uno o más valores en una tupla, podríamos usar _ o .., como verá en la sección “Ignorar valores en un patrón”. Si el problema es que hay demasiadas variables en el patrón, entonces necesita hacer coincidir los tipos eliminando las variables para que el número de variables sea igual al número de elementos en la tupla.
Parámetros de función
Los parámetros de función también pueden ser patrones. El código del Listado 18.6 que declara una función foo que toma un parámetro x de tipo i32 ahora le resulta familiar.
Listado 18.6. La firma de la función usa patrones en los parámetros
fn foo(x: i32) {
//
}
¡La parte x es un patrón! Al igual que con let, podemos hacer coincidir la tupla en los argumentos de la función con el patrón. El listado 18.7 divide los valores de la tupla en el momento en que la pasamos dentro de la función.
Listado 18.7. Función con parámetros que desestructuran la tupla
src/main.rs
fn print_coordinates(&(x, y): &(i32, i32)) {
println!(" : ({}, {})", x, y);
}
fn main() {
let point = (3, 5);
print_coordinates(&point);
}
Este código genera
: (3, 5)
Los valores & (3, 5) coinciden con el patrón & (x, y), por lo que x es 3 e y es 5.
Además, podemos usar patrones en listas de parámetros de cierre de la misma manera que en listas de parámetros de funciones, ya que Los cierres son similares a las funciones, como se describe en el Capítulo 13.
Ya ha visto varias formas de usar patrones, pero no funcionan igual en todos los lugares donde los usa. En algunas situaciones, estos patrones deben ser irrefutables; en otras, pueden ser refutables. Discutiremos estos dos conceptos a continuación.
Refutabilidad: posibilidad de discrepancia de patrones
Los patrones vienen en dos sabores: refutable e irrefutable. Los patrones que coincidirán con cualquier valor posible pasado son irrefutables. Un ejemplo es la x en la declaración let x = 5; porque x coincide absolutamente con todo y por lo tanto no puede sino coincidir. Los patrones que no coinciden con algunos de los posibles significados son refutables. Un ejemplo es Some (x) en la instrucción if let Some (x) = a_value, porque si el valor en a_value es None y no Some, entonces Some (x) no coincidirá.
Los parámetros de función, las sentencias let y los bucles for solo pueden aceptar patrones irrefutables porque el programa no puede hacer nada significativo cuando los valores no coinciden. Las expresiones if let y while let solo aceptan patrones refutables, porque por definición están diseñadas para manejar un posible error: la funcionalidad de una expresión condicional es su capacidad para realizar diferentes acciones dependiendo del éxito o fracaso.
En general, no debería preocuparse por la distinción entre patrones refutables e irrefutables. Sin embargo, debe conocer el concepto de refutación para poder reaccionar cuando lo vea en un mensaje de error. En estos casos, deberá cambiar el patrón o la construcción con la que está utilizando el patrón, según el comportamiento previsto del código.
Echemos un vistazo a lo que sucede cuando intentamos usar un patrón irrefutable en un lugar donde Rust requiere un patrón irrefutable, y viceversa. El listado 18.8 muestra una declaración let, pero para el patrón especificamos Some (x), un patrón refutable. Como era de esperar, este código no se compila.
Listado 18.8. Tratando de usar un patrón refutable con let
let Some(x) = some_option_value;
Si some_option_value fuera igual a None, entonces no coincidiría con el patrón Some (x), es decir, el patrón es refutable. Sin embargo, una declaración let solo puede aceptar un patrón irrefutable, ya que el código no puede hacer nada válido con el valor None. En el momento de la compilación, Rust se quejará de que intentamos usar un patrón refutable donde se requiere un patrón irrefutable:
error[E0005]: refutable pattern in local binding: `None` not covered
-->
|
3 | let Some(x) = some_option_value;
| ^^^^^^^ pattern `None` not covered
Como no hemos cubierto (y no podríamos haberlo hecho) todos los valores válidos con el patrón Some (x), Rust está lanzando un error de compilador con razón.
Para solucionar el problema cuando tenemos un patrón refutable en lugar de uno irrefutable, podemos cambiar el código que usa el patrón: en lugar de dejar, podemos usar if let. Luego, si el patrón no coincide, se omitirá el código entre llaves y el trabajo continuará correctamente. El listado 18.9 muestra cómo arreglar el código del listado 18.8.
Listado 18.9. Usar una declaración if let y un bloque de patrón refutable en lugar de let
if let Some(x) = some_option_value {
println!("{}", x);
}
¡El código está listo! Este es un código absolutamente correcto, aunque significa que no podemos usar un patrón irrefutable sin error. Si le damos a la expresión if let un patrón que siempre coincide, como x, como se muestra en el Listado 18-10, entonces no se compilará.
Listado 18.10. Tratando de usar un patrón irrefutable con una declaración if let
if let x = 5 {
println!("{}", x);
};
El compilador se queja de que no tiene sentido usar una expresión if let con un patrón irrefutable:
error[E0162]: irrefutable if-let pattern
--> <anon>:2:8
|
2 | if let x = 5 {
| ^ irrefutable pattern
Por esta razón, las mangas de la expresión de coincidencia deben utilizar patrones refutables, con la excepción de la última manga, que debe hacer coincidir los valores restantes con el patrón irrefutable. Rust permite que el patrón irrefutable se use en una expresión de coincidencia con solo una funda, pero esta sintaxis no es particularmente útil y se puede reemplazar con una declaración let más simple.
Ahora que sabe dónde se usan los patrones y en qué se diferencian los patrones refutables e irrefutables, conozcamos la sintaxis que podemos usar para crear patrones.
Sobre los autores
Steve Klabnik lidera el equipo de documentación de Rust y es uno de los desarrolladores clave del lenguaje. Es un conferenciante frecuente y escribe mucho código fuente abierto. Anteriormente trabajó en proyectos como Ruby y Ruby on Rails.
Carol Nichols es miembro del equipo de desarrollo de Rust Core y cofundadora de Integer 32, LLC, la primera empresa de consultoría de desarrollo de software centrada en Rust del mundo. Nichols es el organizador de la conferencia Rust Belt sobre el lenguaje Rust.
»Más detalles sobre el libro se pueden encontrar en el sitio web de la editorial
» Tabla de contenido
» Extracto
Para Habitans un 25% de descuento en cupón - Rust
Al pagar la versión impresa del libro, se envía un libro electrónico al correo electrónico.