How can I make match arms recognize value parameter? [duplicate] - rust

This question already has an answer here:
Why is this match pattern unreachable when using non-literal patterns?
(1 answer)
Closed 6 months ago.
I'm trying to swap contents of a struct based on values passed in associated function. However, while using match, arms do not seem to recognize the value parameter, and thus arms after first one become unreachable.
#[derive(Debug, Copy, Clone)]
pub struct Color {
pub r: u8,
pub g: u8,
pub b: u8,
pub a: u8,
}
impl Color {
pub fn swap(mut self, first: u8, second: u8) -> Color {
let mut swapped = self;
match self {
Color { r: first, g, b, a } => swapped.r = second,
Color { r, g: first, b, a } => swapped.g = second,
Color { r, g, b: first, a } => swapped.b = second,
Color { r, b, g, a: first } => swapped.a = second,
}
match self {
Color { r: second, g, b, a } => swapped.r = first,
Color { r, g: second, b, a } => swapped.g = first,
Color { r, g, b: second, a } => swapped.b = first,
Color { r, g, b, a: second } => swapped.a = first,
}
self = swapped;
self
}
}
However, if I put an actual u8 like Color { r: 10, g,b,a }, then it works.
What am I doing wrong here?

You're creating a new first binding, destructuring the Color. To perform variable value comparisons, you need a "match guard" like so:
match self {
Color { r, g, b, a } if r == first => swapped.r = second,
Color { r, g, b, a } if g == first => swapped.g = second,
Color { r, g, b, a } if b == first => swapped.b = second,
Color { r, b, g, a } if a == first => swapped.a = second,
_ => { /* no match */ },
}
The final "match-all" arm is required because the patterns are non-exhaustive, so you need to tell Rust what to do in the case that none of the other arms match. For example, it may be appropriate to panic here to signal bad input, or modify your function to return Result<Color, SomeErrorType> and return Err from the match-all arm.
Note that since you only use one field in each arm, you can ignore the others:
match self {
Color { r, .. } if r == first => swapped.r = second,
Color { g, .. } if g == first => swapped.g = second,
Color { b, .. } if b == first => swapped.b = second,
Color { a, .. } if a == first => swapped.a = second,
_ => { /* no match */ },
}

Related

Macro to match specific identifiers for inline lookups

Okay, writing my absolute first project in Rust. So, I have something like the following sort of setup:
use phf;
use std::str;
struct Values {
a: Option<char>,
b: Option<char>,
c: Option<char>
}
static MAPPING: phf::Map<&'static str, Values> = phf::phf_map! {
"some_key" => Values {
a: Some('a'),
b: Some('b'),
c: None
},
"some_other_key" => Values {
a: Some('1'),
b: None,
c: None
},
"some_third_key" => Values {
a: None,
b: Some('x'),
c: Some('y')
}
}
static NULL_VALUES: Values = Values {
a: None,
b: None,
c: None
}
// Should return a &str for any given key/val
#[macro_export]
macro_rules! get_value {
($key: ident, $val: ident) => {{
use crate::values::MAPPING;
use std::str;
let result = MAPPING.get("$key");
if let Some(r) = result {
if let Some(c) = r.$val {
if let Ok(s) = str::from_utf8(%[c as u8]) { s } else { "" }
} else { "" }
} else { "" }
}}
}
Which, it works, but it's just so much code and seeming like a whole lot of runtime overhead for no other reason than to organise some static values to avoid having to remember them all (in reality there are quite a lot and they're all raw codepoints). What I would love to be able to do is to just have a macro that takes a specific key/val and simply inlines either a known value or an empty value, but as far as I can tell there isn't any way to match a macro on a specific identifier, only any identifier... Is there any way that I can move all these lookups from runtime to compile time?
Macros can pattern match against specific identifiers — just don't use $.
macro_rules! get_value {
(some_key, a) => { Some('a') };
(some_key, b) => { Some('b') };
(some_key, c) => { None };
(some_other_key, a) => { Some(1) };
// ...
}
However, are you sure you don't want to just define a bunch of constants?
const SOME_KEY_A: Option<char> = Some('a');
const SOME_KEY_B: Option<char> = Some('b');
Or expose the Values struct you already designed, which would then be accessed like SOME_KEY.a:
const SOME_KEY: Values = Values {
a: Some('a'),
b: Some('b'),
c: None
};
That way, readers don't have to understand your macro to know that the data is just a constant. This will make your code easier to read and modify.

Exhaustivity checking for 1-to-1 mapping between enums

