Question

How to Print the Type of a Variable in Rust

rusttypes

Question

I have the following Rust code:

let mut my_number = 32.90;

How can I print the type of my_number?

I tried using type and type_of, but those did not work. Is there another way to display the variable's type in Rust?

Short Answer

By the end of this page, you will understand how Rust determines a variable's type, why Rust does not have a simple built-in type() function like some other languages, and how to inspect a type using std::any::type_name. You will also learn what type Rust infers for numeric literals such as 32.90, how type inference works, and common mistakes beginners make when trying to print a type.

Concept

Rust is a statically typed language. That means every value has a type known at compile time, even if you do not write the type explicitly.

For example:

let mut my_number = 32.90;

Rust infers the type of my_number from the value 32.90. A floating-point literal like 32.90 is inferred as f64 by default unless you specify otherwise.

So this is effectively treated like:

let mut my_number: f64 = 32.90;

Unlike dynamic languages, Rust does not usually encourage "printing a type" during normal program logic, because types are mainly a compile-time concept. However, for debugging or learning, you can inspect a type using:

std::any::type_name::<T>()

or by wrapping it in a helper function that accepts a reference to a value.

This matters because understanding type inference helps you:

  • read Rust code more confidently
  • avoid mismatched-type errors
  • choose the correct numeric type like i32, f32, or f64
  • debug generic code and learn what the compiler inferred

Mental Model

Think of a Rust variable like a labeled storage box in a warehouse.

  • The value is what is inside the box.
  • The type is the label on the outside of the box.
  • Rust wants that label to be clear before the program runs.

When you write:

let my_number = 32.90;

Rust looks at the value and decides which label fits best. For a decimal number, the default label is usually f64.

Printing the type is like asking, "What label did Rust put on this box?" Rust does not have a casual built-in type() function for this, but it does give you tools to inspect the label when needed.

Take Quiz

Syntax and Examples

The most common way to print a variable's type in Rust is to use std::any::type_name with a helper function.

use std::any::type_name;

fn print_type_of<T>(_: &T) {
    println!("{}", type_name::<T>());
}

fn main() {
    let mut my_number = 32.90;
    print_type_of(&my_number);
}

Output:

f64

How it works

  • print_type_of<T> is a generic function.
  • T becomes the actual type of the value you pass in.
  • type_name::<T>() returns that type as a string.
  • &my_number passes a reference, which avoids moving the value.

Explicit type example

use std::any::type_name;

fn print_type_of<T>(_: &T) {
    println!("{}", type_name::<T>());
}

fn main() {
    let a = 32.90;
    let b: f32 = 32.90;
    let c: i32 = 10;

    print_type_of(&a);
    print_type_of(&b);
    print_type_of(&c);
}

Possible output:

f64
f32
i32

Short version for one known type

If you already know the type parameter at compile time, you can write:

use std::any::type_name;

fn main() {
    println!("{}", type_name::<f64>());
}

But this does not inspect a variable directly. It prints the name of the type you manually provide.

Step by Step Execution

Consider this example:

use std::any::type_name;

fn print_type_of<T>(_: &T) {
    println!("{}", type_name::<T>());
}

fn main() {
    let my_number = 32.90;
    print_type_of(&my_number);
}

Step-by-step

  1. Rust sees let my_number = 32.90;
  2. The value 32.90 is a floating-point literal.
  3. Because no other type information is given, Rust chooses the default floating-point type: f64.
  4. print_type_of(&my_number) is called.
  5. Rust infers that T in print_type_of<T> must be f64.
  6. Inside the function, type_name::<T>() becomes type_name::<f64>().
  7. println! prints f64.

Trace with another example

use std::any::type_name;

fn print_type_of<T>(_: &T) {
    println!("{}", type_name::<T>());
}

fn main() {
    let x = 5;
    let y = 5.0;
    let z = "hello";

    print_type_of(&x);
    print_type_of(&y);
    print_type_of(&z);
}

Likely output:

i32
f64
&str

Rust infers:

  • 5 as i32 by default
  • 5.0 as f64 by default
  • "hello" as &str

Real World Use Cases

Even though printing a type is mostly for debugging or learning, type inspection is useful in several practical situations.

Debugging generic code

When writing generic functions or reusable utilities, you may want to confirm what concrete type Rust inferred.

use std::any::type_name;

fn debug_type<T>(_: &T) {
    println!("Type: {}", type_name::<T>());
}

Learning type inference

Beginners often wonder what type Rust chooses for:

  • integer literals
  • float literals
  • string literals
  • references
  • collections

Printing types helps verify your understanding.

Troubleshooting compiler errors

If Rust reports a mismatch between expected and actual types, inspecting surrounding values can help you understand the issue.

Working with numeric code

In math, finance, graphics, or APIs, it matters whether a number is:

  • i32
  • u64
  • f32
  • f64

Knowing the inferred type helps avoid bugs and conversion issues.

Take Quiz

Real Codebase Usage

In real Rust projects, developers usually do not print types in production code. Instead, they use type inspection as a temporary debugging or teaching tool.

Common patterns include:

1. Making types explicit when clarity matters

let price: f64 = 32.90;
let count: u32 = 5;

This is often better than printing the type later.

