In this first section, I'm going to go over some of the syntax of go, which you may
already be familiar with, but there are various little bits of syntax that you may not have used and I
want to recap those and also look into some of the areas like
methods and interfaces where there's some depth as worth recapping or
going into as you may not have seen it before. So, to start with, I want
to look at something very familiar, which is the for Loop and in particular the use of
break and continue and at the same time, what happens with a switch statement?
So let's get
We have our world, we don't need that program anymore although we use it just to check that the environment is working.
So let's just use, go run and yeah. Okay, let's looks like it's
good. So the for Loop so the for-loop syntax you are familiar
with because you've been using go for a while, I hope. And so the basic one is just
a simple Loop like this. And what I'm going to do is look at the use
of break and continue, so break obviously gets you
out of a loop so you can use that keyword like this and it will drop
you out of the loop.
Lee and continue will take you back to the beginning of the loop. So it's essentially a
go-to to the beginning of the loop. Both of these can also be used with
labels in particular break. So if we wanted to break out of a particular Loop, when
things were nested, we could actually name the loop. So we will say, you know,
my label and then I could say break my label,
which will take me out of that particular Loop here. So
this is a little example. Let's make ourselves something a bit more interesting.
And I'm going to make a program that counts at numbers and accounts, even and odd
numbers. So, let's go like this, going to have some
variables to count the number of even and odd numbers in my program.
And I'm going to give myself a list of numbers. So I'm going to like this.
So just create a list. This is make something very silly.
Like that. And I'm going to range over those numbers. So we're going to say
numbers. Okay, there we go. Is range over those numbers. Notice, I use the
underscore here because I don't actually care about the index in here. I just care about the number n
itself, and I'm going to go through an account. So I'm going to say,
if n %, 2 equals 0, I is even
then do that. And so, we'll just do even less equals 1
or plus 1. And at the end is to a Quick Print
half. Even how many of those are we got
Order, how many of those we got?
You know what?
This is go run out. There you go. So, there are three even numbers in there,
and there are five odds. So you can see the odds are in here and the
even CSO the evens or to 8 and 10. All right. So that's a very simple
program with a fairly simple Loop. Now, what we could do is we may
decide there are some numbers we want to exclude. For example, let's
not count 0 as even or odd that these only
to be an order. Let's just shove it in somewhere else. Now, let's just say, we decided
that something else that needs to be.
10 different way and we're going to excluded all together and counted as if it is if it
wasn't a number. So I'm going to have a total number of numbers
and I'm going to put myself a little
If and equals zero continue.
So it's going to go like that and total,
we're going to add up the total number of numbers and I just adjust this. So we get a total here.
So this continue here is just going to say, if I've got zero, I'm not going to count it as even or
odd, just run that. There we go. So there's nine numbers in total, and you can see,
five and three, even at odds. So you obviously skipped the 0. Obviously, you could do this a different
way. You could come in here and you can say, you know, what do this? It's
just indent
At or you could. So you could count like this, that would
give you the same result. But continue There is a
handy way especially if you've got a lot of logic, you want to deal with. Now, another thing we could
do is let's suppose that finding zero was a sort of marker in the
list that said you had to stop. Then you could do something like this.
It's clean this up a bit.
So that's going to count even and odd numbers until it hits zero and it's going to break out of that
Loop. So, there we go. There's only one even number 4, 0, that's
number two right here. And another you can do is you
can label this Loop so you could break out of it. So for example,
we could do something very silly like go around this Loop, some number of times,
let's just do a classic thing. It's going to go around, this Loop ten times.
Let's do this.
And we can specifically decide to give up when we find a zero.
So I'm going to say here, abort, its course called this
abort and if we find a zero then we're going to give up
immediately. So let's just run this guy so that gave up immediately
because it broke out of this Loop. If there's no label. Now, let's just run it like that.
Sorry good old go tells me, I miss something. It goes around 10
times. So that aboard label allowed
To jump right out of the loop there and jump out
of two levels of looping. So the commute of you have nested Loop in. You want
to get out altogether, it's just try.
One last thing.
And that you see what that did was it continued up to here. So that actually jumped out of the loop and up
to the loop above it. So with labels, break an abort
allows you to jump around within Loops. There are close to being a go-to and can be
kind of useful, especially if you have some sort of condition where you want to do something.
For example, one common thing is, if you have, like, I'd say a debug mode, where if a certain thing
happens, you need to get out and terminate, you could use a break for that or maybe you
have some mode where you wanted to do is, let's say counting of events, but not
actually,
You the events then you could use a continue to jump around in a loop useful. If you want to sort
of jump quickly in general, if you're using a lot of them, I think it's bad
sign because your Loop is going to be hard to understand. But at the beginning of the loop,
where you want to add a board or jump again, go around
around the loop. Again, without doing work, both break and continue can be super useful.
Another place that break can be useful. As in a switch statement, especially
if the switch statement is rather complicated and so, you can get
out of the switch and you can even
Even get out of a loop inside a switch by using break with a label. I'm going to
demonstrate that by modifying the program that was used in the for Section
which is counting even and odd numbers to have a special case
where it comes across a negative number and aborts. So, first of all, let's have a look at
that original thing. I've cleaned it up just slightly as you can see.
It's got a few different things going on in here. And this, when you've got a lot of
ifs like this, it can actually be really useful to stop and say, hey a switch statement
is useful here rather than having
At long nested thing. So let's turn this into a switch and then
we'll we'll work from there. So switch statement, syntax is like this. So we got these
three cases, right? We've got this case where it's zero. We've got a
case where it's even
We've got the other case I'm going to use default for that. And so, let's clean
this stuff up here. So okay, so they've got a 3. So if we come across is
0, we come across an even number or we come across something else so we can just change that. So
in this case we're going to just add one to even if it's not 0 or that when they start there's and
I'm going to count zeros this time like this. So what do that? And
let's just add this into here.
And down the bottom here, let's put in the zero account.
Okay, so this switch statement is actually much easier to read than those ifs that we had before, especially when you
get a long list of if, sometimes it's really worth going. Hey switch statement makes a lot of sense.
It's especially hard to think about switch. If you're coming from see where you had been
switching on a single thing, whereas here, you can have it as we have three
completely different cases in here and they could be, much, more complicated. All these are
using n, but another case could use completely different variables or
completely different conditions. So let's see if I haven't forgotten anything go.
All right, there we go. So now we got olds even zeros and that so great. Now
suppose that we wanted to terminate this switch a
bit early. If you notice in here
we could say okay well I'm done here. I want to get out of this
here. Now we might we don't need to do that like we would in see there's no fall
through happening here because that happened on the
first the first time that case hit. But we may
want to if there was some other
Ian actually terminate completely so break can be used.
So for example you there could be some sort of if in here and you could say okay well get out
of here if that thing happens otherwise do some other stuff.
The other thing we can do is we can give up completely if there's a problem. So for example, I'm going to use
break here to get out of this switch and to get out of the loop. So I'm going to go back happening
that keyword and bought in here. So here's a label abort. And I'm going to
change this slightly so that it recognizes odd
numbers.
Chloe. So, I'm going to say that Isis counting zeroes evens,
and odds. And then if it comes across something else, that's not one of
those two things, then we, then we can abort. Now, let's
go into here and we're going to go break a board like that.
Okay, so what happens in this Loop now is if there's a negative number none of
the previous three cases, apply, and the default actually happens. You
might be wondering about this case here, for when the number is
negative, or the case of
Above. But, in fact, when you do module mod a negative, number you, still get Negative something.
So neither of those cases will apply in the case where it's negative, and you'll hit the
default and we will actually break from the loop. And if you take a quick look up here,
go run is only counted to even numbers. It hasn't counted the number 10,
which is after the negative number, because it broke at that point, but we can make a bit more
explicit. And this is where you might actually use this kind of thing. Let's make ourselves
an error.
So we would have this error here and in the case that there's a negative number will actually
set that error. And I'm going to use the errors package.
I want to import that and I'm going to say New Era.
Found negative number.
As always, though set. And then at the bottom here, we can actually say, you know, if there was an error.
Just print it out.
Let's print the error like that, so do that and let's just run it
error found a negative number and it broke from the loop at that point. So the break statement within
a switch here, can jump out of the switch and out of the loop using a label, which is
Handy. And of course, it's nothing like in C where you have to break every case within the
switch because otherwise it will fall through. You can also do continue.
Another thing we can do here is we can modify this slightly so that if it sees an odd, number it just carries on but
doesn't count it. So, sorry, a negative number. So let's just do
This is moved a total down to here. So only counts if it gets to here
and every time we get a
negative we don't need this anymore.
We don't need this and let's kill this. So this
has been clean up a little bit every time it gets a negative number is going to continue to that aboard
label up play and perhaps we should say let's change that to ignore
negatives and support doesn't seem like a very good name anymore.
That's from them. So now you can see the III even
numbers because it did actually count the 10 at the end and what it
did was when it hit that negative, it jumped across and you can also see that there's a
total of nine things counted. If we were just to add in
here then numbers just so we can see.
How many there were you can see There Were Ten in total so we ignored one.
So that's really continue and break within the context of a switch
and a for Loop both useful in some circumstances.
Another thing that's worth reviewing, after you've been using go for a little while, is the codon
equals to declare a variable. And remember, that is a declaration
and has interactions with if and scoping that sometimes can
trip you up, if you're not thinking about it too carefully. And I know that personally, I've been
tripped up a couple of times, so let's just look at declaring a variable. So I'm going to do a
little simple example. I'm going to create a couple of slices and going to do two
different ones. I'm going to say in slices, going to be just some
list of numbers.
so,
So there we go. Just a list of numbers. And I'm also going to have a string slice, which is going to have some
strings in it. So I'm going to declare it like this and that's going to
be sky, was
the color kind of like that
and I'm going to use some work with those. Let's just do a
declaration. So it really classic. Kind of Declaration will be something like this
and we will look at that in slice and we would say you know if it's got
Not empty. Then we're going to do something otherwise, when do something else. So let's just go like this,
it might go in here. We like to say in slice has
an element's yourself, rather simple and do that.
And then down here we could do you know
In slice has no elements
like that and we do exactly the same thing for
the string, Slice on the copy, paste on that.
Into string.
Okay, so there we go. It's going to look at these two slices and tell us some information about
them and if I were to close my strings, it would be even better.
Okay. So there's pretty simple program does. Let's run it.
And whoops. What have I forgotten to do here?
That's right. The city on the internet. Let's just do that. Okay.
So this in slices, 8 elements, as we might rather than those of five
elements and you can see the N here was declared
here and it was re declared here and there's no error
occurring. Notice that if we had done this, let me just take this decoration
out of here.
Let's put it here.
Now, do the same thing here.
Just run that. Oh, no new variables, I'd line 18. So, right here
saying this, has been declared before because these two are within the same
scope, but when it was inside the, if here, it was actually not in
the same scope, the scope of that n
is this if statement is whole, if statement up all the way
through, including the else is quite tempting. If you've been using C or
something like that, to think it might just be the first block here, but in fact,
it's being declared in.
At the scope of this statement. And so the same thing, let's just do the same thing
here. This is put this back
So when you have those two things, then you get no error because they're
completely different scope. The other thing, the surprising sometimes is that n
is valid here. So let's just for the fun of it, stick it
in here like this.
Let's run that and you're going to see that well because it does have some elements.
So let's just say it's just messed. This up, just break
this and you can see, has no elements and actually is lying
because I messed up. The FM says eight, but that variable was valid inside that
entire scope and of course, this can be quite deep and nested. So
watch out for that when you're doing declarations these two things are, these
are different variables.
So you'll know if you've been using go a little bit that sometimes you
do do re declarations in the same scope and I'm going to show you an
example of that. Let's just simplify this a bit.
Let's go like this. Now if you remember that in the
wonderful world of printf, you can actually get an error. Return printf will
actually return to you the number of characters and an error. So you can do
something like this. And so I'm going to say printf in
slice has sent the
So there it is. And I'm going to do the following. I'm going
to do this. I'm going to do exactly the same thing again, but I'm gonna do
it for string slice and this is going to give us an error. When I
run, this is going to complain. So, let's just do the following
Okay, so it says no new variables are line. 12 line, 12 is here because n and error will
both declared above. Okay, let's just change this slightly and
do that and now I'm going to actually print those out, just so we see
them and also, they go doesn't complain.
And let's print out the error too.
So I hope they'll be none. Okay, so now
you'll notice that it didn't complain about error being re declared and it
was apparently being re declared as a convenience, what go allows you
to do is if you're doing a declaration like this where there are multiple
variables and some of them have been declared before, you're allowed to reuse, them
is the same same value. So this declaration here and one
error is actually only declaring N1 is not actually read.
Glaring error, it's reusing it and you just be careful if it was a different type, of course, you could get some
sort of error. So that's a convenience that you'll see throughout and in fact, because
quite commonly, you might see something like this. I'm going to ignore everything went.
Let's just say, Sprint out the are we still should be numb.
Here.
You're going to run into a problem because this thing says you're not declaring anything new
and so here you have these set the error. So this is a trap, right? If you change from this being an actual declared
variable to something like that, then you have a problem and you would have to do this because there's
nothing new being declared. Here you go, there's no error
occurring. Another annoying thing that can happen. Sometimes by
accident is shadowing of a variable where you actually read, declare
something, and then you don't get the expected result particularly
this happens.
Named return values for a function. So I'm going to demonstrate that
is that in slice, that I've been using before? And I'm going to make a silly little function,
which tells us whether it's empty like this. It's going to take
in a slice of events and it's going to return a number
and that number is going to be 1. If the slice is not
empty or zero, if the slice is empty, obviously, realistically you would do this with a Boolean
but let's do the following. So, we're going to go in here and we're going to say if
The length of I is greater than zero.
Then n equals 1, n equals 1 and we return
from that function. So that will say if it's non empty and then we'll
just print that result out.
And call it with this in slice, right? So there we go. It's going to go through
here. It's going to set and I'm going to get that value. And by default, of course, n is set to 0
when it gets a 0 value is 0 for integer. And so, let's just run
it.
Oops, go, go go. Okay,
so tells us one now. Let's suppose we're doing slightly more complicated logic and we
decided we need to declare a variable for this. We could do the following so you can go
in here and we're going to say something like, okay, let's just count the number of elements in that
list, and if it's greater than
zero, then we'll set it to 1 and we'll run. So, it's just run that program
zero. Now, that's the big surprise you, it looks like a first glance that.
Is the same thing everywhere and we've sort of just change this value here,
but this end within this, if statement is not
this in here because this thing here is a declaration. So in fact, it's allowed
you to declare something that was already in the return parameters here,
but as something else and there's no warning of this happening. And one of the ways is quite
often happens is with errors. So you have an error return here.
And then you somewhere in here, go error colon equals secure within an area.
Because you're doing that and then lo and behold the actual error, doesn't get returned by the
function so it's definitely a thing to work out to look out for when
you're using named return parameters and in general, if
you can avoid them, I think it turns out to be a bit easier to work with them. So, in this case,
I probably would actually have just done and in like that and returned it in and I'll see if
you have multiple of them, you can have multiple things like that.
If you've read the effective, go web page which has lots of great information about go, there's an
example in there something they call a leaky buffer which is a
buffer of available. In this case buffers it is. It's a
user's a channel to actually store them and it can be useful for
keeping around a pool of something. In more recent versions of go,
there's synced up pool as well. But this example really illustrates something very important
about channels and in particular, the select statement as a, what I want to do here is just
review it.
Lightly. I've slightly simplified the example to look at the pertinent things. So in the
in the Emax here, I've got something called a buffer, which could be, for example, a
bite slice, it could be a list of strings. It could be something that you're going to
reuse and I've got a couple of functions on it one called process and one
called get. So get would fill the buffer with something and process would process
whatever is in the buffer and after that we can assume the buffer can be either reused or
discard it and to keep this leaky buffer around what they do
is they use
Use a channel, but here is actually not a number for jealous, a
buffered channel. So it actually has slots for 10 and the idea is that this channel
will keep around up to 10 of these buffers available
and they can be reused. And so it becomes a little Q fifo queue
of buffers. And then there's a couple of functions near which would typically
run as go goroutines one called a worker
and one called a controller. And the idea is that the worker has to do some work
on buffers and
It gets a free buffer from the available Channel or if it
can't it will make a new one and use it. And when it's finished with that
piece of work, it'll pass it on to another go routine. This case,
I've just called it the controller which will then do something else with the
buffer. So the worker is just filling buffers up and the controller is actually
processing them. And so, what I want to do is look at how select is
used in a particular default to implement this buffer because it's actually a very, very
neat example of how powerful
powerful channels and select can be. So if we look in the worker,
you'll see that it's a continuous loop. So it's an infinite Loop and
there's this select statement and within the select statement is this. So
it's trying to get a buffer from the available Channel.
And if there is one, it will use that buffer, it'll use it to it'll call get on it and it will
then pass it on to the controller down this channel. If there isn't
one available then what will happen is it? The select statement
will immediately use the
Fault. And in this case, it will make a new buffer from scratch. So it'll build a new
buffer which can then get used and pass that on. So, in this way, if there's nothing available in
the pool, in a van in the available Channel, it'll make a new one, otherwise it
will pull one. Now normally when you have a select statement you're quite familiar with it
blocking on a Case statement like this with it with a channel receiving
it. And this is a good example of using default where if no one else is available to
communicate available isn't ready to send us anything, then we'll drop down into
default and really in a very
Small amount of code here. You've got a pool, a pool of buffers, then it gets passed on to the
controller. And the controller here is similarly looping around. It pulls
any buffers they've been created, does whatever process needs to be done on
it and then put them back. And this again is a very, very small select
statement. It tries to put it onto the available Channel and if you remember the
available channel is buffer that has space for 10 of these. So if there's room
it will put it on, put it into there, if not
it
Hit the default and it will then just drop out. So this allows it to drop the buffer on the floor. So
that point, if it doesn't manage to transmit it to available, it drops out of the
loop and we're done. And so this, in very small amount of code gives
you a way of pulling something could be buffers, could be any other sort
of type that that go has using the select statement in particular, using
default to drop out if no one else is ready to communicate.
In this section, I'm going to talk a little bit about Anonymous struct. You'll no doubt
familiar with structs because they're the standard thing, we use to create what might be called
objects within go and normally we do
at some sort of type declarations. I'm going to do a little simple type declaration like
this. I'm going to say translation is a struct
and it's going to have an English word. Just
going to be a string and a French word which is a string and you created a
translation type and then you
Go in here and you might set up yourself. Something like this. So,
so we're going to create ourselves a translation.
And I'm going to say that mr. In English
is monsieur in French and so there you go. Is simple thing and we
could just see all that struck looks like
use Santosh feet print it
Let me go. So obviously there's the structure like that. There are situations which you don't
necessarily need to actually have a named type for it. There
may be something that you're going to use temporarily or particularly. If
it's some sort of template, you're using a test where you might not need to declare a
type, you might just want to do it sort of in line. So for example,
I could actually have taken just this bit here so just the struct
bit which is type literal and go on here.
And on this. So here I am declaring
something called title which, and here's the type for it, and here's
the setup of it. So, it's just run it. I'm just going to
delete that and then, there we go. So
the same thing has happened here, which is that I've now got a type and you can see in the
original one. It actually said it's this type name translation for they named it as a
type. And here it's Anonymous is saying, this is how that particular thing has declared
so super handy. If you want to have a list of different bits of
Later that you're doing and you can then extend this even further, you can have
slices of these and use them for any sort of templating. You have
another time that Anonymous trucks as in handy as when you're reading in some data,
especially if you're doing a little program that needs to quickly read in something and process
it, or output it using a template for example, made a little program here which
extends the English French thing I had before to look at some
Jason now, obviously the Jason probably wouldn't be embedded in your program. Like this, it would probably
be reading from a file but I've just
Just dropped it in here and it's got a three examples. Mr. Doctor and professor translated from
English into French and to read that into something with a
reasonable structure. I've just created an anonymous structure here for these
titles. So I just said, we're going to have English and French in each of these things and these
names correspond to the Jason field names and I'm
going to unmarshal it. I'm being a bit Naughty By ignoring the errors, but this is just an example, you
shouldn't do that and then just output those things. And again here, I'm able
to use the names.
Of these elements because I've declared it here even though doesn't have a name of his own, it doesn't matter.
It's still fully declared and if I run that it should
unmarshal that Jason and then be able to give me the translation from English into French of those
three names. And obviously you can imagine extending this quite easily to something where you
use the OS file to read in a Json, whole Jason
thing, I'm Marshall are into some temporary temporary structure and output
it so as well as having Anonymous struct. So it's also
possible to have an
Interfaces and you probably use one without thinking about it at all.
That's when you write interface like
that you haven't actually given. The type declaration is just an interface. In this case is not only
Anonymous, but it's got nothing in its the, the empty interface but sometimes it can actually
be handy to use and Anonymous interface just quickly declare one and the
sort of the funnest trick is using it within a type
assertion. So here, there's some variable X which is interphase
and what this is doing.
Is saying here is a type of session and it has to support the string
interface. It has to be have a string in it and if it doesn't
if the type of session fails, then we'll say, oh well you know, x-axis
account strength and you could use this for example, within a switch
statement to determine what interfaces are available for a particular
for a particular interface or you can use it like this to actually say, you know, at runtime.
Well actually string is missing the most likely use of this I think, is within a
test.
Sweet. But it could be handy and just bear in mind that whenever you write
interface like this, there's no reason why you can't have something with in here and actually be
declaring temporarily an interface type with without naming
it.
So if you come from an object-oriented background, you might be trying to find a
way to do subclassing and go and things like that, which, of course don't exist. Because
the way in which it deals with objects is quite different to a lot of other
languages but you will also have done things with struct swear structure have
methods on them and there is a way of relating to structures
together. So that, the say the field and the methods on those structures are actually
combined, and that's called embedding. So what I do is I'm gonna do a little example of
structure embedding to show her.
Works. So what I'm going to do is I'm going to create a string class, I'm going to have my own
sorts of strings. I'm going to call this my string, which is going to be a structure,
it's going to store a string inside it and I'm going to declare a method on
it which actually will output it to standard out. So we're going to just make
ourselves. If you've got one of these my strings
then output on it will
print it. So we'll just do simple thing like this m dot strip, which will print it
out and
But also make ourselves a little function to create one new my string, which takes in a
string and returns a my string
with straw set to ass. So that will create that. And then we've create one of these down
here.
My string and sorry, I'm gonna make a new one. You my string,
hello world, and ask it to Output
itself. So we've got structure here, which is a symbol
embedded Field Strip, which is a string, a way to make a new one. A way to take one of
these, my strings and print its output, and then we'll just run it.
Oops.
I should remember that as you return one of these from time,
There we go. Hello world so simple thing. Now let's suppose we wanted to take something
that had all the functionality of my string but
somehow extended it in a way on a simple way to do that is by embedding.
So I'm going to do a new type which I'm going to call shouting.
Which is also going to be a struct.
And it's going to be able to do everything that my stream can do. So I'm going to embed it like
this. So now I've got this shouting structure which is going to be four. Strings are all
in uppercase I want a way of making a new one so I'm going to declare a function to do that.
So it's a new shouting string. It's going to take in a
string. It's going to return a shouting string.
And it's going to be very like new new. My string above, we're going to actually uppercase
the all the letters, so I'm going to bring in the strings package
and I happen to know, there is a nice function for this. It's just
check that I know the right syntax. Sorry,
let's go doc strings here and if we look
there's a nice function here which is called to Upper so that
will uppercase the stream for me. That's great. So I'm going to use that so
I'm going to come in here.
And I'm going to Claire get myself in my
string.
So we can count, sorry, shouting string.
Like that, I'm going to set its string to be
the upper casing of that thing, and I'm going to return so that should be like
that and I'm going to change this to be a shouting string here. So
if we just take a look at this, what's happened to you is, I've created a new one of these shouting
structures, which embeds my string, which contains this strut
and I'm going to set it to be the upper casing of this thing, the shouting part,
and return it. And then
I'm going to create a new shouting string, so this will be of this type and I'm
going to call output and output is actually a method on the parent if you like all there
isn't really a sense of a parenting go so for a lucky I should put her on
that and there you go. Hello world comes out shouting. So what we've done there is we've
embedded one structure to another and it inherited both the fields that
were in it and had access to them and also the methods that
were defined on it. And interesting question is, what happens if you try to define a
method with the same name?
I'm named. Let's just try this out. So I'm going to say, oh, I'm shouting.
And do output. And I'm going to change the slider. Just so we know which one is going to say really loud.
Like that. Right? And there's a line feed in there
and let's run this. And so now in a sense you've
overridden output it now because you're using this one is so it hasn't there's
no error here. You're defining a method on that particular type and
because there was one in the parent if you like it's actually been overridden and now
you're calling calling that particular one. So that's really embedding. It's a useful
thing. Sometimes the most common use of embedding you'll see is to include a
mutex
In a structure, I'll just show you quickly what that would look like. And I'm actually going to
need a new text for this, but there might be a time you could just do that.
Sync, mutex, you've included everything about a mutex, all of its methods, within a, my
string. And then if you want to lock a my string and unlock it, let's suppose that
some reason we need to do that. You could go into here and you can say
hello Dot Lock.
And could unlock it again
and notice here. This this is a shouting string which embedded in my string,
which embedded a sink mutex, but that will allow us to lock and
unlock without having to explicitly talk about a mutex. Some people,
you'll sometimes see something like this mu text, then you've got to go a
Hello dot m dot and it becomes a bit of a Mess by embedding. You,
you make it protected by a mutex also, if you stick it at the
Pierre. And it makes it very clear. Clear? When you're reading the code, that this
particular structure actually has a mutex, which protects it and obviously, there's that means there must be
something that needs needs protecting in it, or it's being used in some interesting concurrent way.
If you spend any time looking at the Go standard packages, you'll come
across some time to time functions which have the name must in them
and these are actually typically methods that will cause a panic or E
runtime hard failure. If something goes wrong and they're usually
using the case that this should never ever happen and it can be quite
useful to do this because what you're doing is you're failing as fast as possible. And a great
example of this is must compile in the wregget.
Package typically within a program, you'll have a lot of fixed regular expressions. And
if you compile them, which you may want to do for Speed the actual standard,
regrets compile function returns an error, and you need to check the error in need to handle the error. But
if you know, for certain that the regular expression will always compile this, something new you
fixed at at the time, you rode the program, then you can
use must compile. So let's just do a little example of that, just to see how that works.
So first of all, you go doc to have a look at reg ex compile.
So this is compile function, which allows you to take an expression and it
gives you an, a regular expression and an error. So, we just bring in the
regular expression package here and we
try to compile something. So, let's say a regular expression error
And in here will be expected to look at that error. So we're going to do something
simple like this. Needs to match. 029
this needs to match a number. I this is a perfectly valid regular expression and if error is
not equal to nil, then we're going to say something horrible. So let's just do this.
Fatal error has occurred, right? Bad regular expression. The when you're writing this sort of the,
you get the feeling like you're writing something which is never ever going to get executed because
this this better compiled, but those we have to handle the error
So, let's just do this and we'll bring in the log package, so that's
being being logged. And then obviously, we could use that regular expression
to match something so let's just look at that. So if we look in here
go dark rexqq match, there's this match function, which allows
you to do it like that, or you can do it on an existing regular
expression, and there's also match string.
That's the one we're going to use when a match the string so I'm going to say like this
does it match just do this?
Truth value.
Does it regular expression match the string? Hello.
Which I'm pretty sure it won't match because there's no numbers in
it. Don't rub some and we don't use,
we tell format, thank you, go for reminding me of that. Okay. Now, it doesn't
match but also you'll notice that this thing never get executed, and it was
completely and utterly useless in this situation and I was just check this really
works. Let's just say hello, one, two, three,
That's right that get out. That does match. Okay, so this is obviously working fine. Now, what you'd
really like to do is eliminate writing useless code like this is never going to get executed,
and also, you want to detect a compilation error early because if
you, if this is wrong, if this thing doesn't compile correctly, then
it's going to indicate that you have really screwed up badly. And this is something
that should have been fixed long before. Let's see if I can make it fail early, you go see,
there's a bad if you did that, then it's a program here. Another way to do this in fact is to
Use must compile. And the best way to do that is something like this. So let's just call this thing
match number and we're going to go up here. I'm going to say well I should let's look it up in the
old go, dog.
It's right, it's right to do that. Okay,
so must compile returns a reggae it's just like
before but it will also Panic if something's wrong. So
let's use that as do must compile and let's
make the same error, that's below.
And then we can eliminate all this code and we can use match number here.
so this is way cleaner than what we had before, and
As usual. I forgot to
remove something to go with Som know about bam Panic. So the program
refuses to execute because in fact this compilation fails and it, the
error is quite clearly shown their this is what's wrong with it. And so if I then
fix that,
Andre run my program. There you go. It works! Fine. So if there are
must something very things available, there's one of the template packages well, when you're taking a
template, which, you know, is fixed, and you know, will actually work. Then use it because it
makes the code, much, much cleaner. You don't have to be if they're erring all the way through.
So, you've probably used the defer statement, for example, to
close a file. When a function exits, especially when you have error conditions, you want to make sure
it gets closed, or if you use the HTTP package, to make sure the
body of a request is closed, and it's a very, very useful
function within go. But there are some cases where it can be a little bit
tricky with a some couple little pitfalls. So I'm just going to review the use of defer
and look at the order in which twofers happen. The way in which
the arguments and the
Defer is are evaluated and also a little nice thing you can do around error handling.
So let's just do some basic defer to start with. Let's make ourselves
a simple little function. Let's call it do
and we'll make it defer a few things so it's going to print out.
This is the first defer, my guess could do
three to First going to look for anything. Very interesting, this function
and we'll call it.
And we'll run it.
So third, second first. So as you can see, first of all,
the defer order is strictly backwards. So the last of fur goes
first, I was very useful. If, for example, you opened a database and then you need to
query on the database. When you opened up the query and you want to make sure
that the roads are closed before the database handle is closed. So that
happens in that order. Now, the other thing that happens
within defer, is that the right hand side, which is always a function
call, it's always a function
Gets evaluated and it gets evaluated in two different ways,
the arguments to the defer get devalue ated, when the defer statement
appears and the actual function gets evaluated when the de first, it
actually executes. So, to illustrate that, I'm going to add in a number in here,
was going to call it and just an integer. And I'm
actually going to print it each time.
so, if we go in here,
So this shouldn't be any surprise in what happens here. Let's just make sure
that when around that
Freshly printed it would help, wouldn't it?
Okay, so one because n is there. So let's now try changing the
value of n here.
So it's changed. Let's run it. So now what you can see is that
and has taken on the value to here
and one here, because this part the arguments to the
function were evaluated at the time that the first statement happened, so you can see
the End changed value, but it didn't change what was in here.
It's quite a common Pitfall to think that n here because it's changed
here before the defer statement actually executed means that this will be
And when in fact, this part was evaluated before hand, that's actually part of the reason why you
quite often, see people do defer Funk with no arguments and then they do all the
evaluation within their. So that gives you in a
defer, you have to remember that that the arguments are evaluated
beforehand and now this is particularly important if you're looking at errors. So if
errors can occur throughout and then you're using it fur and you're looking at the error
value, you need to be a little bit careful. But one, nice thing you can do is you can
use an anonymous functional.
Like this to actually look at the error so let's just do this. Imagine there's an error that can occur in here.
Like this. And I'm going to make this function to fur, which is going to on
exit. Look at that error.
And if there is an error, I'm going to use log. I'm going to say, you know, I don't just
use print out an error occurred.
And so, we go through.
And so, because this is anonymous, there's no this. Nothing to evaluate. Here's
this whole thing. Will go to evaluate it on exit. And so now if I go into here and I say
error equals something bad thing
happened.
I'm going to clear the errors Library.
Then I'm going to need log as well. I don't know. But it looks like
then let's run that bam, I need to remember that we
actually have to call it.
They're an error occurred, even though Arrow was set after the defer
because it goes. Now, what you're doing is you're looking inside there. So this can be very, very helpful,
especially in error situations, where you may want to login error, you don't have to make
sure every instance where you've set the area, you're actually logging it.
The other thing you can do with errors and this is kind of Handy, is format actually has a nice
function called error F, which will actually create an error. So you can do
that and that voice you having to bring in the arrows package. And of course, because it's
an
F function allows you to give a lot more information in there, just like you will be the
Sprint are for printf, so that's really the basics for defer,
just remember that the arguments get evaluated immediately, the function itself gets
evaluated, when the defer executes
So as you will obviously know as a programmer and you spent half your time dealing with errors and error
handling and in go of course there is an error type and
there are various different ways of creating errors, obviously
most of the time you may well be just looking at errors coming back from functions you're calling. But
occasionally you need to generate your own and there are a few different ways of doing that
and so I'm going to take you through how to generate errors. So obviously
within go. There's this built-in type error, which I'm going to look at in a minute, is
actually an
Face type and it has just has one function it. But for the moment, let's pretend it.
We don't know the details. So obviously we have an error and if we want to
generate an error with it, a name, a string in it. There are two really simple ways to do it. So we
can do the following. We can use the errors package and that
allows us to do something like, you know, fatal
exception or something.
and if I bring in errors,
then suppose that's happened and I want to print it out
Let's just do that. So I've created something like that and go
run, okay, so there you go. So there's that particular error and this is an
error, it could be return from a function requires an error, it is of the
of the error type. Now it's a little bit inflexible because it just takes a
string and maybe you want to include some more information in there and within the format
package there's a nice little function. It's just ask your doctor about it.
Our F which is a standard print F Type function and it returns an
arrow returns something that satisfies the air interface. So we could use that
perhaps so you can do error F. And then we can do you know
a problem occurred with and something
and thing and let's just say thing
equals here, my module
So that's just going to create a standard thing and I'm no longer need the errors package.
And run that it from Coburn my module. So that gives you a way to create an error
string. That's great. If you want to pass around, just a
string and you don't need the rest of your program to know, what type of error it is.
But sometimes it's handy to know the type of error and the way
that the simplest way to do that within within one of your packages is to declare
something. So we could say so because
they are ER bad start up this something
failed on Startup,
Then just do it within. Here we go. Areas new failed to start
correctly.
And then obviously, that thing is an error. I need the errors package again.
And I can go in here and just go error. Equals error, bad
startup.
Whoops, when the my braces here,
right? So are Phone Stock correctly, the other good thing is I can actually do things like
this.
I'm gonna do something that's actually be. There's just she cheat and say, we're going
to ignore that.
Right? So no longer prints that because it knows that particular thing and this is extremely useful if those
errors are declared with a capital letter at the beginning so I public in some
package so if you have some package where you have some errors, you want to work with,
then you can do these comparisons and know the type of the error without having to do,
silly things with string matching. So that's that's the Tuda. Basic
thing now error itself is actually just an interface
and in fact it's just an interface like this, it's type error interface.
And it has an error function and the error function Returns the string.
So anything that has an error function is an error, so we could take any sort of
type and add that to it in. This can be particularly useful if you wanted to
augment something with extra information about the type of error so
we could have a type. So my error
and for example, maybe we will, maybe
there's a there's obviously a string which tells us what the error is but there also might
Be a time when it happened. So we could bring in the time
package and we can say, when this error occurred and then we come into here and we could say,
right, I just need to declare error on that. So
my error has to have an error string.
And it has to return a string and then we could use that to
printf out when this occurred. So we could go in here and say
this that happened at and then we can say when
so it go in here, I could say when
and,
We'll go into here and there will create one of these. Let's just do that.
So Eric only calls my error.
And we can set it string, too.
Bad thing when equals timed on now.
So we've got all those things in place and then this thing is in
fact, an error. So we could return it whenever the error type is
needed and we could use it within other functions that require errors. So
what's good here is that as soon as you declare that you are an error within go and you can create
something that's much more structured than you might do just with a simple
string. And you'll see this used quite a lot within the go standard.
Particularly in OS package with things like path errors and other types of Errors, which defined which
have more information. So the error function needs to actually return a
string. So we fix that to use printf and now that we've got this error we
can print it out loud with might do with any other error so I'm just going to Simply do
that. That's going to call the appropriate error function to get a string.
And what we'll see is that we get bad thing which was
the name of the error and actually the time we recording this right now
and so
are you've got a compound error if you like which has more information than just one created with format dot
error are for errors, dot new