I'm writing some basic bioinformatics code to transcribe DNA to RNA:
pub enum DnaNucleotide {
A,
C,
G,
T,
}
pub enum RnaNucleotide {
A,
C,
G,
U,
}
fn transcribe(base: &DnaNucleotide) -> RnaNucleotide {
match base {
DnaNucleotide::A => RnaNucleotide::A,
DnaNucleotide::C => RnaNucleotide::C,
DnaNucleotide::G => RnaNucleotide::G,
DnaNucleotide::T => RnaNucleotide::U,
}
}
Is there a way to get the compiler to do an exhaustivity check also on the right side of the match statement, basically ensuring a 1-1 mapping between the two enums?
(A related question: The above is probably better represented with some kind of bijective map, but I don't want to lose the exhaustivity checking. Is there a better way?)
The fact that a one-to-one correspondence exists between two enums suggests that you should really only be using one enum behind the scenes. Here is an example of a data model that I think suits your needs. This is naturally exhaustive because there is only a single enum to begin with.
use core::fmt::{Debug, Error, Formatter};
enum NucleicAcid {
Dna,
Rna,
}
enum Nucleotide {
A,
C,
G,
TU,
}
struct BasePair {
nucleic_acid: NucleicAcid,
nucleotide: Nucleotide,
}
impl BasePair {
fn new(nucleic_acid: NucleicAcid, nucleotide: Nucleotide) -> Self {
Self {
nucleic_acid,
nucleotide,
}
}
}
impl Debug for BasePair {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
use NucleicAcid::*;
use Nucleotide::*;
let BasePair {
nucleic_acid,
nucleotide,
} = self;
let nucleic_acid_str = match nucleic_acid {
Dna => "dna",
Rna => "rna",
};
let nucleotide_str = match nucleotide {
A => "A",
C => "C",
G => "G",
TU => match nucleic_acid {
Dna => "T",
Rna => "U",
},
};
f.write_fmt(format_args!("{}:{}", nucleic_acid_str, nucleotide_str))
}
}
fn main() {
let bp1 = BasePair::new(NucleicAcid::Dna, Nucleotide::TU);
let bp2 = BasePair::new(NucleicAcid::Rna, Nucleotide::C);
println!("{:?}, {:?}", bp1, bp2);
// dna:T, rna:C
}

What's the difference between One::one() vs 1

