approxeq/
lib.rs

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