Thoughts on Integers (2023)
4 days ago
- Many popular languages (C, C#, Go, Swift) default to a signed `int` type, biasing programmers away from considering unsigned types.
- Rust treats all integer types equally with explicit sizes (e.g., `i32`, `u64`) and no default `int`, forcing deliberate type selection.
- Rust uses `usize` for sizes and indices, preventing negative array indexes and enabling large arrays, but defaults integer literals to `i32`.
- Signed integer overflow is undefined behavior in C, allowing compiler optimizations like eliding overflow checks, but unsigned overflow wraps.
- Chandler Carruth's example shows that making indices signed can improve performance by avoiding extra wrapping instructions on 64-bit architectures.
- Arguments for signed indices include: easier loop construction (e.g., counting down to zero), easier detection of invalid subtraction (underflow gives negative), and simpler bounds checks.
- Unsigned underflow can silently produce large numbers, making bugs harder to catch; signed integers with asserts provide a safer middle ground.
- On 64-bit platforms, using signed `isize`/`ptrdiff_t` for indices loses only one bit of range, which is negligible in practice.
- The author concludes that signed integers with wrapping overflow and liberal asserts are a practical compromise between performance and safety.
- Languages like Odin use address-sized `int` for indices and sizes, avoiding performance pitfalls of mixing 32-bit integers with pointers.