-
Notifications
You must be signed in to change notification settings - Fork 214
Style: checked integer arithmetic? #126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Rust panics on overflow in debug mode, so overflow is not as common a bug in well-tested rust code in my experience. It's typical practice to use regular integer types and test in debug mode. We should definitely use checked methods in code where overflow may occur based on user input. |
Should we use checked arithmetic when doing something like pulling items from a user-provided iterator? The iterator could in theory be longer than |
When does the iterator length come up when pulling from an iterator? You just pull. It's only when you collect into a vector that it matters, and iirc |
It came up for me a couple days ago when I was pulling items from an int iterator and wanted to keep a |
|
Ah yeah in that case you should. |
OK. So in that case, going back to my original question: what do you think about requiring that all arithmetic is explicit about which type of arithmetic you want? Rust standard library gives you several choices:
I assume wrapping_add is the default if you use the |
No, this is highly unusual in Rust. I think we should do this for select cases where user-provided numbers come into play. |
Do we rely on human code review to catch arithmetic that could cause an unchecked overflow? It seems that the Rust style is generally to enforce correctness via static analysis of code. I would expect that there would be, e.g., a Clippy warning that we could enable that forbids the use of an unchecked |
Typically debug assertions in tests catch these. Fuzzing helps.
It tends to be that most cases of doing addition are going to be okay, so the tradeoff is between a cognitive overload with low marginal payoff. Nor does this have the complexity-compounding issues that memory safety has. |
Using rust-lang/rust#9469 (From 2013) brings up the difference in generated code when using |
Additional data on the code size of checked_add: I wrote the following functions: #[no_mangle]
pub fn checked_add(a: i32) -> i32 {
match a.checked_add(100) {
Some(v) => v,
None => 0,
}
}
#[no_mangle]
pub fn saturating_add(a: i32) -> i32 {
a.saturating_add(100)
}
#[no_mangle]
pub fn wrapping_add(a: i32) -> i32 {
a.wrapping_add(100)
}
#[no_mangle]
pub fn default_add(a: i32) -> i32 {
a + 100
} Here is the WASM output:
Bytes of WASM:
So, the safe methods are bigger, but not as big as they were in 2013. To reproduce, see sffc/rust-wasm-i18n@f31c30f |
My attempt at decompiling checked_add by hand: pub fn checked_add(a: i32) -> i32 {
let b = a + 100;
let c1 = (a > -1);
let c2 = (b > -1);
if c1 && c1 != c2 {
// overflow
return 0;
} else {
// no overflow
return b;
}
} I've convinced myself that this is correct as long as I am adding a positive constant. If you don't tell the compiler that the thing you are adding is guaranteed to be positive, the code gets a bit longer, from 33 bytes to 44 bytes:
In any case, I think this is a good enough reason to not recommend using checked_add everywhere. |
We should require checked math for the functions that take input size and compute the worst-case output size. While safe-only Rust code would catch the issue of a too short buffer (due to overflow) at the time of trying to index into the buffer, when the application code is C or C++, an unexpectedly short buffer could be a problem for operations performed on the application side. |
ICU has had numerous bugs involving integer overflow wrapping around and causing crashes when indexing into arrays, etc. Rust integer types have functions like
checked_add
that allow you to perform failure logic if an integer overflow would occur. I was wondering whether we should adopt a best practice to avoid "raw" integer arithmetic and always use the "checked" versions.Should we make an exception for
+
on twousize
s, especially if one of theusize
s is fixed (likei += 1
)?The text was updated successfully, but these errors were encountered: