int_conv/
sign.rs

1//! Types with signed and unsigned variants
2//!
3//! This modules focuses on describing types that have both an unsigned and signed variant,
4//! such as `i8` / `u8`.
5
6/// Types with signed and unsigned variants
7///
8/// Note that references don't currently implement this trait due to
9/// lack of `GAT`s, which are required to specify that a `&'a u8` may
10/// be cast to a `&'a i8` with the same lifetime.
11pub trait Signed {
12	/// Signed variant of this type
13	type Signed;
14
15	/// Unsigned variant of this type
16	type Unsigned;
17
18	/// Reinterprets this value as unsigned
19	fn as_unsigned(self) -> Self::Unsigned;
20
21	/// Reinterprets this value as signed
22	fn as_signed(self) -> Self::Signed;
23
24	/// Returns the absolute value of `self` as unsigned.
25	fn abs_unsigned(self) -> Self::Unsigned;
26
27	// TODO: Maybe add a `fn signal() -> Signal` method? Or maybe two `is_positive` / `is_negative` methods.
28}
29
30/// All types that are signed
31pub trait IsSigned: Signed<Signed = Self> {}
32impl<T: Signed<Signed = T>> IsSigned for T {}
33
34/// All types that are unsigned
35pub trait IsUnsigned: Signed<Unsigned = Self> {}
36impl<T: Signed<Unsigned = T>> IsUnsigned for T {}
37
38/// Macro to help implement [`Signed`]
39macro_rules! impl_signed {
40	(- $TSigned:ty : + $TUnsigned:ty) => {
41		// Make sure `T` has the same size as it's unsigned variant
42		::static_assertions::assert_eq_size!($TSigned, $TUnsigned);
43
44		impl Signed for $TSigned {
45			type Signed = $TSigned;
46			type Unsigned = $TUnsigned;
47
48			#[inline]
49			#[allow(clippy::as_conversions)]
50			fn as_unsigned(self) -> Self::Unsigned {
51				// Casting between integers of the same size is a no-op
52				self as $TUnsigned
53			}
54
55			#[inline]
56			fn as_signed(self) -> Self::Signed {
57				self
58			}
59
60			#[inline]
61			fn abs_unsigned(self) -> Self::Unsigned {
62				// Note: Branch is optimized by compiler in release mode.
63				if self < 0 {
64					// Note: We don't use `-self.as_unsigned()` because it can panic
65					(!self.as_unsigned()).wrapping_add(1)
66				} else {
67					self.as_unsigned()
68				}
69			}
70		}
71
72		impl Signed for $TUnsigned {
73			type Signed = $TSigned;
74			type Unsigned = $TUnsigned;
75
76			#[inline]
77			fn as_unsigned(self) -> Self::Unsigned {
78				self
79			}
80
81			#[inline]
82			#[allow(clippy::as_conversions)]
83			fn as_signed(self) -> Self::Signed {
84				// Casting between integers of the same size is a no-op
85				self as $TSigned
86			}
87
88			#[inline]
89			fn abs_unsigned(self) -> Self::Unsigned {
90				// Note: We're already unsigned
91				self
92			}
93		}
94	};
95}
96
97impl_signed! { -i8    : +u8   }
98impl_signed! { -i16   : +u16  }
99impl_signed! { -i32   : +u32  }
100impl_signed! { -i64   : +u64  }
101impl_signed! { -i128  : +u128 }
102impl_signed! { -isize : +usize }
103
104// Check that they all implement `Signed` / `IsSigned` / `IsUnsigned`
105static_assertions::assert_impl_all! { i8   : Signed, IsSigned   }
106static_assertions::assert_impl_all! { i16  : Signed, IsSigned   }
107static_assertions::assert_impl_all! { i32  : Signed, IsSigned   }
108static_assertions::assert_impl_all! { i64  : Signed, IsSigned   }
109static_assertions::assert_impl_all! { i128 : Signed, IsSigned   }
110static_assertions::assert_impl_all! { isize: Signed, IsSigned   }
111static_assertions::assert_impl_all! { u8   : Signed, IsUnsigned }
112static_assertions::assert_impl_all! { u16  : Signed, IsUnsigned }
113static_assertions::assert_impl_all! { u32  : Signed, IsUnsigned }
114static_assertions::assert_impl_all! { u64  : Signed, IsUnsigned }
115static_assertions::assert_impl_all! { u128 : Signed, IsUnsigned }
116static_assertions::assert_impl_all! { usize: Signed, IsUnsigned }
117
118// Check that all associated types are correct
119static_assertions::assert_type_eq_all! { <i8    as Signed>::Signed, <u8    as Signed>::Signed, i8    }
120static_assertions::assert_type_eq_all! { <i16   as Signed>::Signed, <u16   as Signed>::Signed, i16   }
121static_assertions::assert_type_eq_all! { <i32   as Signed>::Signed, <u32   as Signed>::Signed, i32   }
122static_assertions::assert_type_eq_all! { <i64   as Signed>::Signed, <u64   as Signed>::Signed, i64   }
123static_assertions::assert_type_eq_all! { <i128  as Signed>::Signed, <u128  as Signed>::Signed, i128  }
124static_assertions::assert_type_eq_all! { <isize as Signed>::Signed, <usize as Signed>::Signed, isize }
125static_assertions::assert_type_eq_all! { <i8    as Signed>::Unsigned, <u8    as Signed>::Unsigned, u8    }
126static_assertions::assert_type_eq_all! { <i16   as Signed>::Unsigned, <u16   as Signed>::Unsigned, u16   }
127static_assertions::assert_type_eq_all! { <i32   as Signed>::Unsigned, <u32   as Signed>::Unsigned, u32   }
128static_assertions::assert_type_eq_all! { <i64   as Signed>::Unsigned, <u64   as Signed>::Unsigned, u64   }
129static_assertions::assert_type_eq_all! { <i128  as Signed>::Unsigned, <u128  as Signed>::Unsigned, u128  }
130static_assertions::assert_type_eq_all! { <isize as Signed>::Unsigned, <usize as Signed>::Unsigned, usize }
131
132#[cfg(test)]
133mod tests {
134	use super::*;
135
136	#[test]
137	#[rustfmt::skip]
138	fn as_unsigned_positive() {
139		assert_eq!(u8   ::as_unsigned(1), 1);
140		assert_eq!(u16  ::as_unsigned(1), 1);
141		assert_eq!(u32  ::as_unsigned(1), 1);
142		assert_eq!(u64  ::as_unsigned(1), 1);
143		assert_eq!(u128 ::as_unsigned(1), 1);
144		assert_eq!(usize::as_unsigned(1), 1);
145	}
146
147	#[test]
148	#[rustfmt::skip]
149	fn as_unsigned_negative() {
150		assert_eq!(i8   ::as_unsigned(-1), u8   ::MAX);
151		assert_eq!(i16  ::as_unsigned(-1), u16  ::MAX);
152		assert_eq!(i32  ::as_unsigned(-1), u32  ::MAX);
153		assert_eq!(i64  ::as_unsigned(-1), u64  ::MAX);
154		assert_eq!(i128 ::as_unsigned(-1), u128 ::MAX);
155		assert_eq!(isize::as_unsigned(-1), usize::MAX);
156	}
157
158	#[test]
159	#[rustfmt::skip]
160	fn as_unsigned_negative_big() {
161		assert_eq!(i8   ::as_unsigned(i8   ::MIN), u8   ::MAX / 2 + 1);
162		assert_eq!(i16  ::as_unsigned(i16  ::MIN), u16  ::MAX / 2 + 1);
163		assert_eq!(i32  ::as_unsigned(i32  ::MIN), u32  ::MAX / 2 + 1);
164		assert_eq!(i64  ::as_unsigned(i64  ::MIN), u64  ::MAX / 2 + 1);
165		assert_eq!(i128 ::as_unsigned(i128 ::MIN), u128 ::MAX / 2 + 1);
166		assert_eq!(isize::as_unsigned(isize::MIN), usize::MAX / 2 + 1);
167	}
168
169	#[test]
170	#[rustfmt::skip]
171	fn as_signed_positive() {
172		assert_eq!(u8   ::as_signed(1), 1);
173		assert_eq!(u16  ::as_signed(1), 1);
174		assert_eq!(u32  ::as_signed(1), 1);
175		assert_eq!(u64  ::as_signed(1), 1);
176		assert_eq!(u128 ::as_signed(1), 1);
177		assert_eq!(usize::as_signed(1), 1);
178	}
179
180	#[test]
181	#[rustfmt::skip]
182	fn as_signed_negative() {
183		assert_eq!(i8   ::as_signed(u8   ::MAX.as_signed()), -1);
184		assert_eq!(i16  ::as_signed(u16  ::MAX.as_signed()), -1);
185		assert_eq!(i32  ::as_signed(u32  ::MAX.as_signed()), -1);
186		assert_eq!(i64  ::as_signed(u64  ::MAX.as_signed()), -1);
187		assert_eq!(i128 ::as_signed(u128 ::MAX.as_signed()), -1);
188		assert_eq!(isize::as_signed(usize::MAX.as_signed()), -1);
189	}
190
191	#[test]
192	#[rustfmt::skip]
193	fn abs_unsigned_unsigned() {
194		assert_eq!(u8   ::abs_unsigned(1), 1);
195		assert_eq!(u16  ::abs_unsigned(1), 1);
196		assert_eq!(u32  ::abs_unsigned(1), 1);
197		assert_eq!(u64  ::abs_unsigned(1), 1);
198		assert_eq!(u128 ::abs_unsigned(1), 1);
199		assert_eq!(usize::abs_unsigned(1), 1);
200	}
201
202	#[test]
203	#[rustfmt::skip]
204	fn abs_unsigned_unsigned_big() {
205		assert_eq!(u8   ::abs_unsigned(u8   ::MAX), u8   ::MAX);
206		assert_eq!(u16  ::abs_unsigned(u16  ::MAX), u16  ::MAX);
207		assert_eq!(u32  ::abs_unsigned(u32  ::MAX), u32  ::MAX);
208		assert_eq!(u64  ::abs_unsigned(u64  ::MAX), u64  ::MAX);
209		assert_eq!(u128 ::abs_unsigned(u128 ::MAX), u128 ::MAX);
210		assert_eq!(usize::abs_unsigned(usize::MAX), usize::MAX);
211	}
212
213	#[test]
214	#[rustfmt::skip]
215	fn abs_unsigned_signed_positive() {
216		assert_eq!(i8   ::abs_unsigned(1), 1);
217		assert_eq!(i16  ::abs_unsigned(1), 1);
218		assert_eq!(i32  ::abs_unsigned(1), 1);
219		assert_eq!(i64  ::abs_unsigned(1), 1);
220		assert_eq!(i128 ::abs_unsigned(1), 1);
221		assert_eq!(isize::abs_unsigned(1), 1);
222	}
223
224	#[test]
225	#[rustfmt::skip]
226	fn abs_unsigned_signed_positive_big() {
227		assert_eq!(i8   ::abs_unsigned(i8   ::MAX), u8   ::MAX / 2);
228		assert_eq!(i16  ::abs_unsigned(i16  ::MAX), u16  ::MAX / 2);
229		assert_eq!(i32  ::abs_unsigned(i32  ::MAX), u32  ::MAX / 2);
230		assert_eq!(i64  ::abs_unsigned(i64  ::MAX), u64  ::MAX / 2);
231		assert_eq!(i128 ::abs_unsigned(i128 ::MAX), u128 ::MAX / 2);
232		assert_eq!(isize::abs_unsigned(isize::MAX), usize::MAX / 2);
233	}
234
235	#[test]
236	#[rustfmt::skip]
237	fn abs_unsigned_signed_negative() {
238		assert_eq!(i8   ::abs_unsigned(-1), 1);
239		assert_eq!(i16  ::abs_unsigned(-1), 1);
240		assert_eq!(i32  ::abs_unsigned(-1), 1);
241		assert_eq!(i64  ::abs_unsigned(-1), 1);
242		assert_eq!(i128 ::abs_unsigned(-1), 1);
243		assert_eq!(isize::abs_unsigned(-1), 1);
244	}
245
246	#[test]
247	#[rustfmt::skip]
248	fn abs_unsigned_signed_negative_big() {
249		assert_eq!(i8   ::abs_unsigned(i8   ::MIN), u8   ::MAX / 2 + 1);
250		assert_eq!(i16  ::abs_unsigned(i16  ::MIN), u16  ::MAX / 2 + 1);
251		assert_eq!(i32  ::abs_unsigned(i32  ::MIN), u32  ::MAX / 2 + 1);
252		assert_eq!(i64  ::abs_unsigned(i64  ::MIN), u64  ::MAX / 2 + 1);
253		assert_eq!(i128 ::abs_unsigned(i128 ::MIN), u128 ::MAX / 2 + 1);
254		assert_eq!(isize::abs_unsigned(isize::MIN), usize::MAX / 2 + 1);
255	}
256}