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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
//! # `cadd`: painless checked arithmetics and conversions
//!
//! Features:
//! * [`ops`]: checked arithmetics with `Result`, informative errors, and backtrace.
//! * [`Cinto`](convert::Cinto): `TryInto` alternative for type conversions with better error messages and backtrace.
//! Works for integer types, other primitives, arrays, and string-like types.
//! * [`SaturatingInto`](convert::SaturatingInto):
//! infallible number conversion that returns the closest valid value.
//! * [`non_zero`](convert::non_zero) and [`to_non_zero()`](convert::ToNonZero):
//! conversion to [`NonZero`] with `Result`, informative errors, and backtrace.
//! * <code>.[into_type](convert::IntoType)::<T>()</code>
//! as an alternative to `into()` and `try_into()` without type inference errors.
//! * `no_std` support (`alloc` is still required).
//!
//! # Introduction
//!
//! Rust generally offers amazing capability to create safe, correct programs. However, since it is still
//! a systems programming language, there are still a few aspects where the "default" or the easiest way
//! to do something is the cheapest, but not always the correct one. One of the most ubiquitous example
//! is the behavior of arithmetic operations on integer types and casts between them:
//!
//! * Many arithmetic operators (`+`, `-`, `*`, ...) and some functions (like `.pow()`) are
//! *unchecked* in Release mode. This means that they can silently overflow and wrap, producing a value
//! that is not the true output of the operation.
//! * Some of the operations (like `a / b` and `a.ilog(b)`) will panic if their preconditions
//! are unmet.
//! * `as` conversion between integer types will silently truncate values as necessary to fit the output type.
//! It's also capable of reinterpreting signed values as unsigned and vice versa, producing a value that
//! is not equal to the input.
//!
//! Sometimes this behavior is fine. However, there are many cases where performance is not as important
//! as correctness. For most high level applications, calculating the values precisely is much more important,
//! as these values may represent things like amount of money or amount of physical goods.
//! Arguably, **checked math and conversions should be the default**. A faster, but potentially wrapping or
//! truncating alternative should only be chosen in specific cases, after ensuring that it will not cause
//! a logic bug.
//!
//! Rust's core library provides very good APIs for checked operations. The only problem with them is that
//! they require much more verbose code. Compare a simple `a + b` with a properly done checked addition:
//! `a.checked_add(b).ok_or(Error::Overflow)?`. Any non-trivial code quickly becomes a wall of checked
//! calls and numerous error conversions.
//!
//! This is where `cadd` comes in. This crate aims to provide a set of APIs that are (almost)
//! as easy to use as the basic operations, while being a safe, correct default. These functions
//! never wrap, truncate, reinterpret, or panic.
//!
//! Any errors returned by `cadd` will contain as much information as possible (to a reasonable limit)
//! and will also capture a backtrace if it's enabled. This makes it easy to integrate it into
//! any error handling approach, be it custom error types or "catch-all" errors like `anyhow`.
//!
//! # Example
//!
//! The easiest, but error-prone approach:
//! ```
//! # let price = 1_u64;
//! # let discount_rate = 1_u64;
//! # let price = 1_u64;
//! let amount = price - discount_rate * price / 100;
//! let output = amount as u32;
//! ```
//! A safe approach that uses `std` checked functions and returns a non-informative error on overflow:
//! ```
//! # #[derive(Debug, thiserror::Error)]
//! # enum Error { #[error("overflow")] Overflow }
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # let price = 1_u64;
//! # let discount_rate = 1_u64;
//! # let price = 1_u64;
//! let amount = discount_rate
//! .checked_mul(price)
//! .and_then(|v| v.checked_div(100))
//! .and_then(|v| price.checked_sub(v))
//! .ok_or_else(|| Error::Overflow)?;
//! let output: u32 = amount.try_into().map_err(|_| Error::Overflow)?;
//! # Ok(())
//! # }
//! ```
//! A safe and concise approach with traits and functions from [`cadd::ops`](ops),
//! returning an informative error:
//! ```
//! # #[derive(Debug, thiserror::Error)]
//! # enum Error { #[error("overflow")] Overflow }
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # let price = 1_u64;
//! # let discount_rate = 1_u64;
//! # let price = 1_u64;
//! use cadd::{ops::{csub, Cmul, Cdiv}, convert::IntoType};
//!
//! let amount = csub(price, discount_rate.cmul(price)?.cdiv(100)?)?;
//! let output = amount.cinto_type::<u32>()?;
//! # Ok(())
//! # }
//! ```
//! Errors may look like this:
//! ```text
//! failed to compute 20 - 40: u64 overflow
//! stack backtrace:
//! 0: std::backtrace_rs::backtrace::libunwind::trace
//! ...
//! ```
//!
//! # Recommended clippy lints
//!
//! In order to eliminate unintended side effects, wraps, truncations, and signed-vs-unsigned reinterpretation,
//! it's recommended to set the following clippy lints to `warn` level:
//!
//! - `arithmetic_side_effects`
//! - `cast_possible_wrap`
//! - `cast_precision_loss`
//! - `cast_sign_loss`
extern crate alloc;
extern crate std;
/// Enhanced checked arithmetics.
///
/// Many operators on integer primitives (`a + b`, `a / b`, etc) and associated functions (`a.pow(b)`, `a.ilog(b)`, etc)
/// can overflow or fail under certain conditions. With debug assertions enabled (default when building in debug mode),
/// any such failures will be caught and converted into a panic. With debug assertions disabled
/// (default when building in release mode), some failures (like division by zero) will still result in a panic,
/// and overflows will silently return an overflown value, which is often an unexpected and incorrect result.
/// Therefore, to write the code that returns correct values on every valid input and correctly handles every invalid input,
/// it's highly recommended to use checked alternatives.
///
/// Rust offers great capabilities for checked arithmetics. For every operation that can overflow or otherwise fail,
/// the standard library contains a function with the `checked_` prefix that returns `Option`. For example:
/// ```
/// assert_eq!(300_u32.checked_add(200_u32), Some(500));
/// assert_eq!(3_000_000_000_u32.checked_add(2_000_000_000_u32), None);
/// ```
/// However, writing code that uses checked functions can be quite cumbersome, especially if you use `Result`
/// throughout the code:
/// ```
/// # use std::error::Error;
/// fn calculate_trajectory(mass: u32, velocity: u32) -> Result<(), Box<dyn Error>> {
/// let kinetic_energy = velocity
/// .checked_pow(2)
/// .and_then(|v| mass.checked_mul(v))
/// .and_then(|v| v.checked_div(2))
/// .ok_or_else(|| "mass or velocity too large")?;
/// //...
/// Ok(())
/// }
/// ```
/// It can be improved by moving all arithmetics into functions that return `Option` so that you can use `?`
/// for early returns, but it requires even more restructuring of the code.
///
/// This crate offers a set of traits and functions for easy handling of checked arithmetics.
/// These traits and functions are modelled after the `checked_*` family of functions provided by the standard
/// library for primitive numeric types, such as [`checked_add`](u32::checked_add),
/// [`checked_pow`](u32::checked_pow), etc.
///
/// Function names are shorter, so it's easier to keep the code readable.
/// There is only one rule to remember: replace "checked_" with "c" to get the corresponding improved function.
/// `checked_add` becomes `cadd`, `checked_pow` becomes `cpow`, and so on.
///
/// All functions from this crate return `Result` instead of `Option`, enabling the use of `?`
/// in functions returning `Result`.
/// ```
/// use cadd::ops::{Cpow, Cdiv, cmul};
///
/// fn kinetic_energy(mass: u32, velocity: u32) -> cadd::Result<u32> {
/// cmul(mass, velocity.cpow(2)?)?.cdiv(2)
/// }
/// ```
/// In case of an overflow or another failure, the returned error contains a meaningful error message
/// and a backtrace (if enabled):
/// ```
/// # use cadd::ops::{Cpow, Cdiv, cmul};
/// # fn kinetic_energy(mass: u32, velocity: u32) -> cadd::Result<u32> {
/// # cmul(mass, velocity.cpow(2)?)?.cdiv(2)
/// # }
/// # fn backtrace_enabled() -> bool {
/// # match std::env::var("RUST_LIB_BACKTRACE") {
/// # Ok(s) => s != "0",
/// # Err(_) => match std::env::var("RUST_BACKTRACE") {
/// # Ok(s) => s != "0",
/// # Err(_) => false,
/// # },
/// # }
/// # }
/// let err_msg = kinetic_energy(10, 100_000).unwrap_err().to_string();
/// if backtrace_enabled() {
/// assert!(err_msg.starts_with(
/// "failed to compute pow(100000, 2): u32 overflow\nstack backtrace:\n"
/// ));
/// } else {
/// assert_eq!(err_msg, "failed to compute pow(100000, 2): u32 overflow");
/// }
/// ```
/// Both method style (`a.cadd(b)`) and function style (`cadd(a, b)`) APIs are available.
/// Free functions can make expressions more readable when there are multiple levels of nesting:
/// ```
/// use cadd::ops::{cadd, cmul};
///
/// fn f1(a1: u32, b1: u32, a2: u32, b2: u32) -> cadd::Result<u32> {
/// cadd(
/// cmul(a1, b1)?,
/// cmul(a2, b2)?,
/// )
/// }
/// ```
/// On the other hand, method style may be preferred for better chaining:
/// ```
/// use cadd::ops::{Cadd, Cmul, Cdiv};
///
/// fn f2(a1: u32, b1: u32, c1: u32, d1: u32) -> cadd::Result<u32> {
/// a1.cadd(b1)?
/// .cmul(c1)?
/// .cdiv(d1)
/// }
/// ```
/// Assignment functions ([`cadd_assign`](ops::Cadd::cadd_assign), [`csub_assign`](ops::Csub::csub_assign), etc.)
/// are also provided.
///
/// For binary operations, the traits use an associated type to specify the type of the second argument.
/// It provides better type inference: when the type of the first argument is known, the type of the second argument
/// becomes known as well.
///
/// See also: [crate level documentation](crate).
pub use crateError;
use ;
/// `Result` with error type defaulting to `cadd::Error`.
pub type Result<T, E = Error> = Result;
;
impl_is_negative!;
impl_is_negative!;
impl_is_negative!;
impl_is_negative!;
impl_is_negative!;
impl_is_negative!;
impl_is_negative_non_zero!;
impl_is_negative_non_zero!;
impl_is_negative_non_zero!;
impl_is_negative_non_zero!;
impl_is_negative_non_zero!;
impl_is_negative_non_zero!;
impl_is_negative_false!;
impl_is_negative_false!;
impl_is_negative_false!;
impl_is_negative_false!;
impl_is_negative_false!;
impl_is_negative_false!;
impl_is_negative_false!;
impl_is_negative_false!;
impl_is_negative_false!;
impl_is_negative_false!;
impl_is_negative_false!;
impl_is_negative_false!;
impl_is_negative_false!;