2. Using helper functions during debugging

use std::any::type_name;

fn type_of<T>(_: &T) -> &'static str {
    type_name::<T>()
}

This can be used in logs or tests.

3. Adding explicit conversions

If a function expects a specific type, developers convert values instead of relying only on inference.

let value = 10;
let as_float = value as f64;

4. Guarding against wrong assumptions

A developer may assume a number is f32, but Rust may infer f64. In real codebases, this is usually solved by annotating the type:

let ratio: f32 = 0.5;

5. Validation and error handling around parsing

When reading input from files, APIs, or users, the type is often determined by parsing, not just literals:

let input = "42";
let number: i32 = input.parse().expect("Invalid number");

In these cases, understanding the target type is more important than printing it.

Common Mistakes

Mistake 1: Expecting a built-in type() function

Rust does not have a Python-style type(variable) function.

Broken idea:

let my_number = 32.90;
println!("{}", type(my_number));

Why it fails:

  • type is not a built-in Rust function for runtime inspection.

Use this instead:

use std::any::type_name;

fn print_type_of<T>(_: &T) {
    println!("{}", type_name::<T>());
}

Mistake 2: Forgetting that float literals default to f64

let x = 32.90;

Many beginners assume this is f32, but it is f64 by default.

To make it f32:

let x: f32 = 32.90;

or:

let x = 32.90_f32;

Mistake 3: Passing ownership when it is not needed

This is not always wrong, but passing a reference is safer for inspection helpers.

Less ideal:

fn print_type_of<T>(_: T) {
    println!("{}", std::any::type_name::<T>());
}

Better:

fn print_type_of<T>(_: &T) {
    println!("{}", std::any::type_name::<T>());
}

Passing &T avoids moving the value.

Mistake 4: Confusing value printing with type printing

let my_number = 32.90;
println!("{}", my_number);

This prints the value, not the type.

Mistake 5: Assuming type_name is for core program logic

type_name is mainly for debugging, diagnostics, and learning. In normal Rust code, you usually solve type problems by:

  • adding type annotations
  • reading compiler errors
  • using explicit conversions

Comparisons

ApproachWhat it doesBest useExample
Type inferenceLets Rust choose the typeClean, simple codelet x = 32.90;
Type annotationYou specify the type directlyClarity and controllet x: f32 = 32.90;
Type suffixYou attach the type to the literalShort numeric declarationslet x = 32.90_f32;
std::any::type_namePrints or returns a type nameDebugging and learningtype_name::<T>()

Inference vs explicit annotation

let a = 32.90;      // inferred as f64
let b: f32 = 32.90; // explicitly f32

f32 vs f64

TypeSizeCommon use
f3232-bit floatGraphics, memory-sensitive cases
f6464-bit floatDefault floating-point type in Rust

Debugging type vs declaring type

  • Use type_name when you want to inspect what Rust chose.
  • Use annotations when you want to control what Rust should use.
Take Quiz

Cheat Sheet

// Default float type
let x = 32.90; // f64

// Explicit type annotation
let x: f32 = 32.90;

// Literal suffix
let x = 32.90_f32;
let y = 10_i64;

// Print a variable's type
use std::any::type_name;

fn print_type_of<T>(_: &T) {
    println!("{}", type_name::<T>());
}

// Use it
let value = 32.90;
print_type_of(&value); // prints: f64

Rules to remember

  • Rust is statically typed.
  • Types are usually known at compile time.
  • Rust uses type inference when possible.
  • 32.90 defaults to f64.
  • 5 defaults to i32.
  • Use std::any::type_name::<T>() to inspect a type.
  • Prefer type annotations when you need clarity.

Common defaults

LiteralDefault type
5i32
5.0f64
truebool
"hi"&str

FAQ

How do I print the type of a variable in Rust?

Use std::any::type_name inside a generic helper function:

use std::any::type_name;

fn print_type_of<T>(_: &T) {
    println!("{}", type_name::<T>());
}

What type is 32.90 in Rust?

By default, 32.90 is inferred as f64.

Does Rust have a built-in type() function?

No. Rust does not provide a simple built-in type(variable) function like some dynamic languages.

Can I get a variable's type at runtime in Rust?

You can inspect a type name for debugging using std::any::type_name, but Rust's type system is mainly compile-time focused.

How do I make a float f32 instead of f64?

Use a type annotation or suffix:

let x: f32 = 32.90;
let y = 32.90_f32;

Why does Rust infer f64 for decimals?

f64 is Rust's default floating-point type because it provides higher precision and is the standard default.

Should I use type_name in production code?

Usually only for debugging, diagnostics, or learning. In most real code, explicit annotations and compiler messages are enough.

Take Quiz

Mini Project

Description

Build a small Rust program that prints both values and their inferred types. This helps you practice Rust type inference and learn how different literals are interpreted by the compiler.

Goal

Create a program that displays several variables and the type Rust assigns to each one.

Requirements

  1. Create at least four variables with different kinds of values.
  2. Include at least one integer, one floating-point number, one string slice, and one boolean.
  3. Write a reusable helper function to print a variable's type.
  4. Print both the variable's value and its type.
Take Quiz

Keep learning

Related questions

View more