Rust Casting
A cast is a written change from one primitive type to another. Rust never changes a primitive type quietly.
Cast with as
Write the value, the word as, and the type you want. A float cast to a whole number drops its fraction:
println!("{}", 65.4321_f32 as u8);A u8 becomes a char
Only a u8 casts straight to a char. 65 is the code for the letter A. Any other number type must become a u8 first:
let letter = 65u8 as char;
println!("{}", letter);Narrowing keeps the low bits
Cast to a smaller type and only the low bits survive. Cast between signed and unsigned, and the same bits are simply read the other way:
let big: u16 = 1000;
println!("{} {} {}", big as u8, (-1i8) as u8, 128u8 as i8);Float casts stop at the edge
A float too big for the target becomes its largest value instead. One too small becomes its smallest, and NaN becomes 0:
println!("{} {} {}", 300.0_f32 as u8, -100.0_f32 as u8, f32::NAN as u8);A whole number can lose value in a float
An f32 keeps about seven decimal digits exactly. Past 2^24 — 16,777,216 — whole numbers start landing on neighbours:
let exact = 16_777_217i32;
let as_float = exact as f32;
println!("stored = {}", as_float);
println!("back = {}", as_float as i32);f64 has a limit too
An f64 holds whole numbers exactly up to 2^53. One past it rounds down to the nearest storable value:
let big: u64 = 9_007_199_254_740_993;
println!("as f64 = {}", big as f64);Example
fn main() {
let decimal = 65.4321_f32;
let integer = decimal as u8;
let character = integer as char;
println!("Casting: {} -> {} -> {}", decimal, integer, character);
let thousand: u16 = 1000;
println!("1000 as u8 is {}", thousand as u8);
println!("-1 as u8 is {}", (-1i8) as u8);
println!("128 as i8 is {}", 128u8 as i8);
println!("300.0 as u8 is {}", 300.0_f32 as u8);
println!("-100.0 as u8 is {}", -100.0_f32 as u8);
println!("nan as u8 is {}", f32::NAN as u8);
}Key points
- There is no silent conversion: saving an f32 in a u8 variable without a cast fails with error E0308
- Only a u8 can be cast straight to a char.
- A float as char fails with error E0604.
- Cast to u8 first, then to char.
- A cast on a literal that plainly does not fit is refused while compiling.
- One example is 1000 as u8 written directly.
- Cast a variable instead, and the low-bits rule applies.
- Use as only between primitive types like numbers and char.
- It cannot turn a number into your own struct.
- For that, the type implements the From trait.
- Rust does convert a few things for you.
- The safe list is short.
- A &mut T is accepted where &T is expected.
- A &String fits where &str is expected.
- That fit is deref coercion.
- An array can also be used where a slice is expected.
In this example, decimal is 65.4321. Casting it to u8 drops the fraction, giving 65. Casting 65 to char gives the character A. 65 is the code for A. The next lines show the range rules. 1000 is held in a u16 and cast to u8. The cast keeps only the low bits, giving 232. -1 cast to u8 is 255, and 128 cast to i8 is -128 — the same bits, read the other way. The last three lines show float casts stopping at the edge. 300.0 becomes the u8 maximum 255. -100.0 becomes 0. NaN becomes 0.
Note
In C, some of these casts have undefined behaviour.
The program may do anything at all.
No error is promised.
In Rust every integer cast has one defined answer, so the same code gives the same result on every computer.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
What is 1000 as u8, when the 1000 is held in a u16 variable?