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}