16.2 Primitive Casting with as
The as
keyword provides a direct mechanism for casting between compatible primitive types. It is syntactically similar to C’s (new_type)value
but with more restrictions and different behavior in some cases (e.g., saturation on float-to-int overflow). Crucially, as
performs no runtime checks for validity beyond basic type compatibility rules enforced at compile time. Using as
signifies that the programmer assumes responsibility for the conversion’s correctness and consequences.
16.2.1 Valid as
Casts
Common uses of as
include:
- Numeric Casts: Between integer types (
i32
asu64
,u16
asu8
) and between integer and floating-point types (i32
asf64
,f32
asu8
). - Pointer Casts: Between raw pointer types (
*const T
as*mut U
,*const T
asusize
). These are primarily used withinunsafe
blocks, often for FFI or low-level memory manipulation. - Enum to Integer: Casting C-like enums (those without associated data, potentially with a
#[repr(...)]
attribute) to their underlying integer discriminant value. - Boolean to Integer:
bool
as integer type (true
becomes1
,false
becomes0
). - Character to Integer:
char
as integer type (yields the Unicode scalar value). - Function Pointers: Casting function pointers to raw pointers or integers, and vice-versa (requires
unsafe
).
16.2.2 Numeric Casting Behavior with as
Numeric casts using as
are common but require caution due to potential value changes:
- Truncation: Casting to a smaller integer type silently drops the most significant bits. (
u16
asu8
) - Sign Change: Casting between signed and unsigned integers of the same size reinterprets the bit pattern according to two’s complement representation. (
u8
asi8
) - Floating-point to Integer: The fractional part is truncated (rounded towards zero). Values exceeding the target integer’s range saturate (clamp) at the minimum or maximum value of the target type. This saturation behavior differs from C, where overflow during float-to-int conversion often results in undefined behavior.
- Integer to Floating-point: May lose precision if the integer’s magnitude is too large to be represented exactly by the floating-point type (e.g., large
i64
tof64
).
fn main() { let x: u16 = 500; // Binary 0000_0001 1111_0100 let y: u8 = x as u8; // Truncates to 1111_0100 (decimal 244) println!("u16 {} as u8 is {}", x, y); // Output: u16 500 as u8 is 244 let a: u8 = 255; // Binary 1111_1111 let b: i8 = a as i8; // Reinterpreted as two's complement: -1 println!("u8 {} as i8 is {}", a, b); // Output: u8 255 as i8 is -1 let large_float: f64 = 1e40; // Larger than i32::MAX let int_val: i32 = large_float as i32; // Saturates to i32::MAX println!("f64 {} as i32 is {}", large_float, int_val); // Output: f64 1e40 as i32 is 2147483647 let small_float: f64 = -1e40; // Smaller than i32::MIN let int_val_neg: i32 = small_float as i32; // Saturates to i32::MIN println!("f64 {} as i32 is {}", small_float, int_val_neg); // Output: f64 -1e40 as i32 is -2147483648 let precise_int: i64 = 9007199254740993; // 2^53 + 1, cannot be precisely represented by f64 let float_val: f64 = precise_int as f64; // Loses precision println!("i64 {} as f64 is {}", precise_int, float_val); // Output: i64 9007199254740993 as f64 is 9007199254740992.0 }
16.2.3 Enum and Boolean Casting
Enums without associated data can be cast to integers. Specifying #[repr(integer_type)]
ensures a predictable underlying type.
#[derive(Debug, Copy, Clone)] #[repr(u8)] // Explicitly use u8 for representation enum Status { Pending = 0, Processing = 1, Completed = 2, Failed = 3, } fn main() { let current_status = Status::Processing; let status_code = current_status as u8; println!("Status {:?} has code {}", current_status, status_code); // Output: Status Processing has code 1 let is_active = true; let active_flag = is_active as u8; // true becomes 1 println!("Boolean {} as u8 is {}", is_active, active_flag); // Output: Boolean true as u8 is 1 }
16.2.4 When to Use as
Use as
primarily when:
- Performing simple numeric conversions where truncation, saturation, or precision loss is understood and acceptable within the program’s logic.
- Conducting low-level pointer manipulations or integer-pointer conversions within
unsafe
blocks. - Converting C-like enums or booleans to their integer representations.
Warning: Avoid as
for numeric conversions where potential overflow or truncation represents an error condition that should be handled explicitly. Prefer TryFrom
/TryInto
or checked arithmetic methods in such scenarios.
16.2.5 Performance of as
Numeric casts using as
are generally highly efficient, often compiling down to a single machine instruction or even being a no-op (e.g., casting between signed and unsigned integers of the same size like u32
to i32
).