What is the difference between One::one() and just the number 1? Is there any difference?
One::one() is intended to be used in generic code where we do not know what is the exact type of the numerical value.
It could be 1_i32, 1.0, 1_u8... depending on the exact type the One trait is bound to.
Thanks to the useful comments below, here is a minimal example to try to illustrate better (although it's late).
Trying to initialise some variables with 1 works if they are considered as integers (a and c here).
On the other hand, this does not work for a real (b here); 1.0 must be used instead.
When it comes to our own non-primtive type (Thing here), the One trait helps providing a value considered as 1 (note that the Mul trait must be implemented on this type too).
The One trait becomes really useful in a generic function in which the exact type is not already known when we need the 1 value (like mul_one() here).
use num_traits::One;
use std::ops::Mul;
#[derive(Debug)]
struct Thing {
member: String,
}
// Mul<Self, Output = Self> is required for One
impl Mul for Thing {
type Output = Self;
fn mul(
self,
rhs: Self,
) -> Self {
Self {
member: self.member + "×" + &rhs.member,
}
}
}
impl One for Thing {
fn one() -> Self {
Self {
member: "one".to_owned(),
}
}
}
fn mul_one<T: One>(arg: T) -> T {
// arg * 1 // error: expected type parameter `T`, found integer
arg * T::one()
}
fn main() {
let a: i32 = 1;
// let b: f64 = 1; // error: expected `f64`, found integer
let b: f64 = 1.0;
let c: u8 = 1;
let d = Thing::one();
println!("{:?} {:?} {:?} {:?}", a, b, c, d);
let e = mul_one(a);
let f = mul_one(b);
let g = mul_one(c);
let h = mul_one(d);
println!("{:?} {:?} {:?} {:?}", e, f, g, h);
}
/*
1 1.0 1 Thing { member: "one" }
1 1.0 1 Thing { member: "one×one" }
*/

How do I check if both variables are both Some?

I am confused about the Some(T) keyword.
I want to check for two variables, if the value is defined (not None). If that is the case, the value of this variables is processed.
I know the match pattern which works like this:
match value {
Some(val) => println!("{}", val),
None => return false,
}
If I use this pattern, it will get very messy:
match param {
Some(par) => {
match value {
Some(val) => {
//process
},
None => return false,
}
},
None => return false,
}
This can't be the right solution.
The is a possibility, to ask if the param and value is_some() That would effect code like that:
if param.is_some() && value.is_some() {
//process
}
But if I do it like that, I always have to unwrap param and value to access the values.
I thought about something like this to avoid that. But this code does not work:
if param == Some(par) && value == Some(val) {
//process
}
The idea is that the values are accessible by par and val like they are in the match version.
Is there any solution to do something like this?
If I have several Option values to match, I match on a tuple of the values:
enum Color {
Red,
Blue,
Green,
}
fn foo(a: Option<Color>, b: Option<i32>) {
match (a, b) {
(Some(Color::Blue), Some(n)) if n > 10 => println!("Blue large number"),
(Some(Color::Red), _) => println!("Red number"),
_ => (),
}
}
fn main() {
foo(Some(Color::Blue), None);
foo(Some(Color::Blue), Some(20));
}
This allows me to match the combinations that are interesting, and discard the rest (or return false, if that is what you want to do).
If your function is processing multiple Option values, and would like to discard them if they're not Some, your function could return an Option itself:
fn foo(param: Option<usize>, value: Option<usize>) -> Option<usize> {
let result = param? + value?;
Some(result)
}
This will short-circuit the function in case there's a None value stored in either param or value.
Please read the book for more information on the ? operator.
If your function can't return an Option, you can still get away with destructuring using if let or match:
let x = if let (Some(p), Some(v)) = (param, value) {
p + v
} else {
return 0;
}
let x = match (param, value) {
(Some(p), Some(v)) => p + v,
(Some(p), _) => p,
(_, Some(v) => v,
_ => return 0,
}
Please read What is this question mark operator about? for more information on the ? operator
Please read this chapter in Rust by Example for more information on destructuring multiple things at once
There's a couple more alternatives not yet listed:
If you're willing to use experimental features (and hence the nightly compiler) you can use a try block as an alternative of extracting a function.
#![feature(try_blocks)]
fn main() {
let par: Option<f32> = Some(1.0f32);
let value: Option<f32> = Some(2.0f32);
let x: Option<f32> = try { par? + value? };
println!("{:?}", x);
}
Another alternative is to use map which only applies if the value is not None
let x: Option<f32> = par.map(|p| value.map(|v| p + v));

Is there a non-messy way to chain the results of functions that return Option values?

I have some code that looks like this:
f(a).and_then(|b| {
g(b).and_then(|c| {
h(c).map(|d| {
do_something_with(a, b, c, d)
})
})
})
Where f, g, and h return Option values. I need to use all the intermediate values (a, b, c, and d) in the do_something_with calculation. The indentation is very deep. Is there a better way to do this? Ideally it would look something like this (which of course doesn't work):
try {
let b = f(a);
let c = g(b);
let d = h(c);
do_something_with(a, b, c, d)
} rescue NonexistentValueException {
None
}
Rust 1.22
The question mark operator now supports Option, so you can write your function as
fn do_something(a: i32) -> Option<i32> {
let b = f(a)?;
let c = g(b)?;
let d = h(c)?;
do_something_with(a, b, c, d) // wrap in Some(...) if this doesn't return an Option
}
Rust 1.0
The Rust standard library defines a try! macro (and, equivalently, the ? operator, as of Rust 1.13) that solves this problem for Result. The macro looks like this:
macro_rules! try {
($expr:expr) => (match $expr {
$crate::result::Result::Ok(val) => val,
$crate::result::Result::Err(err) => {
return $crate::result::Result::Err($crate::convert::From::from(err))
}
})
}
If the argument is Err, it returns from the function with that Err value. Otherwise, it evaluates to the value wrapped in Ok. The macro can only be used in a function that returns Result, because it returns the error it meets.
We can make a similar macro for Option:
macro_rules! try_opt {
($expr:expr) => (match $expr {
::std::option::Option::Some(val) => val,
::std::option::Option::None => return None
})
}
You can then use this macro like this:
fn do_something(a: i32) -> Option<i32> {
let b = try_opt!(f(a));
let c = try_opt!(g(b));
let d = try_opt!(h(c));
do_something_with(a, b, c, d) // wrap in Some(...) if this doesn't return an Option
}
Inspired from the concept of try! for Result, let's wrap our own macro to early-return from the scope if the monad drops to None.
macro_rules! get(
($e:expr) => (match $e { Some(e) => e, None => return None })
);
(Stolen from this reddit thread)
Now you can run your code linearly:
fn blah() -> Option<...> { // ... is the return type of do_something_with()
let a = 123;
let b = get!(f(a));
let c = get!(g(b));
let d = get!(h(c));
do_something_with(a, b, c, d)
}
(runnable gist)

Resources