- I wanna show you again, more practical slicing code
and at the same time, teach you a little bit more
about what strings are in Go.
So let's take a look at this sample program.
Now, strings in Go are UTF-8 based.
In fact, the entire language is UTF-8 based.
If you are not saving your source code as UTF-8
you're gonna have a real problem
with those literal strings and raw strings
that you're storing inside your code files.
Everything has to be UTF-8 encoding, all the way through.
Now, what's interesting about UTF-8
is that it's a three layer character set.
You have bytes at the very bottom,
and really, we would always consider strings
just to be bytes at the end of the day.
You've got bytes at the bottom,
in the middle you have what are call code points.
And a code point is a 32-bit or 4-byte value.
And then, after code points, you have characters.
And the idea is that a code point
is anywhere from one to four bytes.
And then a character is anywhere
from one to multiple code points.
You have this, kind of like,
n-tiered type of character set.
So, let's take a look at some code here.
On line 38 here, I have a literal string
and this string actually is going to be
a little string of 18 bytes.
Why is that?
Well, if we look at what this string is,
it's gonna be 18 bytes.
What you're gonna see is that the array of bytes,
I'm just gonna draw a bunch of them
until we get 17 there.
That first Chinese character that you see,
the very first one, that requires
three bytes of encoding in order to store it.
Three bytes, which represents one code point,
to that one character.
And, the next three, also, the next Chinese character there
is one code point that requires three bytes.
This is why we're getting to 18.
Three bytes, three bytes, and then everything else
is one byte all the way through.
Now, I want you to look at the next variable.
Notice that we're creating a variable named buf.
But buf is an array, it's not a slice, okay?
This is a constant in Go, which represents
the max number of bytes you need for code point.
So buf is an array
of four bytes.
That's our buf.
And that is also set to its zero value
through the use of the key word var.
Now, things start to get interesting here.
I'm about to range over the string.
And it might be interesting to think,
"Ah man, wait, I can range over a string?"
Yeah, yeah, yeah, yeah.
You can range over string or else
we are using value semantics here.
But, what's important here is that
we're gonna get the index position
and then a copy of, what?
I love this question.
When we range over the string,
we're going to get the index position of what?
And we are gonna get a copy of what?
You have three choices.
We could get a copy of every byte,
we could get a copy of every code point,
or we could get a copy of every character on iteration.
A lot of people wanna tell me that
I think we're gonna iterate by character.
What we're really gonna do is be iterating by code point.
Code point by code point and
the index position's gonna respect that.
So what does that mean?
It means that on the first iteration
we're gonna start at index zero.
Absolutely start at zero.
But on the second iteration, the next code point,
this is the first code point,
the next code point's gonna start here,
three bytes in, 'cause that Chinese character
has it's own code point that's also three bytes.
Which means on the third iteration,
our next code point is here, then here,
then here, and then here, one byte away.
So when we range over string, we're ranging
code point by code point,
which means r is going to give us back
the net position, the code point that we just
iterated over.
R, represents the type rune,
and rune really isn't a type in Go, it's an alias for n-32.
In fact,
the type byte really also is an alias, for UNT-8
But r represents, rune,
our 32-bit, our 4-byte value.
So, look how this code works.
On the first iteration,
what's going to happen?
Well, the first iteration,
i equals zero.
That's where we're starting, right here's zero.
One, two, three, four, five, six.
Starting at zero.
We get the rune length.
And the rune length is gonna tell us how many bytes
we need for this code point.
That's going to be three.
Remember, we needed three bytes for the first code point.
Then I created this string index position,
which is the sum of both of these values.
That's three.
And now we get to line 54.
And line 54 is interesting to me.
We're using the built-in function copy.
And copy says destination source.
I want you to notice that copy works with both slices
and strings, but since a string is immutable,
it can only be a source.
I also want you to notice that we're slicing
the string here.
Now, that's not going to create a slice value,
that will create a new string value.
So look at what we're doing.
As the source inside the copy s, bracket, I, colon, si,
we're saying create me a new string value.
Here it is.
That starts at index position i zero,
through three.
It's gonna give us a length of three.
So, basically, what we're going to end up having,
is a pointer that points here,
with a length of three.
That's our source.
Now, what is our destination?
Our destination is the buffer itself.
We're saying that we're going to copy this into the buffer.
But there's a problem.
Copy only works with slices and strings.
It doesn't work with arrays.
Buf is an array.
But notice something here,
this buffer, this array, the syntax in Go
allows us to apply by slicing syntax to an array.
One of my favorite sayings in Go, is that every array
is just a slice waiting to happen.
And this is what we see here.
So, in order to be able to use the array
as the backing array, that syntax that I've highlighted
will create a new slice value
using the backing array as our storage,
and setting length and capacity to four.
This is brilliant.
I can now use our local array, in the copy,
and we can tell it to just copy the first three bytes.
So these bytes here, come into here,
there you go.
And we've copied this data.
I'm trying to give you a sense of what this program's
going to try to do, is range over the string,
code point by code point.
We're going to store that code point
inside of our local buffer.
And then we're going to display information about it.
And if notice here, I'm slicing the buffer one more time
and this time what I'm saying is
I want to make sure that the length in this case,
is only three.
So I'm saying from the beginning to rl, which is three.
Which will now do this.
And that way we only display those three bytes.
Now, on the next iteration, what happens?
Well, on the next iteration we come back
and we're going to be here.
Which now means that i isn't going to be zero,
i is going to be three.
The rune length is still three.
Si is now six.
And we go ahead now, and this time,
we're going to create a string value.
It will still have a length of three,
but it will start here.
And we do it again.
And then after that, it will be one, one, one, one.
So, if you look at the output here,
you could see that we're ranging code point by code point,
through this string data that we have.
Code point by code point.
You can see the three bytes for the first Chinese character,
the next three bytes for the next Chinese character,
and I'm even able to display those characters out
the way you see it right there.
And then we ended up going into single byte characters
all the way through.
And you can see what those values are.
So, really cool, more practical code.
And we learned a little bit more
about how strings work in Go.