In this section, I want to talk a little bit about idiomatic. Go, if you've
looked at go programs you will have noticed that they all look very similar.
And there are a couple of reasons for this. I think one of them is of course
to go format tool, which forces a certain coding
style, which of course, is actually based on the language itself, which has certain restrictions.
And the other one is that the language is fairly small. It has a fairly
small number of keywords, for example, and that people are quickly, learn the
entire language.
's. And on top of that, there is a certain idiomatic go, which you'll see in
the packages, particularly in the standard packages and written by other people.
Obviously some of that is enforced by go format but some of it is also up to the
programmer and I think it's useful to talk about a few things you'll see
in well-written go.
One particular thing you'll see is an attempt to keep the main flow of
code at the lowest level of indentation. Sometimes,
that doesn't happen and it can make code very different difficult to read. For
example, let's look at something which is going to run
through a sequence of do something and then otherwise get an error.
So it's not uncommon to see. People do something like this,
let's suppose. We're going to open the file. So let's just look at it.
This guy's we're going to open up a file and it returns a file and an error. So we're going to do something like
there's we're going to say OS dot open.
Some file. And if
if everything's alright.
Then well actually often you'll do this if everything's gone wrong then
we're done. Otherwise everything is all right. And then we're going to start doing something with f. So we
might say
So have a look at what we're going to do. For example, we might go through and start reading
from from that. And we may do something like, okay, well if we get another
error and we do F dot etc, etc. And then
you get this long indentation of F's like this.
This definitely not idiomatic. Go, what would typically see
here is the reason people do this is because of the scope of f, they've done this
declaration here and then F course is not valid outside of this
block. And so then they get tempted to make a long chain of else's
in idiomatic, go. What you would do is completely different, you would say here
you would declare that.
Now f is valid everywhere, your
error return happens like this. So if anything goes wrong you immediately are returning and
giving up or logging in error or whatever is appropriate and then you use
f here. So this way, you're keeping
everything as far to the left as possible along the main flow. Once you get a large
indentation, it can be very, very messy to see it.
Another thing, you sometimes see is a sequence of F's, where
you could be better to use,
Which statement so you might see something like this.
So there's some some value in which is an integer. And you say, you know, if n
is less than 0, then we do something else.
If n is greater than 0, then do something else.
If n is equal to zero
and this is relatively easy to read, but a switch statement is even clearer.
If you look at it like this,
So, the switch keeps absolutely everything grouped together. You can easily could see the case.
There's less syntax to look at, see what the actual switch statement cases are.
So if you find yourself writing a long chain of effort of our users
switch statements, instead these will fall through and he's acting the same way that
the else operates. The sense that if this case doesn't apply the next one, will the next one. The next one.
Well, and it's much cleaner. It's much less horizontal space used up.
Another thing you'll find yourself working with quite widely and go programs is
time. And luckily go, it has a very nice standard package called
time and within that there is a type, which is a
duration and so, we can just take a look at that. And
adoration is actually a 10 in 64, underneath is actually
representing nanoseconds. But what's good about it? Is that you
can use it throughout your program to represent any time value without any
Fusion. And I know that personally I've been confused in the past
and introduce the horrible bug because I assume there's something that was an end was in
microseconds. It was actually in milliseconds, so don't do the following
kind of thing. Timeout, equals
1000.
So, a very common thing would be somebody to write that and put in comments that it is
milliseconds. And then later on having forgotten that someone says, okay, I
need to do a conversion on that. I'll multiply it by a thousand and now you've got a
second and your timeout might be enormous at that point, if far
safer thing to do is to use the time values. So, if you really
want a millisecond, you can just use time.
And you can come in and say one millisecond and then it's
unambiguous what that timeout is meant to be and what the actual
value is intended to be. Sometimes you'll see
durations like this on the command line. And if you take a look at the flag back
Edge,
Within here, there is a duration value
and it will take a value on the command line and convert it into a
duration. And what's really nice about this is it will pass it and so
you can actually type something meaningful on the on the command line. It
uses a thing called
Time pause duration. And this is a function that allows you to type in something
like 300 milliseconds, or 2 hours 45 minutes. So let's just
do a quick example of that. Let's bring in the flag
package.
And we're going to define a timeout variable. I'm going
to use the flag package itself. So let's go back to
so there's this duration, think so, Gosei flag a
duration and we're going to call it time out, they cannot then we're going to give it a
default value, so we give it a millisecond and I
quite like to type 1 times, even those are necessary just to make it really clear that
I meant one millisecond and I'm going to say
timeout value and then we'll pause pause the flags
and then we'll say timeout is and then
Allow the time package to print out. So like that. So
fairly simple little program is going to pass something. It'll be a millisecond
if I don't type anything. That means it's optional and I will that's a
pointer as well. Just make sure you do that.
So the default timeout is one millisecond as you can see, the default string over the time package
tends to print things out, rather nicely and I'm just going to turn this into an
actual programs. And I've got a food program there and I'm going to
say time out equals 1 hour. So now
it's one hour and 300 milliseconds,
200 nanoseconds. So that that is much, much better than spending
any time tapping in integers and then converting them and making comments about
it. Use those
His duration things that are in the time package and in the flag package,
and you'll find you don't have any headaches with the way in which we're doing times and timeouts.
So, if you've been using go for a little while, you'll
know very, well that there are no exceptions in go, you everything is done by
returning errors and go has a fairly simple
style for returning errors. There's the error type and
it is expected that functions return errors and not other ways of indicating
a problem. There is also, of course, panic and recover
which some people like to use the basic rule of
panic and recover is you never
Use them. And if you find yourself tempted to use them, then
it's probably the case that you're thinking, in a non idiomatic go
fashion. So I just want to show you a few ways of returning arrows and a few
things not to do, I'm definitely not going to show you panic and recover their
things, you would not ever use. So, the standard way
returning, an error from a go function is to return a return
value. So let's have a cell do something
and it returns an error. If something
Goes wrong. And then in Maine we can pull out that error.
So, that would be familiar to anybody who's written any go code. And the certainly anybody who's looked at the
packages, will see that. And if do something needed to return something
else, then it might return some other values. So, you know,
my type and an error. And so, whenever your attorney
errors, the truly dramatic thing to do is to return an error value,
what you should definitely not do, is return a null pointer. So if you're
coming from, say see you,
Do something like this. You might say, okay, well what happens is if
all goes well
Then, you know, we're going to return some subtype.
Like this, if something.
Goes wrong, return nil, although go
has nil pointers. They should not be used for returning errors in this
instance, if you want to return an error and you're also returning a pointer,
then do this.
So something's gone wrong. You return nil for the actual type because you haven't actually got one
and some sort of error. So we see there is 2
And here, there's no error, so you can return nil.
So, always always return an error and don't return a pointer
to a nil pointer or even worse. Something else
like a Boolean saying, you know, failed.
So that that also is not idiomatic
always use an ill error, or an error itself to return an error.
If you spend any time looking straight, as it goes down to packages, you'll notice that lots of
things Implement certain common interfaces. For example, there's a
Stringer interface, which turns any struct or other
thing into a string, which is useful for printing. And in fact, things like
printf, use the Stringer interface to print something, there's
also IO reader and I writer for reading and writing and in general,
if you're creating your own structure and types, it can be
useful to implement some of the
Interfaces because many things will consume those standard interfaces.
So, for example, if I made a simple type, let's call it
pair with just as a pair of strings,
and I created one of those.
So, I can say, pair X is
X, x X & Y is y. So I create one of these
pair things and then I went to print it.
So I can just print that out.
Stop. It's not a string is the strapped. Is
neuticles. Okay, so you get a Standard Printing of that thing. What's happened
here is that go has tried to print out the structure by printing out the elements within
it but maybe that's not what you really want. Maybe you want something better. So
Implement one of the standard interfaces which is the Stringer interface and so
we do that by just defining a method and the method is
string that is the Stringer interface just returns a string
form of this.
And I can just do that with.
S printf which turns interesting. And I can will say, let's print
out. Each of the individual elements like this,
So P dot X, PW Y,
and we won that. And now
there's all this changed here is this, interface is available and now
I can write it out like that. So having implemented the Stringer
interface anywhere that a string is required. It becomes automatic
another way in which interfaces are useful is to use them as parameters
to functions. For example, it might be tempting to write a
function that loads the values in this string from a file. So, you
Might be say, I've haven't given a pair. I'm going to load X and I'm going
to load it from some file, a return, an error,
if that happens, that's fine. And you could certainly do that, but it
restricts you to using an actual file. A better way to do,
that would be to use an IO reader, then anything that can be read
from can be put in there, including an OS file. So, now, as file will
work as well. Now, the reason that might be particularly important is that when you come to
write your test suite for these,
Visual functions, you can substitute something like a byte buffer which you filled with
test data for an actual file and you're not actually messing around with files on the
file system. So interfaces will give you a great deal of flexibility in
how your structures actually operate without the tying yourself to
particular, concrete types.
Another aspect of go idiomatic, go
use how to write comments. And if you look at the Go packages, again, they're always
a good source of what sort of go. Should I be writing? You'll see a very
distinct style in terms of the way in which packages
functions and structures or types are commented and it's worth. Just
going over that very briefly so that you end up writing similar sorts of
code. This is also very important because go doc or use that
those comments to actually display.
Information about packages that you yourself create. And so using the same style makes
everything feel very consistent. So let's suppose we make a package
to represent a dinosaur. So we Sol have something like
package dinosaur. The first thing you'd have is you'd have a package
comment. And this always comes right before package of the top and you would
typically would start something like this package dinosaur and then an English
sentence that describes what it is for. So we could say something like that
provides.
Functions for creating and manipulating.
Dinosaurs. So something like that and
so that becomes very clear. That wouldn't go. Doc will come out as the actual documentation
for that particular package. And then within it, of course you might have some type so we might have
a dinosaur
Which will be closed until probably have a name and it might have a
weight and it might have whether it's a
herbivore Etc. And the way in which this would get
commented is with again with an English sentence but with a very
distinct style, it would always start with something like a
dinosaur is and then go on to describe what it is.
So a single
Dinosaur with name, wait.
And weather.
It is.
A herbivore. So always your writing English sentences for these things and
let's suppose the on a dinosaur, you had a particular method. So
let's have a dinosaur method and this is a dynasty this will
output for example the name of the dinosaur. So right
name is going to write the name to an IO writer.
Return an error Topsy. Prevent that. And
these always also have a very similar style. These always start, actually, with the same name as the
particular method. So, right name, write the name
of the
dinosaur to the provided IO writer.
So, throughout the packages, you will see this style of package level,
comment structures being commented with a something like that, proper English and
then individual methods being commented in the same way. And if you follow the same style in your
own code, what will happen is that though doc will display it correctly and also your code will
look like other go code and a big aim of idiomatic go and go
format and even the packages that come with go is to give
all go code of some of the
Dial. So, there's no difficulty reading someone else's code, so if you do this, you'll find that others can
quickly. Pick up what your package does.