Integers and Numeric Operations
This chapter defines integer literal typing, explicit integer conversion, fixed-width arithmetic, and arithmetic traps.
Integer Literals
Integer literal rules:
- Accepted integer literal syntax is defined in Lexical Grammar.
- An unsigned N-bit type has the inclusive range
0through2^N - 1. A signed N-bit type has the inclusive range-2^(N - 1)through2^(N - 1) - 1and uses two's-complement representation. On the current target,usizeisu64-width andisizeisi64-width. - Integer literals start as untyped integer literals.
- If an integer literal has an expected integer type, it takes that type when the value fits.
- When unary
-applies directly to an integer literal, with any number of grouping parentheses between them, range checking uses the combined negative mathematical value. For an expected signed N-bit type, the positive magnitude may therefore be at most2^(N - 1), allowing the exact minimum value. An expected unsigned type is invalid because unary-requires a signed operand. - Without an expected type, a unary-negative integer literal is checked as
i32, including the-2^31minimum case. A larger magnitude does not cause inference of a wider type. - The signed-minimum case becomes one checked integer constant. It does not first construct an out-of-range positive value and does not execute a runtime negation overflow.
- If no context fixes the type, the literal becomes
i32. - Assigning an out-of-range literal is a type error.
- Non-literal integer values are not implicitly converted between integer types.
- Float literals are not supported.
Examples:
let a = 10 // i32
let b: u64 = 10 // u64
let c: u8 = 300 // error: literal out of range
let d = 0xFF_FF // i32
let e: u8 = 0b1010
let minimum: i8 = -128
let too_small: i8 = -129 // error
let x: i32 = 10
let y: u64 = x // error: no implicit integer conversion
Numeric Operations and Conversions
Numeric operations do not perform implicit integer conversion.
Rules:
- Integer binary arithmetic uses operands of the same integer type.
- Shift operators use operands of the same integer type. Integer literals on the right side may be contextually typed by the left operand type when the value fits.
- Shift expressions return the left operand type.
- Left shift moves the fixed-width bit pattern toward the most-significant end, discards bits that leave the width, and fills low bits with zero. Discarded bits do not cause an arithmetic overflow trap.
- Unsigned right shift fills high bits with zero. Signed right shift uses two's-complement arithmetic shift and fills high bits with the original sign bit.
- A zero shift count leaves the value unchanged. A negative count or a count greater than or equal to the left operand's bit width traps before any machine shift is executed.
- Signed division truncates the mathematical quotient toward zero. Signed remainder satisfies
a = (a / b) * b + (a % b), has absolute value less than the absolute value ofb, and is zero or has the same sign as dividenda. Unsigned division and remainder use the ordinary non-negative quotient and remainder. - For a signed type, both
minimum / -1andminimum % -1trap as division-family overflow even though the mathematical remainder would be zero. - Integer literals may take an expected integer type when the value fits.
- Non-literal integer values are not implicitly converted.
booldoes not implicitly convert to or from integer types.- Explicit integer conversion uses
expr as Type. - Integer
asis allowed only for lossless conversions. - Narrowing integer conversions are not allowed with
as. - Signedness-changing integer conversions are not allowed with
asunless the target type can represent every value of the source type. - On the ARM64 macOS target,
usizehas the same range asu64, andisizehas the same range asi64for conversion checking.
The same as expression can explicitly select a one-step type-owned borrow coercion when the source is already borrowed. That distinct contract is specified in Borrow Coercions; it does not relax the numeric rules above.
Examples:
let a: u32 = 10
let b: u64 = 20
let c = a + b // error
let d = (a as u64) + b // OK
let x: u32 = 10
let y: u64 = x as u64 // OK: lossless widening
let signed: i32 = 10
let unsigned = signed as u64 // error: not lossless for all i32 values
let big: u64 = 300
let small = big as u8 // error: narrowing
let checked = u8.checked(big) // u8?
let truncated = u8.truncate(big) // u8
checked and truncate are explicit numeric conversion APIs declared as construction functions by the active standard-library package. Their names have no special grammar meaning:
construct u8 {
pub noalloc func checked(value: u64): Self?
pub noalloc func truncate(value: u64): Self
}
Explicit u64 bit-mixing APIs are ordinary methods:
let sum = left.wrapping_add(right)
let product = left.wrapping_mul(right)
let mixed = left.bit_xor(right)
let rotated = left.rotate_right(amount)
wrapping_add and wrapping_mul compute modulo 2^64. rotate_right uses the low six bits of amount, so every u64 amount is valid. These methods do not allocate or fail. Normal arithmetic operators retain the trapping rules below; the mixing methods do not introduce new operator spellings.
Arithmetic trap rules:
- Overflow in normal integer arithmetic traps.
- Wrapping arithmetic must use an explicit wrapping API.
- Division by zero traps.
- Remainder by zero traps.
- Signed division-family overflow traps for both
/and%when the operands are the minimum signed value and-1. - Shift counts greater than or equal to the bit width of the shifted value trap.
- Shift counts must be non-negative.
- Left-shift bit loss is not integer overflow. It follows the fixed-width bit-shift rule and does not trap.
Trap semantics are specified in Control Flow. These arithmetic safety checks are always-on for every build mode; see Safety Checks and Build Modes.
Other fixed-width numeric mixing APIs are not part of the current standard surface.