- I wanna show you another piece of code here
around the idea of side effects and how dangerous
they can become if you're not aware of it.
And this is all I'm trying to do,
I'm not trying to scare you away from the data structure,
I'm not trying to make you go and have to use
the built in function, copy, all the time.
That's not it at all.
I just wanna continue to iterate
that when you're using pointer semantics,
and we're mutating memory,
we have to be aware of the side effects.
This is what makes programming difficult.
Your functional programming language friends
don't have to worry about this stuff.
Functional programming says everything is value semantics,
every piece of code, every function we call,
operates on its own piece of data,
but we don't get all the efficiencies we need
to have the fastest programs that we want,
and this is why GO is brilliant in giving you
the responsibility of both value and pointer semantics,
but we have to be aware of when we're using what,
and when we're using the pointer semantics,
we have to be aware of the mutations
and the problems they can cause.
So, let's take a look at this program here.
We've got a user defined type called user,
and the idea is that I have a system of users,
and we're gonna track likes for this user.
We're gonna track likes throughout the app.
Now let's just say I have a small system,
and we start this system up, it starts running,
and we only have three users.
So, I go ahead right here, in this program,
and I create my slice, here it is, my slice,
of three users, there they are.
Now, this is set to its zero value.
It's a slice of users, so I don't know why
I'm writing strings here, funny stuff, Bill.
But we're just gonna maintain our likes here,
and we know right now
that these users start with zero likes,
and let's just pretend we're gonna use the index position
and bind it to a user value.
So there we are, we've got users zero, one, and two.
They're like are set right there.
We've got a length and capacity of three.
Now, I go ahead like I would normally do in a program,
and I wanna share the user one, right here,
I wanna share it, and I create some sort of pointer
that now points to user one.
It's very possible that I'm gonna do this,
and I wanna share that user down the call stack,
in some other part of my program
that's handling likes on some event,
so we can continue to increment this,
and in fact, on line 23, what you see
is through the use of the share user pointer,
we increment the value that the pointer points to.
After line 23, this user doesn't have zero likes,
they have one.
Brilliant, but guess what happens?
If you notice here on line 26,
and I'll add some extra print statements here
so we can isolate the output on the screen,
we should see that that user one has one like
and the others have zero.
And there it is, user zero, no likes,
user one, one like, user two, zero likes.
Brilliant, it's exactly what I wanted to see.
Now, the system is running, it's dynamic,
some user now registers on the system,
and we go ahead and do what I would expect us to do,
we append a user,
but watch what happens on the append.
Is length and capacity the same?
The answer is yes, it is.
Uh oh, what does that mean?
It means that we're now going to have to create
a new backing array.
Well, our new backing array will be doubling in size,
and our length and capacities are going to change.
Suddenly, we're gonna have a length of four,
but a capacity of six, which also means that
all of this data is going to have to be copied,
and it will, here we are,
and we now point to this.
Notice what has just happened.
This pointer that was pointing to this backing array
is still pointing to this backing array,
but this is no longer our data.
This is now our data, because of the append.
Which means that if I continue
to increment likes for the user,
like you see on line 34, guess what?
They don't get registered.
You might think, Bill, this would never happen,
this would never happen.
We're incrementing this, we're not incrementing this,
but this is the data that we're working with now,
and guess what?
We kinda have a memory leak
in a sense that this memory can't get released
because of this pointer right here.
And you might think, Bill, this would never happen,
and I know some of the best GO developers on the planet
who have created bugs like this, not thinking how append,
when length and capacity are the same,
is going to make a copy of the current data,
which is the point of truth,
and now make this the point of truth,
and yet we have these pointers now
working against the old data structures.
This is a side effect,
these are the nasty bugs that are so hard to find,
and so anytime we're working with pointer semantics,
that's great, it's gonna give us levels of efficiency,
right, we have to be careful there,
but we also have to make sure
that we're very clean with data updates, like with slices,
and that our mutations are not going to cause problems,
or the mutations are happening in the wrong place.
This is just a program to show you
what a side effect really can look like
in production-level code, and how careful we have to be
when we're sharing these slices beyond program boundaries,
especially if the call to append is going to happen.
And one of the things I do a lot
is any time I see a call to append in code,
I immediately put the breaks on the code review,
because append can create some of the nastiest side effects
you've ever seen.
So unless the code is decoding or unmarshalling,
usually an append call is somewhat of a smell,
and it's not necessarily wrong,
but we're gonna have to double and triple check
that we're not causing some sort of side effect
with a backing array that's gonna get replaced.