I'm wondering what the difference is between:
"some string".to_string()
And
"some string".into_string()
The former seems to come from ToString, which is quite clear.
However, the latter seems to comes from IntoString, which is less clear to me.
What does it mean to consume a value ? What is the difference between the two traits ?
Additional information after doing some digging around.
Here's the current implementation of into_string for a String. As you can see, it only returns itself, so no allocation is done.
Move Semantics
Consuming a value has to do with moving a value. Before I discuss the differences between the two traits, I'll give some examples of what it means to move a value. Let's create a
VecofAsciicharacters:asciis.Internally, the
Vecis a struct with three fields:Vec.Vec.Vec.Pictorially, the memory layout of the
Vecand the data it is managing may look something like this.When our
Vecgoes out of scope, it frees the memory it's managing. The freed memory is garbage to us. It would be erroneous to access freed memory. This would look something like the following. TheVecis gone and the memory on the heap has been freed.Now, let's go back to our code and try to make a copy of
asciis.Let's guess that
an_attempted_copyis a copy ofasciis. After we make the copy, our memory may look something like the following.Right before we try to
println!asciis,an_attempted_copygoes out of scope! Just like before, the data pointed to by ourVecis freed.Uh oh,
asciisis pointing into freed memory! This is bad news since we're just about toprintln!asciis.So how would we remedy the situation? Well, here's two options.
asciisintoan_attempted_copy, we could copy the data pointed to byasciisinto a freshly allocated piece of memory. Other languages like C++ do this.asciis, we can move it! This is what rust does.So what does it mean to move? It means that
an_attempted_copywill take ownership of the data previously pointed to byasciis.asciisloses ownership and we can't use it anymore. Let's renamean_attempted_copyfor clarity.Now, let's draw our memory layout right after we move into
actually_a_move.asciisdoesn't own the memory anymore, so we can't useasciisanymore. This means it's pretty much garbage. So if we can't useasciisanymore, what happens when weprintln!it? We get the following error.As expected, the rust compiler is telling us we were trying to use
ascii, butasciiwas a moved value; this is erroneous.Move semantics (and related topics like borrowing and lifetimes) is tough stuff. I've only barely scratched the surface here. For more information, rust by example and this stackoverflow question are both good resources.
to_stringvsinto_stringNow that I've explored the concept of consuming or moving a value, let's get to the differences between the two traits. Let's first look at the type signature of
to_string.This function takes a reference to
selfand returns a freshStringfor us to use. I haven't discussed references and how they affect movement, but trust me when I say no moving is done here.Now let's look at the type signature of
into_string.This function does not take a reference to
self. Instead,selfis moved into the function.So what are the implications of this difference? Let's take a look at an example.
We again create a
VecofAsciicharacters. Then, when we callasciis.to_string(), a brand newStringis created andasciisis never moved. This code will build and run as you expect, printing out[h, i]. Now, let's useinto_string.Here's the error message we get when trying to build this code.
So what happened? Well
asciisis being moved into the functioninto_string. Just like the last time we tried to useasciisafter we moved it, the rust compiler will reject our code.