Refutabilidad: Si un Pattern Puede Fallar al Hacer Match
Los patterns se dividen en dos formas: refutables e irrefutables. Los patterns
que coinciden con cualquier valor posible son irrefutables. Un ejemplo sería
x
en la declaración let x = 5;
porque x
coincide con cualquier cosa y,
por lo tanto, no puede fallar al hacer match. Los patterns que pueden fallar al
hacer match para algunos valores posibles son refutables. Un ejemplo sería
Some(x)
en la expresión if let Some(x) = a_value
porque si el valor en la
variable a_value
es None
en lugar de Some
, el pattern Some(x)
no
coincidirá.
Los parámetros de funciones, las declaraciones let
y los bucles for
solo
pueden aceptar patterns irrefutables, porque el programa no puede hacer nada
significativo cuando los valores no coinciden. Las expresiones if let
y
while let
aceptan patterns refutables e irrefutables, pero el compilador
advierte contra los patterns irrefutables porque, por definición, están
destinados a manejar posibles fallas: la funcionalidad de una condicional está
en su capacidad de realizar de manera diferente dependiendo del éxito o el
fracaso.
En general, no debería preocuparse por la distinción entre patterns refutables e irrefutables; sin embargo, debe estar familiarizado con el concepto de refutabilidad para poder responder cuando lo vea en un mensaje de error. En esos casos, deberá cambiar el pattern o la construcción que está utilizando el pattern, según el comportamiento previsto del código.
Veamos un ejemplo de lo que sucede cuando intentamos usar un pattern refutable
donde Rust requiere un pattern irrefutable y viceversa. El Listado 19-8 muestra
una declaración let
, pero para el pattern hemos especificado Some(x)
, un
pattern refutable. Como puede imaginar, este código no se compilará.
fn main() {
let some_option_value: Option<i32> = None;
let Some(x) = some_option_value;
}
Si some_option_value
fuera un valor None
, no coincidiría con el pattern
Some(x)
, lo que significa que el pattern es refutable. Sin embargo, la
declaración let
solo puede aceptar un pattern irrefutable porque no hay nada
válido que el código pueda hacer con un valor None
. En tiempo de compilación,
Rust se quejará de que hemos intentado usar un pattern refutable donde se
requiere un pattern irrefutable:
$ cargo run
Compiling patterns v0.1.0 (file:///projects/patterns)
error[E0005]: refutable pattern in local binding
--> src/main.rs:3:9
|
3 | let Some(x) = some_option_value;
| ^^^^^^^ pattern `None` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
= note: the matched value is of type `Option<i32>`
help: you might want to use `let else` to handle the variant that isn't matched
|
3 | let Some(x) = some_option_value else { todo!() };
| ++++++++++++++++
For more information about this error, try `rustc --explain E0005`.
error: could not compile `patterns` (bin "patterns") due to 1 previous error
Debido a que no hemos cubierto (¡y no pudimos cubrir!) Cada valor válido con el
pattern Some(x)
, Rust produce un error del compilador.
Si tenemos un pattern refutable donde se necesita un patrón irrefutable,
podemos solucionarlo cambiando el código que utiliza el patrón: en lugar de
usar let
, podemos usar if let
. Entonces, si el pattern no coincide, el
código simplemente omitirá el código entre llaves, dándole una forma de
continuar válidamente. El Listado 19-9 muestra cómo solucionar el código del
Listado 19-8.
fn main() { let some_option_value: Option<i32> = None; if let Some(x) = some_option_value { println!("{x}"); } }
¡Le hemos dado una solución al código! Este código es perfectamente válido ahora.
Sin embargo, significa que no podemos usar un pattern irrefutable sin recibir un
error. Si le damos a if let
un pattern que siempre coincidirá, como x
, como
se muestra en el Listado 18-10, el compilador dará una advertencia.
fn main() { if let x = 5 { println!("{x}"); }; }
Rust se queja de que no tiene sentido usar if let
con un pattern
irrefutable:
$ cargo run
Compiling patterns v0.1.0 (file:///projects/patterns)
warning: irrefutable `if let` pattern
--> src/main.rs:2:8
|
2 | if let x = 5 {
| ^^^^^^^^^
|
= note: this pattern will always match, so the `if let` is useless
= help: consider replacing the `if let` with a `let`
= note: `#[warn(irrefutable_let_patterns)]` on by default
warning: `patterns` (bin "patterns") generated 1 warning
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.39s
Running `target/debug/patterns`
5
Por esta razón, las opciones del match deben usar patterns refutables, excepto
por la última opción, que debe coincidir con cualquier valor restante con un
pattern irrefutable. Rust nos permite usar un pattern irrefutable en un match
con solo un brazo, pero esta sintaxis no es particularmente útil y podría
reemplazarse con una declaración let
más simple.
Ahora que sabes dónde usar patterns y la diferencia entre patterns refutables e irrefutables, cubramos toda la sintaxis que podemos usar para crear patterns.