1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
#[cfg(test)] mod tests; mod implements; /// Trait for equality comparisons that are [approximately equal](https://en.wikipedia.org/wiki/Approximation) /// /// /// This trait allows for approximate equality, for results that just have to be *"good enough"*. /// /// Herein `a ~= b` implies that `a.aeq(b)` and `a !~=` implies `a.nae(b)`. /// /// The approximate equality, however, must be (for all `a`, `b` and `c`) /// symmetric, in that: `a ~= b` implies `b ~= a`. /// /// ## How can I implement `ApproxEq`? /// /// `ApproxEq` only requires the [`aeq`] method be implemented; [`nae`] is defined /// in terms of it by default. Any implementation of [`ane`] *must* respect /// the rule that [`aeq`] is a strict inverse of [`ane`]; that is, `!(a ~= b)` if /// and only if `a !~= b`. /// /// An example implementation for a domain in which two books are considered /// the same book if their ISBNs have the same parity, even if the formats differ: /// /// ``` /// use approxeq::ApproxEq; /// /// enum BookFormat { /// Paperback, /// Hardback, /// Ebook, /// } /// /// struct Book { /// isbn: i32, /// format: BookFormat, /// } /// /// impl ApproxEq for Book { /// fn aeq(&self, other: &Self) -> bool { /// self.isbn % 2 == other.isbn % 2 /// } /// } /// /// let b1 = Book { isbn: 3, format: BookFormat::Paperback }; /// let b2 = Book { isbn: 3, format: BookFormat::Ebook }; /// let b3 = Book { isbn: 10, format: BookFormat::Paperback }; /// /// assert!(b1.aeq(&b2)); /// assert!(b1.nae(&b3)); /// ``` /// /// ## How can I compare two different types? /// /// The type you can compare with is controlled by `ApproxEq`'s type parameter. /// For example, let's tweak our previous code a bit: /// /// ``` /// use approxeq::ApproxEq; /// /// #[derive(PartialEq)] /// enum BookFormat { /// Paperback, /// Hardback, /// Ebook, /// } /// /// struct Book { /// isbn: i32, /// format: BookFormat, /// } /// /// // Implement <Book> ~= <BookFormat> comparisons /// impl ApproxEq<BookFormat> for Book { /// fn aeq(&self, other: &BookFormat) -> bool { /// match self.format { /// BookFormat::Ebook => self.format == *other, /// _ => other != &BookFormat::Ebook, /// } /// } /// } /// /// // Implement <BookFormat> ~= <Book> comparisons /// impl ApproxEq<Book> for BookFormat { /// fn aeq(&self, other: &Book) -> bool { /// *self == BookFormat::Ebook && other.format == BookFormat::Ebook || /// *self != BookFormat::Ebook && other.format != BookFormat::Ebook /// } /// } /// /// let b1 = Book { isbn: 3, format: BookFormat::Paperback }; /// /// assert!(b1.aeq(&BookFormat::Paperback)); /// assert!(BookFormat::Ebook.nae(&b1)); /// ``` /// /// By changing `impl ApproxEq for Book` to `impl ApproxEq<BookFormat> for Book`, /// we allow `BookFormat`s to be compared with `Book`s. pub trait ApproxEq<Rhs: ?Sized = Self> { fn aeq(&self, other: &Rhs) -> bool; fn nae(&self, other: &Rhs) -> bool { !self.aeq(other) } }