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, orf64 - 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.
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.Tbecomes the actual type of the value you pass in.type_name::<T>()returns that type as a string.&my_numberpasses 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
- Rust sees
let my_number = 32.90; - The value
32.90is a floating-point literal. - Because no other type information is given, Rust chooses the default floating-point type:
f64. print_type_of(&my_number)is called.- Rust infers that
Tinprint_type_of<T>must bef64. - Inside the function,
type_name::<T>()becomestype_name::<f64>(). println!printsf64.
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:
5asi32by default5.0asf64by 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:
i32u64f32f64
Knowing the inferred type helps avoid bugs and conversion issues.
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:
typeis 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
| Approach | What it does | Best use | Example |
|---|---|---|---|
| Type inference | Lets Rust choose the type | Clean, simple code | let x = 32.90; |
| Type annotation | You specify the type directly | Clarity and control | let x: f32 = 32.90; |
| Type suffix | You attach the type to the literal | Short numeric declarations | let x = 32.90_f32; |
std::any::type_name | Prints or returns a type name | Debugging and learning | type_name::<T>() |
Inference vs explicit annotation
let a = 32.90; // inferred as f64
let b: f32 = 32.90; // explicitly f32
f32 vs f64
| Type | Size | Common use |
|---|---|---|
f32 | 32-bit float | Graphics, memory-sensitive cases |
f64 | 64-bit float | Default floating-point type in Rust |
Debugging type vs declaring type
- Use
type_namewhen you want to inspect what Rust chose. - Use annotations when you want to control what Rust should use.
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.90defaults tof64.5defaults toi32.- Use
std::any::type_name::<T>()to inspect a type. - Prefer type annotations when you need clarity.
Common defaults
| Literal | Default type |
|---|---|
5 | i32 |
5.0 | f64 |
true | bool |
"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.
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
- Create at least four variables with different kinds of values.
- Include at least one integer, one floating-point number, one string slice, and one boolean.
- Write a reusable helper function to print a variable's type.
- Print both the variable's value and its type.
Keep learning
Related questions
Absolute Values in Rust: Using the abs() Method
Learn how to calculate absolute values in modern Rust with abs(), handle signed integer overflow, and choose safer alternatives.
Accepting Iterators in Rust Functions with impl Iterator
Learn how to write Rust functions that accept HashMap iterators using generic bounds or impl Iterator, avoiding trait object and lifetime errors.
Access Enum Variant Values in Rust with Pattern Matching
Learn how to extract and print values stored in Rust enum variants using match and if let pattern matching, with practical examples.