Skip to main content

compare_variables

Macro compare_variables 

Source
compare_variables!() { /* proc-macro */ }
Expand description

A macro to compare types which implement PartialOrd.

§Overview

This macro performs comparison between two or three values of any type T which implements PartialOrd and creates a compare_variables::Comparison struct. If the comparison evaluates to true, the struct is wrapped in Ok, otherwise in Err to allow easy usage with the ? operator.

The macro syntax is

compare_variables(x _ y)

for comparing two values and

compare_variables(x _ y _ z)

for comparing three values with _ being any of the comparison operators <, <=, ==, !=, >, >=.

x, y and z can be either a literal (e.g. 3.141 or 1e10) or a variable:

use compare_variables::compare_variables;

assert!(compare_variables!(2.0 > 1.5).is_ok());

let x = 1;
let y = 2;
assert!(compare_variables!(x < 2 == y).is_ok());
assert!(compare_variables!(x >= 2).is_err());
assert!(compare_variables!(x != y).is_ok());

The following example shows how the macro can be combined with ?:

use compare_variables::{compare_variables, Comparison};

fn checked_sub(left: u16, right: u16) -> Result<u16, Comparison<u16>> {
    compare_variables!(left >= right)?;
    return Ok(left - right);
}

assert_eq!(checked_sub(2, 1).unwrap(), 1);
assert_eq!(checked_sub(2, 2).unwrap(), 0);
assert!(checked_sub(2, 3).is_err());

It is also possible to use named and anonymous struct fields as inputs:

use compare_variables::compare_variables;

struct NamedField {
   x: f64
}
let n = NamedField {x: 1.0};
assert!(compare_variables!(n.x > -1.0).is_ok());
assert!(compare_variables!(n.x > 1.0).is_err());

struct AnonymousField(i32);
let a = AnonymousField(-5);
assert!(compare_variables!(a.0 > -6).is_ok());
assert!(compare_variables!(a.0 > 1).is_err());

§Customizing the message

The keywords val and as allow to customize the treatment of variable names in the message:

use compare_variables::compare_variables;

// Error message with literals only
let err = compare_variables!(5i32 >= -1i32).unwrap_or_else(|x| x);
assert_eq!(err.to_string(), "`5 >= -1` is true");

let x = 1;
let y = 2;

// Default error message
let err = compare_variables!(x > y).unwrap_or_else(|x| x);
assert_eq!(err.to_string(), "`x (value: 1) > y (value: 2)` is false");

// Rename x in the error message
let err = compare_variables!(x as variable <= y).unwrap_or_else(|x| x);
assert_eq!(err.to_string(), "`variable (value: 1) <= y (value: 2)` is true");

// Only display the underlying value, not the variable name:
let err = compare_variables!(val x > y).unwrap_or_else(|x| x);
assert_eq!(err.to_string(), "`1 > y (value: 2)` is false");

// `as` is ignored if used together with `val`:
let err = compare_variables!(val x as variable > y).unwrap_or_else(|x| x);
assert_eq!(err.to_string(), "`1 > y (value: 2)` is false");

§Examples

use compare_variables::compare_variables;

// Different float types:
assert!(compare_variables!(1.5 < 2.0 == 3.0).is_err());
assert!(compare_variables!(1.7f32 == 1.7f32).is_ok());
let f = 2.0;
assert!(compare_variables!(f < 5.2).is_ok());
assert!(compare_variables!(f as f_var == val f).is_ok());

// Signed and unsigned integers
assert!(compare_variables!(1i32 >= 2i32).is_err());
let u = 3usize;
assert!(compare_variables!(2usize < u < 4usize).is_ok());
let i = 15i64;
assert!(compare_variables!(-10i64 <= i).is_ok());
assert!(compare_variables!(-10i64 <= i <= 10i64).is_err());

// Custom types implementing `PartialOrd`.
// Clone and Copy are not required and are only used here to simplify the example.
#[derive(PartialEq, Clone, Copy)]
struct MyFloat64(f64);

impl PartialOrd for MyFloat64 {
   fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
       return self.0.partial_cmp(&other.0);
   }
}

let myfloat1 = MyFloat64(1.0);
let myfloat2 = MyFloat64(2.0);
assert!(compare_variables!(myfloat1 == myfloat1).is_ok());
assert!(compare_variables!(myfloat1 <= myfloat2).is_ok());
assert!(compare_variables!(myfloat1 >= myfloat2).is_err());