Type conversion in Rust (as, From, and Into)
Richard Oliver Bray
Posted on January 15, 2021
I'm fairly new to Rust and it's a language I've actually enjoyed learning, mainly for game development with Bevy. As someone who is coming for a JS/TS background, there is a huge learning curve and I'm eventually planning to create a video course on how to make a simple game with Rust, but for now, I'm going to document things down (in the form of dev.to articles) as and when I learn them to help people who are having difficulties.
This particular feature of Rust is something I've seen a lot in Bevy example code particularly when it comes to colours, for example:
materials.add(Color::rgb(0.7, 0.7, 0.7).into())
The .into() method is something that I've scratched my brain over for ages and I've finally been able to figure out what it does. Well, at least I think I have.
as
Simple (explicit) type conversion or casting in Rust can be done with the as keyword:
fn main() {
let num:u8 = 10;
let multiplied = times_ten(num as f32);
println!("{}", multiplied); // 100
}
fn times_ten(val: f32) -> f32 {
val * 10.0
}
From
From in Rust is one of two traits for type conversion (if you don't know what a trait I'm planning to write a post on it in the future).
Let's say I want to create my own special type called MySpecialNumber
and want to convert an existing u32 variable to my custom type, I would do it like this:
#[derive(Debug)] // needed because struct displays nothing so needs to have a debug view
struct MySpecialNumber(u32);
impl From<u32> for MySpecialNumber {
fn from(num: u32) -> Self {
MySpecialNumber(num)
}
}
fn main() {
let existing_var = 30;
let num = MySpecialNumber::from(existing_var);
println!("{:?}", num); // Prints -> MySpecialNumber(30)
}
If this is the first time you've seen the struct
or impl
keyword again, don't worry. I'm planning to cover those in future posts. They're a bit, different if you're coming from Javascript, you can think of them as a slightly more complicated way of making an object.
The above code snippet takes the existing_var
of type u32 and changes it to type MySpecialNumber
using the from method written above.
Because the From method is extending a trait it has to be written in a very specific way for it to work. If I changed the line fn from(num: u32)
to fn from(num: f32)
it wouldn't work. The source code for the From trait can be found on the Rust Github repo.
Into
Into is the second trait in Rust used for type conversion. It works in the opposite direction to the From trait, instead of creating x FROM y, it turns y INTO x, makes sense? It's a bit confusing right. I feel like code is the best way to explain things so let's jump straight into it (no pun intended).
First way to use Into
If there's an implementation of the From trait already we can also use the Into trait which is pretty cool.
To use into the main function of the above code snipped can be re-written as:
...
fn main() {
let existing_var: u32 = 30;
let num: MySpecialNumber = existing_var.into();
println!("{:?}", num); // Prints -> MySpecialNumber(30)
}
Arguably this was is more readable. The existing_var
is being converted to MySpecialNumber
because it has been added as a type annotation. If that get's removed Rust won't know what type to convert the existing variable to.
let num = existing_var.into(); // this won't work
If you really wanted to, you could write a full Into method similar to the way I wrote the From method above using the Into that from the Rust source code, but I think From is good enough.
Second way to use Into
For this way we're going back to my times_ten
function I used above when desiring the as keyword.
fn main() {
let num:u8 = 10;
let multiplied = times_ten(num as f32);
println!("{}", multiplied); // 100
}
fn times_ten(val: f32) -> f32 {
val * 10.0
}
Instead of using as we can use the Into trait like so:
fn main() {
let num:u8 = 10;
let multiplied = times_ten(num);
println!("{}", multiplied); // 100
}
fn times_ten<T: Into<f32>>(val: T) -> f32 {
val.into() * 10.0
}
So if you compare the last two code snippets, you'll see we can omit the as keyword and the type to convert to and move that logic to the times_ten function in the form of a generic data type T
and the Into trait.
No matter what type the argument is that is passed into the times_ten function it will always be converted fo an f32 before it is multiplied by 10.
Don't worry if this doesn't make sense now, the more you use it, the more sense it will make.
Like I said at the beginning I'm new to Rust so there's a chance I haven't explained something properly or I've missed something out when it comes to type conversion then please let me know.
I've tried my best to explain this in the simplest way possible but if something I've said doesn't make any sense, again please let me know.
Sources
https://doc.rust-lang.org/rust-by-example/conversion/from_into.html
https://www.youtube.com/watch?v=vH5xXr81a2Y
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=c749b272a173bfb34371092c562551fa
Posted on January 15, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.