You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
.. guideline:: Do not use an integer type as a divisor during integer division
86
85
:id: gui_7y0GAMmtMhch
87
86
:category: advisory
88
87
:status: draft
@@ -92,32 +91,39 @@ Expressions
92
91
:scope: module
93
92
:tags: numerics, subset
94
93
95
-
This guideline applies when a `DivisionExpression
96
-
<https://rust-lang.github.io/fls/expressions.html#syntax_divisionexpression>`_ or `RemainderExpression
97
-
<https://rust-lang.github.io/fls/expressions.html#syntax_remainderexpression>`_ is used with a RightOperand of
98
-
`integer type <https://rust-lang.github.io/fls/types-and-traits.html#integer-types>`_.
94
+
Do not provide a right operand of
95
+
`integer type <https://rust-lang.github.io/fls/types-and-traits.html#integer-types>`_
96
+
during a `division expression
97
+
<https://rust-lang.github.io/fls/expressions.html#syntax_divisionexpression>`_ or `remainder expression
98
+
<https://rust-lang.github.io/fls/expressions.html#syntax_remainderexpression>`_ when the left operand also has integer type.
99
+
100
+
This rule applies to the following primitive integer types:
101
+
102
+
* ``i8``
103
+
* ``i16``
104
+
* ``i32``
105
+
* ``i64``
106
+
* ``i128``
107
+
* ``u8``
108
+
* ``u16``
109
+
* ``u32``
110
+
* ``u64``
111
+
* ``u128``
112
+
* ``usize``
113
+
* ``isize``
99
114
100
115
.. rationale::
101
116
:id: rat_vLFlPWSCHRje
102
117
:status: draft
103
118
104
-
The built-in semantics for these expressions can result in panics when division by zero occurs. It is
105
-
recommended to either:
106
-
107
-
* Use checked division functions, which ensure the programmer handles the case when the divisor is zero, or
108
-
* To create divisors using :std:`std::num::NonZero`, which then allows the programmer to perform those
109
-
operations knowing that their divisor is not zero.
110
-
111
-
**Note:** since the compiler can assume the value of a :std:`std::num::NonZero`
112
-
variable to not be zero, checks for zero when dividing by it can be elided in the
113
-
final binary, increasing overall performance beyond what normal division can have.
119
+
Integer division and integer remainder division both panic when the right operand has a value of zero.
120
+
Division by zero is undefined in mathematics because it leads to contradictions and there is no consistent value that can be assigned as its result.
114
121
115
122
.. non_compliant_example::
116
123
:id: non_compl_ex_0XeioBrgfh5z
117
124
:status: draft
118
125
119
-
When either the division or remainder are performed, the right operand is evaluated to zero and the
120
-
program panics.
126
+
Both the division and remainder operations in this non-compliant example will panic if evaluated because the right operand is zero.
121
127
122
128
.. code-block:: rust
123
129
@@ -129,23 +135,43 @@ Expressions
129
135
:id: compl_ex_k1CD6xoZxhXb
130
136
:status: draft
131
137
132
-
There is no compliant way to divide with an integer type. Here, instead, the developer explicitly:
138
+
Checked division prevents division by zero from occurring.
139
+
The programmer can then handle the returned :std:`std::option::Option`.
140
+
Using checked division and remainder is particularly important in the signed integer case,
141
+
where arithmetic overflow can also occur when dividing the minimum representable value by -1.
142
+
143
+
.. code-block:: rust
144
+
145
+
// Using the checked division API
146
+
let y = match 5i32.checked_div(0) {
147
+
None => 0
148
+
Some(r) => r
149
+
};
150
+
151
+
// Using the checked remainder API
152
+
let z = match 5i32.checked_rem(0) {
153
+
None => 0
154
+
Some(r) => r
155
+
};
156
+
157
+
.. compliant_example::
158
+
:id: compl_ex_k1CD6xoZxhXc
159
+
:status: draft
133
160
134
-
* Uses a checked division function, which ensures a zero divisor is handled separately, and
135
-
* Creates a divisor using :std:`std::num::NonZero`, which outsources the check for zero to the
136
-
construction of that struct. It's worth noting that such a divisor can be used multiple times after it's been created, whilst keeping the guarantee that such divisions will be safe.
161
+
This compliant solution creates a divisor using :std:`std::num::NonZero`.
162
+
:std:`std::num::NonZero` is a wrapper around primitive integer types that guarantees the contained value is never zero.
163
+
:std:`std::num::NonZero::new` creates a new binding that represents a value that is known not to be zero.
164
+
This ensures that functions operating on its value can correctly assume that they are not being given zero as their input.
165
+
166
+
Note that the test for arithmetic overflow that occurs when dividing the minimum representable value by -1 is unnecessary
167
+
in this compliant example because the result of the division expression is an unsigned integer type.
137
168
138
169
.. code-block:: rust
139
170
140
-
let x = 0;
171
+
let x = 0u32;
141
172
if let Some(divisor) = match NonZero::<u32>::new(x) {
142
-
let result = 5 / divisor;
173
+
let result = 5u32 / divisor;
143
174
}
144
-
let result = match 5u32.checked_rem(x) {
145
-
None => 0,
146
-
Some(r) => r,
147
-
}
148
-
149
175
150
176
.. guideline:: Do not divide by 0
151
177
:id: gui_kMbiWbn8Z6g5
@@ -157,64 +183,79 @@ Expressions
157
183
:scope: system
158
184
:tags: numerics, defect
159
185
160
-
This guideline applies when unsigned integer or two’s complement division is performed during the
0 commit comments