Type Conversion in Rust
Type conversion is the process of converting a value from one type
to
another. Rust provides several
ways to perform type conversions, ranging from simple casting with the
as keyword to safe,
trait-based conversions. In this lesson, you'll learn when and how to use each
method.
The as Keyword
The as keyword is used for primitive type conversions. It's simple
but
can be unsafe:
Click Run to execute your code
as keyword can truncate values
without
warning!
Converting a large number to a smaller type will silently lose data.
Common Uses of as
| Conversion | Example | Notes |
|---|---|---|
| Integer to Integer | let y: i64 = x as i64 |
Can truncate if target is smaller |
| Float to Integer | let i: i32 = f as i32 |
Truncates decimal part |
| Integer to Float | let f: f64 = i as f64 |
Safe, no data loss |
| Char to Integer | let n: u32 = c as u32 |
Gets Unicode code point |
| Boolean to Integer | let n: i32 = b as i32 |
true=1, false=0 |
as only for primitive types
when
you're certain the conversion is safe.
For more complex conversions, use traits like From and
Into.
From and Into Traits
The From and Into traits provide safe, explicit type
conversions:
The From Trait
From defines how to create a type from another type:
// Converting &str to String
let my_str = "Hello";
let my_string = String::from(my_str);
// Converting i32 to i64
let small: i32 = 42;
let large: i64 = i64::from(small);
The Into Trait
Into is the reciprocal of From. If a type implements
From, it automatically gets Into:
let num: i32 = 42;
let num_i64: i64 = num.into(); // Calls i64::from(num)
let text: String = "Hello".into(); // Calls String::from("Hello")
From::from(value)- Explicit about the target typevalue.into()- Target type inferred from context
Click Run to execute your code
Parsing Strings to Numbers
The parse method converts strings to numbers. It returns a
Result because parsing can fail:
// Using parse with type annotation
let number_str = "42";
let number: i32 = number_str.parse().expect("Not a number!");
// Using turbofish syntax
let float_num = "3.14".parse::().expect("Not a float!");
// Handling errors with match
let input = "not a number";
match input.parse::() {
Ok(n) => println!("Parsed: {}", n),
Err(e) => println!("Parse error: {}", e),
}
::<Type> syntax
(called
"turbofish") explicitly
specifies the type parameter when it can't be inferred.
TryFrom and TryInto Traits
For conversions that can fail, use TryFrom and TryInto.
They return a Result:
use std::convert::TryFrom;
use std::convert::TryInto;
// TryFrom - safe conversion that can fail
let big_num: i64 = 1000;
let small_num: Result = i8::try_from(big_num);
match small_num {
Ok(n) => println!("Converted: {}", n),
Err(e) => println!("Conversion failed: {}", e),
}
// TryInto - the reverse
let value: i32 = 100;
let result: Result = value.try_into();
match result {
Ok(n) => println!("Success: {}", n),
Err(e) => println!("Failed: {}", e),
}
| Trait | Use Case | Returns |
|---|---|---|
From |
Infallible conversions | Target type |
Into |
Infallible (reciprocal of From) | Target type |
TryFrom |
Fallible conversions | Result<T, E> |
TryInto |
Fallible (reciprocal of TryFrom) | Result<T, E> |
- Use
asfor simple primitive conversions where you're certain it's safe - Use
From/Intofor infallible conversions - Use
TryFrom/TryIntowhen conversion might fail - Use
parsefor string-to-number conversions
Common Type Conversions
String Conversions
// &str to String
let s1 = String::from("Hello");
let s2 = "World".to_string();
// String to &str
let owned = String::from("Hello");
let borrowed: &str = &owned;
// Number to String
let num = 42;
let num_str = num.to_string();
let formatted = format!("{}", num);
Number Conversions
// Safe widening (smaller to larger)
let small: i32 = 42;
let large: i64 = small.into();
// Unsafe narrowing (use TryFrom)
let big: i64 = 1000;
let small: i8 = i8::try_from(big).unwrap_or(-1);
// Float to int (truncates)
let pi: f64 = 3.14;
let pi_int: i32 = pi as i32; // 3
Common Mistakes
1. Silent truncation with as
Problem:
let big: u32 = 1000;
let small: u8 = big as u8; // Silently becomes 232 (1000 % 256)
Solution:
let big: u32 = 1000;
let small: u8 = u8::try_from(big).unwrap_or(255); // Handle overflow
2. Not handling parse errors
Wrong:
let num: i32 = "not a number".parse().unwrap(); // Panics!
Correct:
let num: i32 = "not a number".parse().unwrap_or(0); // Default value
// Or use match to handle error properly
3. Confusing From and Into
Problem:
let s: String = "Hello".from(); // Error: from is not a method!
Correct:
let s = String::from("Hello"); // From is a trait method
let s: String = "Hello".into(); // Into is a method (needs type annotation)
Exercise: Type Conversion Practice
Task: Fix the code to perform type conversions correctly.
Requirements:
- Parse strings to numbers safely
- Convert between numeric types
- Handle conversion errors properly
- Use appropriate conversion methods
Click Run to execute your code
Show Solution
use std::convert::TryInto;
fn main() {
// Parse string to integer
let age_str = "25";
let age: i32 = age_str.parse().expect("Failed to parse age");
println!("Age: {}", age);
// Convert float to integer
let price: f64 = 19.99;
let price_int = price as i32;
println!("Price (integer): {}", price_int);
// Safe conversion with error handling
let big_number: i64 = 1000;
let small_number: Result = big_number.try_into();
match small_number {
Ok(n) => println!("Converted: {}", n),
Err(_) => println!("Number too large for i8"),
}
// Parse with error handling
let input = "not a number";
match input.parse::() {
Ok(n) => println!("Parsed: {}", n),
Err(e) => println!("Parse error: {}", e),
}
// Char to Unicode
let letter = 'Z';
let unicode_value = letter as u32;
println!("Unicode value of '{}': {}", letter, unicode_value);
// &str to String
let str_slice = "Hello, Rust!";
let owned_string = String::from(str_slice);
println!("String: {}", owned_string);
// Boolean to integer
let is_active = true;
let status_code = is_active as i32;
println!("Status code: {}", status_code);
}
Summary
askeyword: Simple but unsafe primitive conversions, can truncateFromtrait: Infallible conversions, explicit target typeIntotrait: Reciprocal of From, target type inferredparsemethod: Convert strings to numbers, returns ResultTryFrom/TryInto: Safe fallible conversions, return Result- Always handle errors when parsing or converting between incompatible sizes
- Use
unwrap_orormatchto handle conversion failures - Prefer trait-based conversions over
asfor safety
What's Next?
Now that you understand type conversion, you're ready to learn about functions. In the next lesson, you'll learn how to define functions, pass parameters, return values, and understand the crucial difference between statements and expressions in Rust.
Enjoying these tutorials?