- Okay, the other type of string is the String class
and that's much more dynamic.
This is how you create an instance of String.
The String structure has a static method if you like.
It's called an associated function from.
String::from hello.
So this here is a literal piece of text
and what we're doing here
is we're creating a String object,
s is a String object, which basically owns its own memory.
When you say String::from hello, the String object s
will basically copy hello into here
and it now owns that memory.
So I'm gonna forget about the String literal now.
S has its own copy of that text
and that's what a String object looks like.
If you wanted to, no particular reason,
rather than using implicit typeing for that string,
you can use explicit typing to say s is a String object.
And that would give you the same effect.
So internally, a String object holds text
as a potentially growable vector of bytes.
That's actually specified in the documentation.
Internally, a string holds a vector of bytes
to hold the text that you've assigned.
So consider this example.
I talk about memory allocation.
This is how memory is allocated.
The String variable s lives on the stack.
So that's just a local variable on the stack.
We have this local variable s.
And so I'm gonna draw it as a String object.
That's my String object s.
The text that the String holds is allocated on the heap.
So the text that it holds, hello,
that text is allocated on the heap.
Hello.
The String object has a pointer onto the heap like so.
So continuing our example
where I've got my stack-based variable s,
which has a pointer into the heap,
and on the heap, it holds hello like so,
let's consider what happens at the end of the function.
At the end of the function,
this is how memory gets deallocated.
So first of all, my String object s is just a local variable
and it goes out of scope at the curly bracket.
And when it goes out of scope,
this object is going to be destroyed
just because that's what always happens
when you have objects allocated locally on the stack.
Just before the String object dies,
it has a destructor effectively.
There's a function we'll have look at called drop,
which is effectively a destructor.
The drop function is automatically called
on an object when it goes our of scope here.
And for the String structure,
the drop function deallocates its buffer.
So it'll deallocate the drop function,
will automatically deallocate the buffer
and then the String object goes out of scope
and the String object itself disappears.
So it's very similar
to the idea of destructors in C++.
There's no garbage collection in Rust.
That would be far too slow.
It does it deterministically.
An object cleans up its own allocation
just as it's about to be dropped.
So like I said, there is a Drop mechanism.
There's actually a trait on interface called Drop in Rust.
And the trait called Drop has a function called drop,
which is similar to a destructor.
It so happens that this happens,
this drop function will be called automatically
at the end of an object's lifetime,
very much like destructor in C++.
And it gives the object a chance
to deallocate its resources.
So luckily for us,
the String structure implements the Drop trait.
It has a drop function automatically called
when the String is about to be destroyed.
The drop function deallocates the buffer on the heap.
So I'm gonna have a look at some mutable String objects.
Then we'll have a look at the demo.
You can modify text in a String as long
as you've declared the String as mutable.
So there are various methods
that'll actually change the value of the String.
You can invoke methods to modify contents.
There are also methods to query contents
as well obviously, like .length.
Let's have a look at the documentation
of the String class or the String structure I should say
to see what's available.
And then we'll have a look at the demo.
This is the documentation for these String structure
in the String module, in the std crate.
Lots of functions here, which allow you to query
and modify the contents of a String.
Like push_str, for example, is like an append.
There's a push function, which appends a single character
and a push_str, which depends a string.
There are other methods down here which are listed
under a different section, as_ptr, for example.
You can get back the address
of the text that the string points to.
So you'll probably find yourself looking in here quite a lot
just to see what methods you have
and how they work and what parameters they take.
So let's have a look at our example again to see how to work
with the String structure.
Okay, so I'm back in the example.
I'm just going to reset the clock.
Just get rid of all my little windows here.
So we're back into a clean state.
This is my demo_string_handling in the demo.
My do_it function, we've looked at string_literals.
So now we're going to look
at string_objects and how to have mutable_string_objects.
So here's my example here.
I've got a string_object, s3, wales.
I've got a string_object, s4.
I've typed this explicitly.
No particular benefit of doing that really
but just for demonstration purposes.
And that String is cymru, which is, as you know,
is Welsh for Wales.
I can print out a string,
I can get the address of the first byte in the text
and I can get the length of the string.
And I've done that for string three
and I've done it for string four.
At the end of the function,
these two string objects go out of scope.
The drop function is called on the String objects
because String, the String structure,
implements the Drop trait.
In other words, it has a drop function,
which will dynamically effectively delete the memory
on the heap.
And then I've got a mutable_string example.
I've got a mutable_string, s5,
which has got some white space
at the beginning deliberately, super.
When you call the push_str function,
the String object kind of expands
its dynamic memory allocation.
So it's the same String object,
it's just that the buffer gets expanded.
You can convert the string to_uppercase,
trim the string to get rid of leading and trailing space
and then convert the String to_uppercase.
So s6 will be an uppercase version of super swans.
Let's run it and see.
So we have the details for s3 and s4.
So s3 is wales, the address of the first byte
and the number of bytes.
Likewise for s4.
And then we have our mutable_string.
S5, well numbered.
So initially, that string contains super swans.
So s5 contains super swans with spaces.
That's s5 and I've got the address of it there.
S6 is the kind of converted version.
It trimmed it and then converted it it to uppercase.
So s6 is the trimmed uppercased version of super swans.
Super.