Type Casting in Rust

Type casting in Rust

In the Rust programming language, type casting is used to cast a variable from one data type to another.

Rust has strict rules for data types and the kinds of operations that can be performed on them.

The simplest and safest way to perform type casting is by using the as keyword:

let a = 5;
let b = (a as f64) / 2.0; 
println!("b: {}", b);

Standard Output:

b: 2.5

Data Types and Type Casting

There are several categories of data types, including:

The type of a variable can be defined explicitly, or we can allow Rust to implicitly choose a type based on the value that we assign to the variable.

// Implicitly define the variable type: 
let a = 6;

// Explicitly define the type:
let b:i32 = 8;

Because Rust enforces strict safety rules, it is common for the compiler to throw an error when there is a type mismatch. For example, we can’t do math with two different numerical types.

// Attempting to add an i32 and an i64 results in an error: 
let op1: i32 = 1; 
let op2: i64 = 1; 
let my_sum = op1 + op2;

Output:

Standard Error

expected `i32`, found `i64`

This is why Rust also allows type casting, which enables a variable to be cast from one type to another.

How Type Casting Works in Rust

Rust has strict rules that govern type casting.

Allowed Type Casting

  • Integer to integer
  • Integer to float
  • Float to Integer
  • Integer to char
  • Char to integer
  • Bool to integer
  • Integer to string

Disallowed Type Casting

  • Character to string
  • String to character
  • String to integer
  • String to float
  • Character to float

Using as Keyword to Safely Type Cast

The as keyword is designed only to type cast safely. This means that it will strictly enforce safety rules regarding type casting.

The syntax of the as keyword required an operand as well as a data type:

<operand> as <type>

For example:

my_var as i32

Will cast the variable my_var as a 32-bit integer type.

The following example shows how type casting can be used to enable division between an integer and a float:

let a = 5;
let b = (a as f64) / 2.0; 

In this case, we are type casting the variable a as a 64 bit floating point number because we want to divide it by 2.0 and get a fractional value. If we didn’t type cast a, the compiler would throw an error because a was initially (implicitly) declared as an integer type.

Type Casting Integer to Integer

We can use the as keyword to cast an integer type to another integer type. This generally works without issues, as long as the type we are casting to is large enough to hold the value, and we aren’t casting a signed type with a negative value to an unsigned type.

The following code shows a few examples of how to cast between integer types:

fn main() {
    let a: u32 = 42;
    let b: i32 = -42;
    println!("{}", a as u64); // Unsigned to unsigned: works well
    println!("{}", b as i32); // Signed to signed: works well
    println!("{}", a as i32); // Unsigned to signed: works well
    println!("{}", b as u32); // Signed negative doesn't cast properly to unsigned
}

Standard Output:

42
-42
42
4294967254

Type Casting Between Integers and Floats

Type casting between integers and floats and between float types generally works well.

However when we cast from a float to an integer, the decimal is removed. And when we cast between float types, there will be rounding errors:

fn main() {
    let a: i32 = -42;
    let b: f32 = 42.2;
    println!("{}", a as f32); // Int to float: works well
    println!("{}", b as i32); // Float to int: removes decimal
    println!("{}", b as f64); // Float to float: works but rounding errors
}

Standard Output:

-42
42
42.20000076293945

Type Casting Between Integers and Characters

Rust allows type casting between integers and characters, but it will perform a Unicode conversion as it does so.

Additionally, only a u8 can be cast as a char.

For the examples below, it is helpful to know that the Unicode value of uppercase ‘A’ is 65 and ‘B’ is 66:

fn main() {
    let a: u8 = 66;
    let b: char = 'A';
    println!("{}", a as char); // Converts int '66' to char 'B' (Unicode) 
    println!("{}", b as i16); // Converts char 'A' to int 65 (Unicode)
}

Standard Output

B
65

Casting a Boolean to an Integer

In Rust, a Boolean can be cast as an integer, but an integer cannot be cast as a bool.

When a Boolean is cast as an integer, a value of true will be cast to an integer value of ‘1’, and a value of false will be cast to an integer value of ‘0’:

true => 1
false => 0

The following example shows how this looks in code:

fn main() {
    let a: bool = true;
    let b: bool = false;
    println!("{}", a as i32);
    println!("{}", b as u8); 
}

Standard Output:

1
0

Type Casting To String

In general, primitive types like numbers, characters, and Booleans can be cast to strings. Casting from string to any of these types, however, is not generally an easy task.

To cast from a primitive type to a string, we can use the .to_string() method:

fn main() {
    let a: u8 = 42;
    let b: bool = true;
    let c: char = 'A';
    println!("{}", a.to_string());
    println!("{}", b.to_string());
    println!("{}", c.to_string());
}

Standard Output:

42
true
A

Type Casting With transmute

Rust uses the transmute keyword, which allows for unsafe type casting.

The transmute keyword tells Rust to ignore safety rules and simply treat a variable as if it is a different data type. In other words, transmute functions at the bit level; the data is kept intact, but is interpreted as though it is of a different type.

The only requirement is that both types must have the same size.

This can enable successful compilation in situations where the as keyword won’t work.

You can learn more about transmute in the official documentation.