into()
in RustPublished on March 23, 2025
Rust has several features that may seem unusual at first but serve an important purpose. One such feature is the into()
method.
into()
Do?into()
is a method from the Into<T>
trait that converts an object into a different type. Since Rust supports method overloading based on return type, the target conversion type is inferred from context.
For example:
// Conversion from int to float
let converted_value: f64 = 42.into();
According to the Rust documentation, into()
performs a value-to-value conversion and is conceptually the opposite of From
.
From<T>
vs Into<U>
From<T>
Automatically Provides Into<U>
?When implementing From<T>
for U
:
// Implementing From<T> for type U
trait From<T> {
fn from(value: T) -> Self;
}
This provides a way to convert T
into U
. Rust then automatically provides an implementation of Into<U>
for T
:
// Into<T> for type U
trait Into<T> {
fn into(self) -> T;
}
Rust offers a blanket implementation of Into
in terms of From
, meaning Into
is always available when From
is implemented:
impl<T, U> Into<U> for T
where
U: From<T>,
{
fn into(self) -> U {
U::from(self)
}
}
Compiler Explorer link: here
struct Point {
x: i32,
y: i32,
}
impl From<i32> for Point {
fn from(value: i32) -> Self {
Point { x: value, y: value }
}
}
fn main() {
let num: i32 = 7;
let p: Point = Point::from(num);
let p2: Point = num.into(); // Automatically available
println!("p = ({}, {})", p.x, p.y);
println!("p2 = ({}, {})", p2.x, p2.y);
}
From<T>
and Into<U>
From<T>
is explicitly implemented and defines how to convert T
into a target type U
.Into<U>
is automatically available when From<T>
is implemented for U
, allowing the use of .into()
..into()
Over From::from()
When writing generic code, the exact target type may not be known in advance. .into()
offers more flexibility.
fn convert_to_f64<T: Into<f64>>(value: T) -> f64 {
value.into() // Works with any type that implements Into<f64>
}
From::from()
fn convert_to_f64<T>(value: T) -> f64 {
f64::from(value) // Error: Rust cannot guarantee that f64::from(T) exists for all types
}
If the context already defines the target type, .into()
keeps the code concise:
let num: i32 = 42;
let float_num: f64 = num.into(); // Rust infers the target type as f64
// Also works, but longer
let float_num2: f64 = f64::from(num);
However, if the target type is not known, .into()
will not work:
let float_num = num.into(); // Error: target type cannot be inferred
When multiple types can be converted to a target type, .into()
allows for a wider range of inputs.
struct Distance(f64);
impl From<i32> for Distance {
fn from(value: i32) -> Self {
Distance(value as f64)
}
}
impl From<f32> for Distance {
fn from(value: f32) -> Self {
Distance(value as f64)
}
}
fn print_distance<T: Into<Distance>>(value: T) {
let d: Distance = value.into();
println!("Distance: {}", d.0);
}
fn main() {
print_distance(10); // Ok
print_distance(5.5_f32); // Ok
}
.into()
You should not use .into()
if you don’t need ownership. Instead, consider:
&T
)AsRef<T>
(for read-only borrowed conversions).clone()
or .to_owned()
(to create a copy)Copy
trait (for lightweight types)Additionally, avoid .into()
when the target type is unclear. In such cases, explicit conversion using From::from()
or as
is preferable.
into()
Since Into<T>
is a value-to-value conversion, the value is consumed. However, for primitive types implementing Copy
, the value is not moved but copied:
let v_a: i32 = 43;
let v_b: f32 = v_a.into();
println!("v_a = {}", v_a); // Still valid
println!("v_b = {}", v_b);
For non-Copy
types like String
, the value is moved:
let v_a = String::from("hello");
let v_b: String = v_a.into();
// println!("v_a = {}", v_a); // Error: value has been moved
println!("v_b = {}", v_b);
.into()
in StructsA common pattern is accepting impl Into<String>
parameters in constructors:
struct User {
name: String,
surname: String,
}
impl User {
fn new(name: impl Into<String>, surname: impl Into<String>) -> Self {
User { name: name.into(), surname: surname.into() }
}
}
fn main() {
// Conversion from `&str` to `String`
let user1 = User::new("Albert", "Einstein");
// Directly passing a `String`: no conversion
let user2 = User::new(String::from("Satoshi"), String::from("Yamamoto"));
}
From<T>
explicitly defines how to convert T
into U
.Into<U>
is automatically available when From<T>
is implemented..into()
when writing generic code, when the target type can be inferred, or to allow multiple input types..into()
if ownership is not needed or when the target type is unclear.Rust Documentation
Rust's standard library documentation on the Into
trait.
Link: Rust Documentation - Into Trait
The Rust Programming Language - Chapter 10
The official Rust book's chapter on conversion traits like From
, Into
, AsRef
, and ToOwned
.
Link: The Rust Programming Language - Conversion Traits
Rust by Example - Conversions
Practical examples and code snippets on conversions between types in Rust.
Link: Rust by Example - Into Trait
Rust
No comments yet. Be the first to comment!