Introduction
Welcome to this comprehensive Go programming course for beginners! In this course, you'll learn the key concepts and techniques to write performant, idiomatic Go code. Guided by experts Lane Wagner and Alan Lers, who have over 18 years combined coding experience, you'll master topics such as variables, functions, loops, and much more.
Course Highlights
- Hands-On Coding Lessons: Get hands-on experience with over 100 coding lessons and exercises.
- Real-World Projects: Apply your newfound skills by building seven real-world projects, including an RSS aggregator and implementing authentication with API keys.
- Scalable Infrastructure: Discover why Go has exploded in popularity among modern tech companies for building scalable backend infrastructure.
Why Choose Go?
Go's rapidly growing popularity makes it a crucial tool for any aspiring developer. Low complexity, great performance, and a fantastic developer experience make Go an excellent language to learn. Let's delve into why Go could be a game changer for your coding career:
- Fast Execution: Go is a compiled language, outperforming common interpreted languages.
- Compiling Efficiency: Go's fast compile times increase developer productivity significantly.
- Low Memory Usage: Processes run more efficiently utilizing less memory compared to languages like Java.
Interactive Learning Approach
Course Structure
The course starts with over 100 hands-on coding lessons. Towards the end, learners will build a production-ready backend server from scratch.
Practical Tools and Resources
- Free Resources: Utilize Discord chat groups, forums, and GitHub repositories provided in the course for additional resources and troubleshooting.
- Personalized Support: Connect with the instructors through social media platforms like Twitter or YouTube.
Getting Started with Go
Setting Up Your Environment
Before diving in, ensure you have the necessary development tools:
- Go Language: Install the Go programming language on your computer.
- Text Editor: Use an IDE or text editor like VS Code for building your applications.
- Command Line Terminal: Familiarize yourself with using command line tools to navigate and execute commands.
Introduction to Basic Syntax
Let's cover Go's foundational syntax and functionality:
- Variables: Understand how to declare and use variables, responding to data types efficiently.
- Control Structures: Explore loops, conditional statements, and functions.
- Packages and Modules: Learn about Go's package management system.
- Concurrent Programming: Understanding goroutines and how they boost Go's performance.
Advanced Programming Concepts
As you advance through the course, you'll explore more intricate aspects of Go programming:
- Error Handling: Learn best practices for managing errors in your Go applications.
- Concurrency: Understand the concurrency model with goroutines and channels.
- Structs and Interfaces: Use structs to define complex data types and interfaces to define behavior.
- Generics: Discover the power of generics in Go, introduced in version 1.20, helping you write less repetitive code.
Building Real-World Applications
Using the skills you've learned, you will create:
- An RSS aggregator to fetch and display content from various feeds.
- Implement user authentication and create both user and feed management capabilities.
Conclusion
This course equips you with everything you need to succeed in using Go programming effectively. Whether you aim to build innovative applications, enhance your career prospects, or simplify everyday software development tasks, Go will give you the tools to realize these goals. Dive in and start your journey with Go today!
welcome to this comprehensive go programming course for beginners throughout this course you'll learn key
Concepts and techniques to write performant idiomatic go code you will be guided by the expertise of Lane Wagner
and Alan lers who combined have over 18 years of coding experience alongside mastering topics such as variables
function loops and more you'll also have the opportunity to apply your new found skills in seven real world projects
ranging from building an RSS aggregator to implementing authentication with API keys so get ready to unlock the
incredible potential of go go has been exploding in popularity recently it feels like all the most modern tech
companies are using go to build scalable backend infrastructure it actually makes a lot of sense Go's fast lightweight has
an amazing developer experience and is actually super easy to learn stick around and in just a few minutes I'll
explain the rest of the reasons why go could be a game changer for your coding career at this point I'll just introduce
myself really quickly I'm Lane the founder of boot. Dev and I've been writing go for a little over 7 years and
I've been building software for about 10 I've actually spent over 2 years designing this go course and I've taught
thousands of students with this material the feedback from all of my students over the last couple years is actually
been Incorporated in the course so everything is very battle tested and up todate I was actually just making some
updates yesterday so how does this course actually work well we're going to start by doing over 100 handson coding
lessons and exercises now when we're done with all of that you'll actually have a really strong grasp on the
fundamentals so at that point we'll go build a production ready back-end server Ino from scratch now I'm begging you
please do not binge watch this video tutorial hell is a very real place and it's a place that you will go if you
don't write your own code get your hands on the keyboard and write some code with me in fact you should actually be coding
ahead of me and only using my Solutions when you get stuck so head over to boot. and create a free account that's where
all of the code samples for this course are hosted now alternatively I have linked a GitHub repo in the description
below all of the raw code for the samples in this course are hosted there it won't be quite a streamline of an
option but it is an option now you should also know that this course is just one part of the full back-end
developer career path over on boot. so if you're interested in going from zero to hired as a backend developer you
should definitely check that out too now as long as we're talking about external resources know that if you get stuck
during this course you have some options for help first you've got the boot. deev Discord second you've got the free code
Camp Discord and third you've got the free code Camp Forum I will link all of those down in the description below okay
one last thing before we jump into the course if you want to connect with me personally or you want access to my
other go and backend content then you can follow me on Twitter wag lane or you can subscribe to my YouTube channel at
boot. I'll link both of those down in the description below with all of that out of the way let's talk about go
everyone always wants to talk about how fast the go programming language is let's talk about it and let's compare go
to some of the other more popular programming languages so in terms of execution speed
execution speed go is much faster than JavaScript python Ruby and PHP pretty much any
interpreted language a language that's not compiled is going to be slower than go because go is a compiled language now
don't worry too much if you're not familiar with the terms compiled and interpreted we'll talk about those in
just a minute for now just understand that go is much faster um than these languages when it comes to executing
programs if we're doing computationally heavy work go is going to be much more performant um than some of these other
languages now here on the other side I've listed some compiled languages so when it comes to compilation
speed compilation speed go is actually much faster than these compiled languages when it comes
to compiling the code now again we'll talk about compiling in a minute but for now just understand that you have to
compile your code before you can can run it when you're working with a compiled language and so by having a fast
compilation speed like go has it actually increases developer productivity quite a bit we can iterate
more quickly on our code we can deploy it more quickly it's not as expensive to run tests to compile uh the program and
deploy it to production um so this is actually a huge benefit that go has now I do want to point out that go does not
necessarily run faster it does not necessarily have a faster execution speed than all of these languages but it
does beat them handily when it comes to compiling not to beat a dead horse but I want to talk a little bit more about
execution speed so we talked about how go is generally faster than the interpreted languages right python
JavaScript Ruby PHP and so on it gets a little interesting when we compare go to kind of the natively compiled languages
or the languages that compile directly to uh kind of machine code that runs on your CPU versus the compiled languages
that run on top of a virtual machine right so the the two big ones that run on a virtual machine are Java and C
while some of the compiled or natively compiled languages that you'll be familiar with might be rust C C++ even
though go is a natively compiled language a language that compiles directly to machine code like rust C and
C++ its execution speed is actually more similar to Java and C when it comes to its runtime speed how many computations
it can do kind of per second and we'll talk more about this later but the primary reason for that is the go run
time there's basically a chunk of code that's included in every go program that manages memory and that tends to slow
down the execution speed a little bit that said it is worth pointing out that a go program tends to use much less
memory than Java and csharp because there isn't a need for an entire virtual machine here we are the first coding
challenge let me break down what we are supposed to do and then I'll kind of explain what this code over on the right
actually does so um our assignment is to log the string starting textio server to the console instead of hello world so
throughout this course we'll be building out little pieces of the textio product which if you're familiar with twilio
it'll kind of be like a twilio clone it's a kind of backend server that sends SMS and email messages and works a lot
with kind of text textual data every file of go code has a package declaration at the top here we have
package main simply because this program builds into an executable go program right so we can run this code kind of
Standalone the next line is importing the fmt package from the standard Library we are importing it because
we're using it down here within the main function now the main function is the entry point to the program so every go
program starts exec ution at the top of the main function which is just a function named main that takes no inputs
and doesn't return anything on line six and seven we have some single line comments these don't execute they are
not part of the program they're just there for documentation and single line comments just start with that double
slack finally on line eight we have the one thing that this program actually does which is print the string hello
world to the console so let me go ahead and run that see down here it printed to the
console it's using the stand libraries fmt package um and the print line function that is exposed from that
package uh to do so so the assignment here is pretty simple uh we're just supposed to swap out that hello world
message for starting Tex iio server going to run that and make sure it looks like what I would
expect we're good to go next let's fix a quick bug I love quick wins um we're not going to talk about all of this syntax
in this program what we're interested in is just fixing the math bug on line 17 so the assignment description says texo
users are reporting that we're billing them for wildly inaccurate amounts they're supposed to be build
for2 for each text message they send something else is happening so the total cost here is being set to the cost
per message plus the number of messages now that doesn't make a lot of sense to me go ahead and pause the video see if
you can figure this one out on your own um to me it looks like the cost per message should be multiplied by the
number of messages right and let me let me run it like this so that we can see Doris spent 4.02 on text messages
today four messages that doesn't make sense right for four messages she should not be
build $42 if each message only cost 2 cents so I'm going to go ahead and change that to multiply I would expect 8
cents yep Dora spent 8 cents on text messages today cool going to go ahead and submit that this is one of my
favorite XKCD Comics uh you can pause and read it uh really quick if you'd like slow and resource expensive
compilation times a really terrible thing to work with I've worked on systems in Java and C++ that took over
an hour to compile the code that means that if we find a bug and we want to deploy it to production even if we get
the bug fixed within five minutes it's still going to take an hour just to build the new production version so that
we can deploy it on our servers I've personally never worked on a go program that's taken more than just a couple of
seconds to build and compile so the first question is go code generally runs blank than interpreted languages and
compiles blank than other compiled languages like C and rust so the answer is going to be faster and faster right
it runs faster than inter most interpreted languages and it compil Iles faster than most compiled
languages so the question on this one is does go generally execute faster than rust the answer is going to be no so
I've thrown around this word compilation or compiled a few times but we haven't really talked about what it means when
we write code we write it in a human readable format right typically in a file in the
case of go A.O file right maybe main.go and this file is going to contain human readable text right go code that
we as developers work on the thing is your computer's Hardware doesn't know what any of that human readable text
means your computer's CPU only understands binary right which is ones and
zeros right which at the end of the day is just numbers right but it's just numbers and simple operations things
like add and subtract so we need some process that we can use to convert human readable code to machine code that can
be executed by the computer's hardware and that's all that compilation is compilation is just the process of
taking some human readable code right go code in our instance and converting it to Binary or machine code that our
computer can actually understand so what does this process actually look like Well normally you'd start by writing
your go program so say main.go and you'd run it through the go compiler on your command line you'd type go
build and that would produce a new file which is the executable program let's say we're doing a hello word a hello
world program it might be hello world.exe
right an executable program on your computer and you could run that executable directly on your operating
system without ever having to use the go tool chain again so the great thing is you can take this hello world.exe
program and give it to someone else and they can run it on their computer without ever having to install the go
tool chain or even know that you used go to build the program in the first place this is different than languages like
python where if you want to run someone's python code you have to use the python interpreter every single time
time and you run the source code you run the source code directly on your machine it's also worth pointing out that part
of the reason that compiling is so much faster at runtime is that we do all of this compiling work upfront so when we
go to run the executable we don't have to do any conversions from Human readable text to Binary machine code
that's different than how interpreted languages work with an interpreted language as we run the program The
Interpreter is reading the human code and kind of at runtime converting it to machine code that the CPU can operate on
so you might be wondering where is the compiling Happening Here on botev well we actually do the compiling and the
running at the same time for you so when you click the Run button um we're actually taking your code shipping it
off to our servers compiling it running it and giving you the result don't worry when we get down the road to the actual
project you'll be building and running your own go code on your own machine but we can see the difference between a
compiler error and a runtime error even here on Bev so for example um here in this code the assignment says to pass
this exercise fix the compiler error in the code so I'm just going to run it as is and you'll see we get this nasty
error here and it says main.go right line six syntax error this is actually a compile time error so we weren't even
able to compile this code it didn't fail at runtime it failed compile time right which again that distinction will become
more clear a little bit later so anyways um we can fix it by adding that Clos parentheses that was
missing and now it compiles and runs just fine so to review go code like this
little go program here that prints hello world that's not understood directly by the hardware on your computer your
computer's processor or your computer's CPU understands machine codee so we need to
take our human readable go code run it through the go compiler to produce the machine code that we can run directly on
our CPU right the CPU is designed by the manufacturer to run a specific format of binary cool so the question for this
exercise is do computer processors understand English instructions like open the
browser no processors are not chat GPT uh they need machine code now we touched on this very briefly
before but I want to talk about how you distribute a compiled program versus how you would distribute an interpreted
program so let's say that you'd written a script in Python so you've got this script main.py right this is raw python
code and you want to give it to your friend so that they can run your script well all you would do with an
interpreted language is give your friend the main.py file and then on their computer they would run the command
Python main.py and they'd be able to run your code now there are a couple of downsides
to this approach the first problem is that your friend needs to have python installed on their computer so your your
friend being able to use your program is dependent on them already having python installed it's even dependent on them
knowing how to use it or knowing how to use a command line right so Distributing the programs of interpreted languages
can be tricky because it's really only useful if you're Distributing to other developers who know how to use these
tools already the other problem is the code itself let's say you spent you know many weeks writing this python script
and it's super useful and you're trying to sell it to customers if you just give them your python code I mean they
effectively own it they can change it even if you didn't intend for it be open source congratulations it's now open
source the problem is you can't really allow someone to use your program without giving them all of the Special
Sauce that makes it work let's review how this works in a language like go so in go we'd start the same way right we'd
write some human readable go code in a file called main.go but instead of giving that file to our friend we're
going to compile it first going to compile it we'd use the go go tool chain so we'd
write something like go build in our command line and that would produce a new executable file right so this is
this is machine code here um let's just say that the name of our program is Hello World why
not so if we're on Windows it might be hello world.exe right so when you go online and you
download a program to use say it's a video game um it is probably a bundle of machine code it is a built
binary right in order to run your favorite video game let's say Starcraft 2 or World of Warcraft you don't need to
install the C++ compiler right you're just given the built kind of final product the final executable program and
that's what we're doing in uh the go programming language as well so now we can give our friend this
executable they don't need to install go and they do not need access to the original source code so generally
speaking Distributing programs that are natively compiled is much much easier um than Distributing programs that kind of
have a runtime dependency like an interpreter so to answer the question for this exercise do users of compiled
programs need access to the source code no they don't and a related question which language is interpreted we've got
go C++ Python and rust and the answer is python the rest are all compiled so now the question is why is it generally more
simple to deploy a compiled serers side program or backend application and the answers are compiled code is more memory
efficient because Docker exists there are no runtime language dependencies or because compiled code is
faster well it's just the same as what we talked about earlier um when we deploy to a server say on the cloud uh
we have to have all of the dependencies uh that our backend application needs in order to run installed on that server
and if there are no runtime dependencies if all we need is a compiled binary then that's arguably the simplest way to do
it right so the answer is it's more simple to deploy a comp to deploy a compiled program to a back-end server if
there are no runtime language dependencies things like the python interpreter or The nodejs Interpreter
for example go is strongly typed and statically typed and that's a really good thing if you've been paying any
attention at all to the JavaScript World you'll notice that a lot of JavaScript developers are making the switch to
typescript and that's primarily to get access to static typing a lot goes into typing and type systems but one of the
biggest benefits of a static type system like goes is that when we declare a string right like say this username
string that I've set equal to wag Lane we can't later accidentally change it to an integer a number right like a float
64 um it's going to stay a string the nice thing about a static type system like we have with go or that you have
with typescript is that we get feedback on our errors uh much more quickly right rather than finding out about a bug when
our code is running in production we find out about the bug say when we compile our code let's move on to the
assignment the the assignment says we'll be using basic authentication for the text iio API so basic authentication is
is just this format here where you've got a username and a password um in an HTTP header it tells the server on each
individual request to the server who you are right it's kind of like logging in um or rather it's kind of like being
logged in okay uh so the code on the right has a type error change the type of password to a string Okay cool so I'm
just going to run it to see the type error first so invalid operation username plus colon plus password has
mismatch types of string and int now this is a compile time error we we weren't even able to compile this code
right let alone run it in production um so we've been instructed in the assignment change the type of password
to a string but use the same numeric value so that it can be concatenated with the username variable Okay cool so
I'm just going to change this to a string by surrounding in double quotes that's how you do strings and go
pretty similar to other languages and then I'm just going to change that type to a string type and run it again
authorization basic wag Lane colon number that looks good to me when we're talking about the
performance of a programming language or an application we really care about how it performs across two different axes
one is speed how fast it can do computations right which is kind of measured in CPU cycles and then we also
have memory consumption which is just how bloaty the program is how much data it has to store in memory to be able to
do those computations every program you write no matter the language is going to be using memory every time you create a
new variable it allocates space in memory where it can store that variable's data now in languages like
rust or C memory management is effectively manual now that said Russ does have some nice tooling that kind of
takes care of it for you at compile time so it's not quite as error prone um as as it would be in C or C++ but at the
end of the day your program is allocating memory right it's saying this chunk of memory I'm going to use I'm
going to store some variable data here in this bit of RAM and then later your program says I'm not using it anymore um
so we can free it up for use by other programs now let's jump over to Java so with Java it's a little bit different um
Java is a garbage CL CED language garbage collection which essentially means that memory management is
automated and in Java it's done by the Java virtual machine so every time you run a Java program you're actually
creating an entire mini virtual machine that your Java bite code runs
within so this is the jvm and then you kind of insert your code
into the jvm and you run your code in there and the jvm is what takes care of allocating
and freeing all of the memory that you use and this creates overhead basically at the end of the day Java programs use
quite a bit more memory than rust or C programs go is in an interesting sort of in between world where go is a garbage
collected language like Java so it has automated automated memory management but it does not have a jvm when you
compile go code rather than having to run it within a jvm just like with Rus and C you get one binary or one
executable program the difference is that go includes a runtime a
runtime within every single binary that's built using the go programming language so we could think of it as
something like this it's kind of like a little side car that is compiled
alongside your code so your go program basically has this this little bit of extra code that's added to it and that
bit of code is what handles garbage collection and automated memory management so it's a little more bloaty
than what you'd get with rust and C right it is garbage collected um but it's not nearly as um expensive in terms
of memory overhead as a language like Java or C let's take a look at what some actual numbers look like I pulled this
chart from Dexter darwick uh blog and if you're following Along on botev you can click the link to go check out the full
the full blog and the description of this experiment but basically he built a restful web server in three different
programming languages right in Java go and rust and then me measured the memory consumption and when the servers were
just at rest effectively doing nothing and waiting for requests to come in Rust used less than half of the memory that
go was using and go used 100 times less memory right measured in megabytes than the jvm was using to run the rest
service so to order the three languages in terms of memory efficiency I would say Java is the least efficient uh go is
in the middle and rust would be the most memory efficient and it's also worth pointing out just really quickly that as
the load on the server increases I would expect go to have more similar performance to Java in other words we
wouldn't see as quite a large discrepancy here right go would likely still be more memory efficient than Java
almost certainly in fact um but it probably wouldn't be a 100x discrepancy we're seeing this huge discrepancy um
mostly because this is an idol uh a test of an idle program so generally speaking which language uses more memory that's
going to be Java another question on the same topic is what's one of the purposes of the go runtime so to style go code
and make it easier to read that doesn't make sense that would be like compile time tooling right not run time tooling
to clean up unused memory to cook fried chicken or to compile go- code so it's definitely going to be to clean up
unused memory and if you remember remember the runtime is just that little bit of extra code that's included in
every compiled go program that among other things handles memory management so we've already talked about
how go has strong and static types but we haven't yet talked about what those types are you're probably already
familiar with Boolean values and string values which are valid go types and also exist in pretty much every other
programming language um numbers are where it first starts to get a little bit different uh if go is your first
compiled programming language generally speaking numbers fall into four different buckets we have integers
unsigned integers or uints floats and complex numbers integers are just whole numbers they can be positive or negative
right 1 2 3 4 you get the idea unsigned integers are the same as integers but they're not signed which basically just
means they don't have a negative component you can only represent positive numbers in in an unsigned
integer you probably already familiar with the idea of floats it's just fractional numbers right numbers that
have a fractional component things like 1.21 or 3.14 complex numbers are a little funny
they're used to represent the concept of imaginary numbers if you've gotten to imaginary numbers in your math studies
I've never actually used complex numbers um in production I'm sure there are plenty of use cases for them but we're
not going to go into detail on how they work here you can certainly go read up on it if you'd like the only other thing
worth mentioning is that size matters when it comes to types A uint 8 and a uint 16 are two different types they
both represent unsigned integers but a u 16 has twice as much room for data within it it has 16 bits of data whereas
a u 8 only has eight bits for example the largest number that you can store in a uint 8 is 255 because that's the
largest number you can represent with eight 1es and zeros in binary but with a uint 16 the largest number you can store
is about 65,000 again because that's the biggest number that can be represented by 16
binary digits just like bigger number better person uh bigger number in your types means you can represent more
possible values uh within that type so float 64 can represent more values than a float 32 the only reason you wouldn't
use a larger type is if you're trying to save on memory if you're trying to write a program that is hyper performant
you'll want to use a smaller size right if you know that an integer is only ever going to store three different values
say 1 two or three then you might consider using a u and 8 the bite type is an interesting one and it's one that
you'll use a lot especially when you're say marshalling a Json object um to be sent across a network connection or
maybe you're reading to and from a file on disk um but under the hood a bite is just an Alias for the uint8 type which
makes sense right a bite is just eight binary digits eight bits and that's all a uint 8 is a rune is a Unicode code
point which generally speaking you can think of as one character in a string and that's usually how it's used under
the hood it's just an alias for the int32 type moving on to the assignment it says initialize the given variables
to int float 64 bu and string respectively with their zero values and um as we can see here if we just use VAR
the name of the variable and the type that should do it so we'll initialize the variables here so
VAR SMS sending limit is an INT VAR cost per SMS is a float 64
has as permission is a and username
is a string okay cool and all so all of these variables are now instantiated and should contain their zero value so for
example zero uh 0.0 false and empty string so let's go ahead and run that and that looks correct to me I'm
going to go ahead and submit it we've been declaring variables the hard way now we're going to do it the easy way
there is an operator in go colon equals that is the short assignment operator and it allows us to to declare variables
and have go infer their type so instead of typing VAR empty string we can just say empty colon equals the empty string
and go knows that this has to be a string so it is a string now when we use this short assignment operator we're not
saying this is a loose type that can change in the future it's still a static type empty is a string just like it
would be if we declared it this way in reality in go you will very rarely see variables declared like this you will
almost always see them declared using the short the short assignment operator for example num Carson equals 10 creates
a new variable called num cars and sets it equal to 10 and its type will be inferred to be an INT and the int type
Alias is either int32 or int64 depending on your computer's architecture if you want to specify a particular size of
integer then you would declare it using this kind of lonand syntax so in this assignment we're just meant to declare a
variable named congrats with the value happy birthday using a short variable declaration so as simple as congrats
colon equals the string happy birthday let me run that
perfect this next assignment says our current price to send a text message in texo is 2 cents however it's likely in
the future that the price will have to be a fraction of a penny or have a fractional part to the cost so we should
use a float 64 to store this value edit the pennies per text declaration so that it's inferred by the compiler to be a
float 64 Okay cool so here we're just setting it equal to two if I run that then I get the type of
pennies per text is int and this percent T in go um is is a formatting verb that that tells the go programming language
or or at least the print F function I should say the formatting package from the standard Library um that I want to
print the type of this variable rather than its value so that's why we're saying int there instead of
two um to get a float all we need to do is change it from two to 2.0 I believe 64
cool another handy syntactic Quirk of the go programming language is that we can declare multiple values on the same
line so in this assignment it says declare a float called average open rate and a string called display message on
the same line okay so average open rate display message same line the average open rate
um must be23 and the display message should be the
string is the average open rate of your message and then it looks like this is just going to print them
together so it's going to say 0.23 is the average open rate okay cool let's go ahead and run
that 023 is the average open rate of your messages that looks correct to me so we've already briefly talked about
the different type sizes in go right so we have the int type int 8 int 16 int 32 and int 64 and it's important to
understand that the int type just aliases int32 or int64 um same with the uint type just depending on your cpu's
architecture so you might be on a 32 or a 64-bit machine most modern machines are going to be 64 bits my
recommendation is that unless you have an explicit reason to care about the size right so unless you're trying to
kind of hyper optimize for performance then you should really just stick to these four types int uint float 64 and
again if you're working with imaginary numbers then complex 128 but that's honestly unlikely so so these three
types are going to do the vast majority of the heavy lifting when it comes to working with numbers in go we can also
convert numbers between you know different number types for example we could take a an integer 88 and convert
it to a float like this so it become 88.0 right um converting the other way is a little trickier though because say
we had 88.6 if we were to convert it to an INT we would lose the point 6 we would truncate it down to just 88 now
this assignment says our text AO customers want to know how long they've had accounts with us follow the
instructions in the comment provided you will create a new variable called account age int that will be the
truncated integer version of account age Okay cool so create a new account age int here should be the result of casting
account age to an integer so we'll just do int account age and I would expect that to be two after casting it because
it should truncate the 6 so let's go ahead and run that your account has existed for 2 years yeah that looks good
to me we already briefly touched on this but it's worth mentioning again I
recommend that you stick to the I call them the default types so for example we know there's um you know five or six I
can't think of it off the top of my head now different types of inss right int int 8 int 16 in 32 int 64 I'd recommend
sticking to int unless you have a very good reason to specify a smaller size like int 8 or in32 and that's simply so
you avoid cluttering your code with tons of type conversions that can sometimes even lead to bugs so unless you need a
smaller type for performance reasons just use these default types okay so the question for this assignment is when
should you elect to not use a default type when either a default or a specific size will work when my system has lots
of extra Hardware that I want to utilize or when performance and memory are primary concern so it's going to be
performance if you have performance concerns that's the time I would maybe stray away from the default
types the next question is what does the size of a type indicate so a float 64 what does 64 mean is it bits bites or
nibbles uh the answer is going to be bits now it is worth pointing out nibbles is a real thing so a bite is 8
Bits a nibble is actually Four bits if you didn't know that fun interesting trivia aside from variables go also
supports constants which are immutable values um and in go just like in JavaScript or typescript we use the
const keyword and constants do not support the short declaration syntax so we have to kind of write it all
out okay so getting to the assignment says use two separate constants something weird is happening in this
code what should be happening is that we create two separate constants Premium plan name and basic plan name right now
looks like we're trying to overwrite one of them okay cool so on line six we've got Premium plan name and we're setting
it to Premium plan and then we have Premium plan name again attempting to override the value and set it to a new
string if we try to run this we actually get a compile time error so we're not allowed to mutate constants in go so
what we should be doing is creating a separate constant and it should be named basic plan name let's go ahead and run
that plan Premium plan plan basic plan looks good to me constants in go are not the same as constants in JavaScript and
in typescript in JavaScript and typescript the const keyword really just means you can't reassign to this
variable but you can compute the variable or the variable's value at runtime in go every value
that's stored in a constant must be known or computed at compile time before the program runs so if we create this
new constant called my int and set it equal to 15 within the compiled go binary effectively this symbol my in
just refers to the static number 15 the cool thing is that we can actually compute constants like we can
make constants that depend on other constants but that computation will run when we compile our code not when we run
our code so for example um I can create this constant first name Lane last name Wagner and then I can create a new
constant called full name it's first name plus a space plus and the last name and that's really convenient in case I
ever want to change first name uh now I don't have to change it in two places right full name will automatically
update but this is only valid because all of the inputs to this full name constant first name and last name are
known at compile time so the compiler can still do the thing it wants to do which is replace full name with a static
string right lane space Wagner so on to the assignment says keeping track of time in a message
sending application like textio is critical imagine getting at a point reminder an hour after your doctor's
visit not very helpful right complete the code using a computed constant to print the number of seconds in an hour
okay so we've got the number of seconds in a minute is 60 the number of minutes in an hour is also 60 so how many
seconds are in an hour cool well we could hardcode this as like 60 * 60 but the cool thing is we can actually
compute it because we can say well we know the minutes the number of minutes in an hour and we know the number of
seconds in a minute and if we multiply those two together we should get the number of
seconds in an hour right that looks correct to me so I'm obviously a huge go fan but
formatting strings in go is honestly one of my least favorite features of the language I think it's one of its
weaknesses at the moment who knows maybe it'll improve in the future the way it's done well we essentially have two
different functions provided to us by the standard Library we have print F and S printf printf prints a formatted
string directly to standard out and S printf just Returns the formatted string as a value basically all string
formatting in go currently works the same way we have these formatting verbs things like percent V percent s percent
D and they're replaced in the string template with actual values so for example I am percent V years old the
percent V in this case is replaced by 10 the first parameter uh that comes after the template in the print F function
here we could also replace with a string instead of an integer right I am percent V years old way too many for I am way
too many years old percent V is sort of the default formatter it's usually what you want assume you don't want to print
in kind of the default standard way using percent V then there are a few others um percent S interpolates A
String percent D interpolates an integer in decimal form so for example 10 becomes the number 10 instead of say in
binary form uh percent s is for floats so you can specify the number of kind of places after the decimal point uh that
you want printed out to the console or printed out to The Returned string in the case of s printf um so I actually do
end up using percent F fairly regularly when I'm working with floats so onto the assignment it says create a new variable
called MSG online 9 MSG stands for message of course um it's a string that contains the following High name your
open rate is open rate percent where name is the given name and open rate is the open rate rounded to the nearest
10's place okay so the 10's place is the number right after the decimal so let's get started here message colon equals
fmt dos printf so we're going to use S printf instead of print F because we don't want this value going to standard
out we want it returned from the function so we can save it in the MSG variable
let's just grab this template so we'll use percent s we could use percent s or percent V here because
we're just uh interpolating a string and we'll do percent um .1 F because we want to just print the
first number after the the decimal point right the tenth place okay and then we just pass the two
values as the following parameters or as the last two parameters so name and open rate right so the first value name will
go into the first verb the second value will go into the second verb let's run that see what we get hi s Goodman your
open rate is 30.5% that looks good to me let's talk about conditionals in go so a
conditional is just where we are checking if a condition is true if it is we do one thing if it's not we may do
another so for example uh here's an if statement in go and if this expression evaluates to true then we'll run the
stuff within the curly braces the body of the estatement if you're familiar with other programming languages like
JavaScript this is um a very very similar syntax the only difference is we're not surrounding the height is
greater than four section with um parentheses so again to be clear this bit between the if keyword and the curly
brace um will be evaluated and if it evaluates to true then the stuff inside the curly braces will be executed um so
in this case we have the variable height and we're using the greater than operator um to compare it to the number
four so if height is greater than four then we'll print you are tall enough sort of listed um some of the different
comparison operators uh down here at the bottom they are basically identical to pretty much every other programming
language you will uh probably have used uh up to this point additionally we can do different things um if the if
statement does not evaluate to true so this is a perfectly valid if statement you do not need an else if or an else
block they are they're optional effectively um but the way it works is when we get to this code uh basically
we'll we'll uh compare height to six right and if that expression evaluates to true then we'll just print you are
super tall and we'll be done okay otherwise if that expression evaluates to false then we'll drop down
into this next if else or sorry else if statement and we'll compare height to four if height is greater than four then
we'll print you are tall enough and again we'll be done at that point so if this evaluates to true we execute this
um kind of section of code between the curly braces and at that point we'll be done otherwise if that's also false so
if height is not great than six and height is not greater than four then the else statement executes notice the else
statement does not have its own expression it just kind of always executes if all of the if and else if
statements um turned out to be false so let's jump into the assignment it says fix the bug on line 12 the if statement
should print message sent if the message length is less than or equal to the max message length or message not sent
otherwise Okay cool so up here we've defined two variables uh message length is 10 Max message length is 20 um and
then here we're going to do some comparing let me just run the code in its current
state says trying to send a message of length 10 and a max length of 20 message not sent okay so that seems problematic
right because with a message length of 10 and a Max message length of 20 I should be able to send that message so
the Bug Online 12 I think we just need to flip this operator to be less than or equal to so that now this expression
right message length less than or equal to Max message length should evaluate to true because it is message length is in
fact smaller okay let's run that message sent that seems to work it's worth pointing out that in go we
also have kind of an alternate way to write if statements if the variable that we are
comparing in an if statement is only used in that if statement then this syntax can be helpful okay so here's
kind of the traditional way of doing something we would create a variable called length let's just assume it's an
integer you can kind of forget um this function Syntax for now we'll talk about it um in a future chapter but basically
the idea here is we have a length variable it's an integer right and we're initializing it here we're creating the
variable here um and then we're compar pairing it against one right we're checking if it's less than one and if it
is we're doing something in this case we're printing that the email is invalid well instead of this syntax and by the
way this works perfectly fine there's nothing wrong with doing it this way um but we can alternately do it this way
which is basically to initialize that length variable in the if block within that first kind of initial statement so
notice there's two statements here um separated by a semicolon and in the first one we're
creating that length variable and then then we're moving the comparison itself kind of after the semicolon and then if
that condition obviously evaluates to true then we'll um execute the block this does kind of two things for us
first it saves us a line of code um which I would argue probably isn't the biggest benefit in the world um but more
importantly uh it makes it so that this length variable is only accessible within uh kind of the scope of this if
block so kind of down under Neath this code we wouldn't be able to use the length uh variable anymore which is kind
of nice if you never intended to use it in the first place you can kind of think of this as a clean code hack or like a
um kind of safety hack um to ensure that the length variable is never reused Down Below in other code when you never
intended it to be reused okay so uh the question for this assignment is why would you use the initial section of an
if statement uh and the answers are to confuse other programmers to keep the code concise and the scope limited or to
speed up your code um the answer is going to be to keep the code more concise and to again limit the scope
that that variable um exists within like other programming languages go supports functions functions are basically just a
way to break up your code into individual units that are easier to reason about right a function takes in a
specified number of inputs and it returns ears a specified number of outputs for example this subtract
function here named sub takes two inputs X and integer and Y an integer and it returns a single integer in this case it
just performs the simple calculation x - Y and then Returns the result to the CER now this little bit right here right
Funk sub X integer y integer returns integer is what's known as the function signature if you've never heard that
term before it's basically just a description of what the function does in terms of its types in terms of its
inputs and its outputs and what types they are right this basically says this is a function called sub it takes an
integer X and an integer Y and returns another integer function signatures are great because they tell us how we can
use the functions really at the end of the day if we're the person calling the function or the person using the
function really all we care about is what we need to give the function as input and what we get out of the
function in terms of its outputs so a function signature basically tells us all we need to know about a function to
be able to use it it omits all of the implementation details the stuff with within the curly brackets or within the
body of the function so let's get on to the assignment assignment says we often need to manipulate strings in our
messaging app that makes sense right we're working with SMS and email uh messages within textio so we're doing a
lot of textual data manipulation the concat function should take two strings as inputs and smash them together right
so returning a new string that is a concatenation of the inputs for example hello Plus World equals hello world so
we'd expect to return this concatenated string from our concat function over here fix the function the function
signature of concat to reflect its Behavior Okay cool so let me try running this and just see what happens looks
like undefined S1 undefined S2 undefined S1 okay so these are
undefined and so S1 and S2 that kind of stands for string one string two that makes sense
the problem here is that we're not we're not telling go what the types of the inputs should
be and this plus operator when operating on strings just concatenates so that should work let's go ahead and run
that yeah this looks good to me I want to just make one more point about function signatures in go you'll notice
that the type comes after the name of the variable so S1 is a string S2 is a string and that's just
to make it a little easier to read the authors of the go programming language kind of built on uh a lot of the ideas
from C and in C it was the reverse it was string S1 string S2 and that just kind of reads a little clunky if you're
used to kind of speaking in plain English makes more sense for the type to kind of come after um what it describes
go provides another bit of interesting syntactic sugar when it comes to function signatures when multiple
arguments are of the same type in this case X and Y the inputs to the add function are both integers um the type
only needs to be declared on the last one assuming that they're in order right so in this case this is valid go code
and X and Y are both integers because they follow one another we can put the integer um just after the y if we were
going to add say a string as a third parameter uh to this function then we would just add a comma here after int
and put you know name String or or whatever um this is just a bit of syntactic sugar uh it makes our code a
little less verbose you don't need to do this you can explicitly put the type on every input and output but you will
often see code like this it's a convenient shorthand so we've got these two um example Snippets of code Funk
create user first name String last name String age in and Funk create user first name last name String agent so which of
the following is the most succinct way to write a function signature Su synct is just another word for kind of it's I
guess it's the opposite of verbose right fewer fewer words um it's going to be the one that
uses the syntactic sugar which is this one because we're omitting the string keyword after first name so we already
talked about this briefly the idea that in go we specify the type of of a variable after the variable name and
this is you know different from kind of the C style way of doing things which if you were to declare a variable Y and C
you would say int Y and really the authors of the go programming language just felt that that didn't flow
naturally from English it's not the way we talk right we say x is an integer not integer is X if you want to read up on
that decision and why they ended up uh choosing that style then you can follow this link here assuming you're following
Along on botev so the question is what are we talking about when we discuss this declaration syntax right this this
swapping of the name and the type um and here are options the decision about Campbell case versus snake case um the
style of language used to create new variables types and functions guard Clauses versus if else no the ever
important question of tabs versus spaces no it's going to be the style of language used to create new variables
types and functions related question is Which languages declaration syntax reads like English from right to left C or go
the answer is going to be go go supports functions as data or basically the idea that you can pass functions around your
program uh to be called in different places callbacks right if you're familiar with JavaScript then you're
probably familiar with the idea of a callback it's a function that you could pass to another function to be called
later this question deals with that idea whenever we pass a call back in go the type of the function Chang based on what
its inputs and outputs are so for example here funk in in int is a function that takes two integers as an
input and returns an integer and that's going to be a different type than a function that say took three integers as
input and returned an integer and if you think about it it makes sense right if I'm going to pass a function to another
function so that it can call it later it kind of needs to know how many inputs how many parameters it can pass into
that function if it's a function that takes two inputs versus three inputs then the caller is going to have to
write the code differently so we have to treat every function signature as its own unique type so this question is a
bit of a doozy I encourage you to pause the video and try to work this one out on your own um but basically it says
what is this hairy beast here right F Funk Funk int int int int int all right potential answers are a
function named F that takes as int takes an INT as the argument and returns an INT uh no it is not nearly that simple
right a function named F that takes a function and an INT as arguments and returns a function let's
see uh that right there is a full function and an INT as arguments and returns a function no this returns an
INT okay a function named f that takes a function and an INT as arguments and returns an INT that's what I believe it
will be function named F that takes a function as the argument and returns an INT yep that's not it so ju to be clear
f is a function it takes two parameters a function right a function of a specific
type a function that takes two ins and returns an INT and as its second parameter an INT and then F returns an
INT hopefully that makes sense again feel free to pause the video and and stare at
that for a second let's talk a little bit about memory and how data that we create in our program using variables is
stored in memory so over here I'll keep track of the memory of our program and over here I'll write some
code so let's say in our code we write X colon equals 5 right so we're creating a new variable called X and we are giving
it the value the integer value of five and then we're placing the value five in that memory right stored as
binary data within Ram now in our program X the symbol is essentially just a
pointer it points to this location in memory so let's say on the next line of code we update X and we say x equal 2 so
now we're reassigning the value of x to two now let's try something different what if we create a new variable called
Y and we initialize it to the current value of x which in this case happens to be two well in this
case we're actually going to allocate a new section of memory to store the value of y and we'll initialize it to the
current value of x which is two and the symbol y now has its own location in memory so
we basically created a copy of X now this idea is really important to understand because sometimes in
programming we'll have multiple variables that actually point to the same location in memory they can
overwrite each other and sometimes we don't sometimes we have copies of data right so now for example if I were to
update y let's say I made y equal 1 at this point x would be unaffected X would remain two but y now becomes one right
because we have a copy these two variables X and Y reference different locations in memory so why does all of
this matter well in go variables are passed by value not by reference so let's take a look at this code snippet
here top of main we deare a new variable called X we set it equal to 5 and then we pass X into a function called
increment the increment function just adds one to X right making it six after that we print X and the weird thing is
that when we print X here we still get five and the reason for that is the increment function was operating on a
copy of X when we pass X in here to increment increment gets a new copy of X still equal to 5 it increments that X to
6 and then because we are not returning it it essentially just gets thrown away and then back in main we still have this
same X that's still equal to five the correct thing to do here would be to have the increment function return X
after making its modification and up in main we would write x equals increment X so that we could capture the return
value from the increment function so moving on to the assignment it says it's critical in texo that we keep track of
how many SMS messages we have sent on behalf of our clients fix the bug to accurately track the number of SMS
messages sent okay let me just try running this in its current state and we're missing a
return okay I'm going to remove this this uh type return there and see what happens sent 430
messages and it looks like here we have sends so far sends to add increment S is doing nothing because s so far is still
printing as 430 okay I think I'm understanding so let me put that back in the assignment
says alter increment sends so that it Returns the result after incrementing sends so
far alter the main function to capture the return value from increment sends and overwrite the previous sends so far
value Okay cool so this is pretty similar to the code snippet here basically we need to return an INT so
we're going to return send so far and then here we need to reassign
sense so far into the result of increment sense cool so again here we'll still be operating
on copies but because we're going to return the copy and save it back into the original variable we should be good
to go let me run that yep you've sent 455 messages that looks correct functions in go can have
multiple return values and when they do have multiple return values the Syntax for specifying that is just to wrap the
return values in parentheses as well so when there's just a single return value we do not wrap that return value in
parenthesis but when there are multiples we do wrap them in parenthesis one thing I really like about go is that it does
not allow you to have unused variables and because it doesn't allow you to have unused variables and because it allows
you to have multiple return values from a function we kind of need a way to ignore some of the return values because
there are definitely instances where a function returns two things but maybe we only care about one of those things for
example a point on a graph can be described by its XY coordinates but maybe all we care about is the x
coordinate so here we can call the get Point function and ignore the Y value by using an underscore and it's important
to understand that the underscore is not just like a conventional name that we're going to ignore it's actually ignored
the compiler completely removes it from our code so moving on to the assignment here
in texo we have obviously first names and last names for all of the users that we're able to send messages to well when
we welcome someone to texo we don't need their last name so let me go ahead and try to run this code and you'll see
we'll actually get a compiler error that says last name declared and not used used like I said go does not allow us to
have unused variables which I think is kind of an awesome uh little bit of the tooling it helps keep our code very
clean and concise easy to understand um so we need to explicitly ignore that last name with an underscore if we're
not going to use it let me try running that again and that looks good to me in go we can name our return values and if
we do it actually Alters the behavior of the function just a little bit let's take a look at
this function get chords or get coordinates it returns two integers and we've named the integers X and Y and by
naming them we've actually initialized at the top of the function the the variables X and Y and they're
initialized with their zero values so in the case of an integer literally just the number zero for both of them the
other interesting thing about naming our return values is that if we use a naked return statement a return statement that
doesn't explicitly say for example return 0 comma 5 then the values of X and Y are automatically returned from
the function so this version of get coordinates is actually the exact same as this kind of
more verbose version of get coordinates right here we have not given the return values the names X and Y and instead
we've initialized X and Y to their zero values and then returned them explicitly now a couple of recommendations I would
recommend using named returns when you want to document kind of what the intended purpose of each return value is
so for example if you have a function that just returns three integers that function signature could be pretty
confusing but if you have a function signature that says it returns three integers and they're named width height
and length that's a lot more interesting to the caller of the function they understand the purpose of each
individual return value much better so I like to think of named return values as basically a built-in way of documenting
what the purpose of all of your return values are and you should generally just use them on the other hand this implicit
or automatic return that you get along with um named return values I would typically advise against you'd only want
to use this in like very short very simple functions um because it harms readability right and I'm pulling this
directly from the tour of go they also agree with me um implicit returns or naked return statements um generally a
little bit harder to understand so the way I would write this function personally would be get chords XY in and
then I would explicitly return X and Y so let's jump down into the assignment it says one of our clients likes us to
send text messages reminding users of Life events coming up fix the bug by using named return values in the
function signature so the code will compile and run as intended Okay cool so this is the function we are interested
in fixing years until events looks like it takes a user's age as input and then returns or should return kind of the
number of years until they're an adult which is 18 the number of years until they can drink at least in the US which
is 21 and the number of years until they can rent a car which apparently is 25 um and it looks like we never want a
negative number so if if any of these are less than zero we just set them equal to zero that makes sense once
you're over 18 your years until you're an adult are just zero right you're already an adult okay let me try running
this see what we get okay undefined years until adult all right this makes sense right because there's no colon
here so we're not defining a new variable and the assignment said to use named return values so let's go ahead
and do that so here's until adult years until drinking I'm going to format this a little
better and we'll do years until car rental so again this will declare all of these values with their zero value at
the top and then this naked return statement should return them in order and just to make sure adult
drinking car rental adult drinking car rental okay we're in the right order let me run that
first test so four years old they'll be an adult in 18 years can drink in 17 can the current 21 that all looks good 10
it's going down 22 Yep this looks good to me so as I mentioned before explicit returns are
probably better than implicit returns in most scenarios um it just makes a lot more sense right so here in this
function get chords XY in so we're using named return but we're still explicitly returning X
and Y this is how I would recommend writing most of your go code um this this function here is doing the
same thing um it's explicitly returning hardcoded values though instead of the variables X and Y and it's just
important to understand that this effectively overrides the implicit return of X and Y so in this case five
and six will be returned again that's why I recommend doing it explicitly because when you see a return statement
that has explicit values being returned those are the ones that are returned you don't have to do any guess work you
don't have to scroll back to the top of the function to see which values are being returned so now we're going to
break that advice just for practice's sake um the assignment says fix the function to return the named values
implicitly okay so here we have a problem in our code where we are basically explicitly returning zeros
which as we talked about overrides the implicit return so my guess is if we look at all of these
yep every test is returning zeros if we just remove that implicit return and run that then it should work as intended yep
and then just to show you what I mean like what I would recommend doing is this it's
bigger like that that's how I'm going to recommend doing it and in fact because Boot Dev just checks the output this
should work just fine with our test so I'm going to submit it like this moving on to some questions about
named returns so it says when should naked returns be used and the answers are for large functions for small
functions or for complex functions and I would argue if you're going to use naked returns at all which honestly I'd kind
of just recommend against then you should only use them for small functions the more complex and large your
functions get the more important it is to be explicit and readable and document your returns with named returns and
things like that the next question is when should named returns be used so when there are many values being
returned when the function is simple or when there are few parameters being returned I would argue it's never really
a problem to name your return values but it's really important when there are many values being returned especially if
there are many values of the same type being returned because then you can you know essentially tell the collar of your
function through your function signature what they should expect each value to represent let's talk about one of my
favorite programming patterns or programming Styles um that is early returns or um what they're also
sometimes called as guard Clauses so an early return or a guard Clause is exactly what it sounds like it's just
when we return early from a function so this function divide if it's past a divisor of zero then it returns early
with an error other wise it goes ahead and does kind of the division and returns uh the results and a nil error
now we're going to talk about errors soon you don't have to worry too much about how they work for now um just
understand that a nil error effectively means no error so when we're looking at this divide function we can understand
that if the divisor is zero we're going to return early and and basically say we can't do this division because we can't
divide by zero um otherwise we'll take the happy path towards the end of the function best practices when it comes to
software engineering and how we write code change all the time right there's millions of developers all around the
world writing code and we all have different opinions and kind of the the common opinion about a certain style
tends to change over time um the interesting thing is I think that these days um guard Clauses and early returns
are kind of looked at as a good thing um this is clean code right this is a good way to write code certainly most go
programmers think this way way um but it wasn't always that way um there used to kind of be a heuristic that uh
developers used which was you shouldn't ever return from a function in more than one place so back when that was kind of
the more popular way of doing things you'd get kind of nasty if else nested statements like this um if you look at
this function get insurance amount basically takes a status as input it returns an integer and it only has one
return statement so it only returns from one place but I would argue that doesn't necessarily make this function all that
much easier to understand right we're initializing a variable up at the top amount and then just in this big nasty
nested if else chain we're kind of reassigning the value of amount uh based on some conditional logic now compare
that with guard Clauses right so with guard Clauses instead of overwriting the variable amount and then returning it at
the end of the function we're just returning early with the proper amount at each step of the way now both of
these functions do the exact same thing they have the same behavior um but I would argue that the one with the guard
Clauses is much easier to understand so the question for this exercise is which is true guard Clauses are unreadable
guard Clauses are generally worse than nested IFL statements or guard Clauses provide a linear approach to logic trees
okay so it's definitely not um these two right so I'm I'm going to go with provide a linear approach to logic trees
and really all that means is rather than having to follow kind of a tree structure to look at conditional logic
we can just follow a straight line right is it this no we move on is it this no we can move on right it it allows us to
break up kind of the cognitive load when we're when we're reading code so definitely a linear approach there the
next question is what is a guard Clause so a bit wise or operation an and operation in Boolean logic or an early
return from a function when a given condition is met and it is an early return let's talk about structs so
structs are the first collection type that we're going to talk about in this course a collection type is just a type
that contains other types in the case of a struct a struct is just a collection of key value pairs if you're familiar
with python dictionaries or JavaScript object literals this is basically the same idea so for example we can define a
car struct and we can say a car has a make a model a height and a width and each of those feels has its own
Associated type so let's move on to the assignment I think looking at the code is going to be the easiest way to
understand structs okay complete the message to send struct definition it needs two Fields phone number an integer
and message a string and um on these exercise I always recommend kind of going and looking at the test weite this
is all the code the way boot Dev works like all the code is here um and we're really just testing standard output to
see if you got the right answer so you can see literally everything that's going on
um so here you can see where a message to send is going to be instantiated with phone numbers and messages and here you
can see where um it's the uh fields are going to be accessed with the dot operator so let me go ahead and run it
in its current state um we should get yep a compile time error where it's saying message is undefined phone number
is undefined right so we need to add those to the definition all right phone
number integer message string let's go ahead and run
that and sending message love to have you aboard to that big number okay that looks good to me struct keys can hold
any type uh not just primitive types like integers strings and booleans um here you can see we've actually nested
the wheel struct within the car struct right so we have the car struct from the last example and we've added a front
wheel and a back wheel and they are each of type wheel right and a wheel has a radius and a material so we can actually
Nest structs within other structs and then we also saw this just a little bit in the last assignment but this is how
we can instantiate a new instance of a struct right so this is the struct definition we're saying this is what a
car looks like and then here we've created a new empty car called my car and when you create it kind of with
those empty uh those empty brackets all of the fields inside of the struct will just be initialized to their default
values their zero values right so strings will be empty strings ins will be zero and then here we're using the
dot operator to access Fields right so my car. front wheeel right so we're accessing the front wheel key and then
radius to access access the radius within the front wheel and we're setting it equal to five again with this syntax
stuff it's just best to get Hands-On keyboard and jump right into it so let's get to the assignment says textio has a
bug we've been sending texts with information missing before we send text messages in texo we should check to make
sure the required Fields have nonzero values notice that both the user struct so that's this here is a nested struct
or notice that the user struct is a nested struct within message to send okay so message to send has a message
which is a string and then a sender and a recipient and both of those are of type user okay that makes sense a user
is the sender a user is the recipient and then there is a message cool complete the send the can send message
function okay it should only return true if the sender and recipient Fields each contain
a name and a number if any of the default zero values are present return false instead Okay cool so can send
message this is essentially a function that's going to validate a message to send to see if it actually has data
inside of it so so um if I run the code right now it's always returning true right so you have an appointment
tomorrow you have an event tomorrow from Susie Saul ah see there's a phone number missing there that's a
problem looks like there's a phone number missing there that's a problem okay let's see so sender and recipients
contain a name and a number so if M to send dot sender and recipi sender contain
name dot name is empty
false right and then if m dot is going to be recipient is empty also return FSE so
we're just going to do some guard Clauses here and then sender and recipient and
now we're interested in the number number is an integer so if it's zero cool so we're just we're just
basically doing a a very simple validation to make sure um that names aren't blank and numbers are not blank
so let me run that okay so this one has all the information there and it's sent now this
one has a number missing can't send message that looks correct okay this is looking good to
me all right next up we have Anonymous strs so Anonymous strs are just struct instances that don't have a name so
whenever you create a new Anonymous struct in go you're you're immediately instantiating a struct of a given type
right the type of the struct doesn't have a name so for example here we have a
struct with a make and a model now in the exercise previous we remember we actually had this same exact struct but
we'd given it name the name was car right here we haven't created a new struct definition we haven't created a
new struct definition called car instead we're immediately instantiating a new instance of a struct called my car this
could be named anything right and it just has a make and a model field on it so the only reason you would use an
anonymous struct is if you have no reason to create more than one instance of the struct so to be clear what's
happening here we're creating a new variable called my car and it has two Fields it's a struct with two Fields
make and model and we're immediately giving it a value of make Tesla Model 3 and this type this specific struct type
doesn't exist anywhere else within our program this is kind of a type that's just unique to this one instance um
called my car it's not very common that you'll see kind of top level um Anonymous structs like this more often
you'll see nested Anonymous structs right so rather than creating wheel as a separate struct type we've just said
well Wheels kind of always exist within cars I know that's not really true but maybe within our program it's true um so
we just create a little Anonymous struct um within the greater car struct now as far as best practices in writing clean
code my opinion is that you should generally favor um named structs you avoid Anonymous structs unless you have
a really good reason to use them uh you'll really never go wrong with naming your structs okay so the question on
this assignment is what is a good reason to use an anonymous struct you're worried about security you need your
code to be faster you're worried about user privacy or it is only being used once well the only thing even remotely
related to how Anonymous trucks work um is that it's only being used once so if you're certain that you only want this
type to be used one time maybe you don't want someone accidentally reusing a type um then you might want to use an
anonymous struct next question is what's one advantage of using an anonymous struct
anonymous trucks make your code run faster Anonymous strs prevent you from reusing a struct definition you never
intended to reuse or Anonymous TRS can be compiled more quickly um it's this reuse one um one place that I have used
Anonymous kind of top level structs from time to time is in HTTP handlers so if I know that a given HTTP endpoint will
always return a a specific Json payload then I'll use an anonymous struct to define the shape of that Json payload we
haven't really talked about Json in go yet but kind of spoiler alert structs are how we structure Json data
typically next we have embedded structs and embedded structs are not the same thing as nested structs an embedded
struct is basically where would take all the fields from one struct and kind of shove them into another one uh let me
show you what I mean so here's our car struct that we've been using for our examples um it's got to make in a model
great here's our truck struct now you'll notice we've embedded the car type here but the car is missing kind of a name a
key in the truck struct bed size is the key int is the type here we just have the type which is car so what does this
do and how does it differ from a nested struct well in the embedded struct if we want to access the field model from a an
instance of a truck rather than doing truck. car. model we would just do truck. model because these fields of the
car type are becoming kind of top level fields of the truck type we're inheriting all of those fields from the
car type now I have to be careful with the word inherit even though it is kind of a pretty good descriptive term for
what's happening go is not an objectoriented language because it doesn't support classes or inheritance
in the class-based sense so if you're familiar with the idea of object-oriented programming just know
that classes and inheritance aren't really what's going on here uh this is you can almost just think of this as a
shorthand for kind of retyping this make and model into the truck struct it's it's almost just a syntactic sugar um so
that we don't have to retype all of these Fields so let's take a look at some code um and how we would use this
truck struct so I've created this new um instance of a truck called it Lane's truck has a bed size which is an integer
right and it has a car here now you might look at that and say that looks an awful lot like a nested struct and the
Syntax for creating a new instance of an embedded struct is very similar to uh the Syntax for a nested struct
essentially the key is just the same name as the type this just kind of a quirky thing about um
composite literals uh the embedded stuff looks like the nested stuff however when we are accessing the individual fields
on um Lanes truck using the dot operator you'll see they're all accessed at the top level it's not Lan truck uh doar
doake it's Lan truck. make right it's not lanest truck. car. model it's just LAN truck. model so those fields are
being brought up into the top level it's just when we kind of instantiate the truck the first time that we need to do
this sort of nested syntax so let's hop into the assignment it says at texo a user which is a struct represents an
account holder and a sender is just a user with some additional sender specific data a sender is a user that
has a rate limit field that tells us how many messages they are allowed to send fix the system by using an embedded
struct as expected by the Tex test code okay so let's go ahead and take a look at the test
code so it looks like the test code is creating some senders and it's expecting that a sender
has a rate limit and that it has a user right and this is looking like an embedded
struct and up here you can see s is a sender and we're directly accessing name number and rate limit all the top
level so I think all I need to do here is embed the user structure in fact let's run this without without that see
what happens yep we're getting some undefined Fields we'll embed the user type in
there and that's looking pretty good to me I will mention just to give you an idea of like when you use this in the
real world one place that I use it actively on boot Dev is users have kind of public fields on boot Dev and private
Fields so public fields are um stuff that we show on the leaderboard things that kind of anyone can see about your
profile maybe your bio or your profile picture um but users also have private Fields things like um your hashed
password right the password we store in the database and I've actually embedded the private Fields within the
public user so that I can easily nullify them when I don't want to send private data uh to a given web page all right
let's go ahead and run this code let's talk about methods on structs or just methods in general in go um I know
I told you go is not object oriented and it's not but if you squint really hard structs in go kind of look like classes
in a language like Java JavaScript or python so methods in go are just Behavior or functions that we can Define
on a type and more often than not we end up defining methods on structs although we could Define methods on any type so
let's take a look at the Syntax for this so here we have a simple rectangle struct it has a width and a height right
and here we've defined an area method on the rectangle struct so this is just a function right we're familiar with
functions already the only difference is that we've added this special parameter before the name of the function which is
again just a parameter that comes into the function just it's just a special parameter um and in this case it is of
type rect which is just a struct and we've named it R and then this function just returns r. width * r. height right
so it Returns the area of the rectangle so why would we use a method on a struct well there are reasons that we'll get to
later when we talk about interfaces um but for now it's mostly a syntactic thing a syntactic sugar thing if we have
behavior that we want to Define on a given type then strs can be a really good choice they give us this nice
tactic sugar right we create this new rectangle called R and now we can just call r. area to get the area of the
rectangle it's kind of a nice way to do computed properties on a type right so we could have stored area as a separate
number within the wct struct the problem with that is now we lose kind of a single source of Truth when it comes to
the area right if we store the area as say 50 and the width and height as 5 and 10 but then we update the height and
forget to update the area right now we have a bug in our code so this is a great way to kind of have a oneline
accessor to get the area of a rectangle but we don't have to actually store that area in our struct as kind of duplicate
data so let's get down to the assignment says let's clean up texo authentication logic we store our users authentication
data inside an authentication info struct okay so that's here it's got a username and a password we need a method
that can take that data and return a basic authorization string the format of the string should be authorization basic
username colon password right so this is the kind of standard basic authorization that's used in HTTP requests the
assignment says create a method on authentication info called get basic off that Returns the formatted string Okay
cool so let's create a new a new method and we can kind of reference this syntax over here so it's
funk and we want the receiver to be an authentication info struct so I'll just call it AI oh that's that's actually
confusing let's do off aui authentication info and we wanted the name of the
method to be called get basic off and it returns a string okay and then we want to return
this format here so if you remember we can use the format package to do that so we'll
return fmts printf remember s printf Returns the string rather than printing it to
standard out or to the console and we'll use that template username and password are both
just strings so we'll use percent s for our formatting verbs and then we can do off I dot username first
and ai. password next okay cool that looks correct to me let's run
it perfect let's talk about interfaces so an interface in go is just a collection of method signatures for
example take a look at this shape interface so we have an interface it's named shape and it specifies two
different method signatures so area is a method that takes no parameters and returns a float 64 perimeter is another
method that takes no parameters and returns a float 64 now any type that implements both of these methods and and
matches their method signatures will implement the shape interface which really just means that we can think of
it and treat it as a shape so for example let's take a look at this rectangle struct so erect has a width
and a height uh both of which are float 64s and again this is just a this is just a kind of standard struct and it
has two methods on it uh one is the area method that takes no parameters returns a float 64 um one is the perimeter that
takes no parameters and returns a float 64 and because a rectangle implements both of these methods we can think of a
rectangle as a shape a shape is just anything where we can kind of calculate its area and calculate its perimeter and
multiple types can implement the same interface so for example this circle struct it has
different underlying data right rather than a width and a height we can represent a circle with just a radius um
but to calculate its area and its perimeter the calculation is a little bit different right we're using pi for
example um but the method signature is identical right we don't pass anything in because we have all the data we need
on the circle struct and we just return a float 64 so both circles and rectangles because they implement the
required methods can be thought of as shapes or we could say they implement the shape interface let's get into the
assignment I think it'll all start to make a little more sense so the assignment says the birthday message and
sending report strs have already implemented the get message method so let's take a look at that so uh birthday
message is this struct here sending report is this struct here they both have this get message method that
returns a string and they're just it looks like the strings that they return are just a little bit different right
the birthday messages get message uh function returns this like hi blank it is your birthday on blank and a sending
report says your blank report is ready right so they just they just format a little little
differently okay so assignment says first add the get message method as a requirement on the method interface okay
so we need to finish the message interface and add a get message method that returns a string
Okay cool so now this message interface because birthday message and sending report both implement this method we can
think of both of those as messages next it says complete the send message function it should print a messages
message which it obtains through the interface meth method Okay cool so the a message is an interface so inside of the
send message function we don't actually know if we're dealing with say a birthday message or a sending report we
just know that we have access to a message so really the only thing we can do with it is call get message which we
know will return a string and it says it should print a message so we'll do ft. print line just print out the message
cool okay now this is powerful right let's go take a look at how this code is actually called so we have this test
function that also just takes a method a message and it sends that message right it's just
calling our send message function here but down here and this is where it's most interesting the test function
is not past like interface literals that's not even like a real thing right an interface is like abstract type that
represents other types instead because the test function takes an interface we can pass into it any struct that
implements that interface so for example here on line 42 we're passing in a sending report and then on line 46 we're
passing in a birthday message those are two different types in a strongly typed language being passed in as the first
parameter to a single function but the reason it works is because we're using interfaces okay let's go ahead and run
this see what happens your first report report is ready you've sent 10 messages hi Dondo
it is your birthday this looks great this looks great to me
in go interfaces are implemented implicitly and what that means is when we have a type like in our last example
we had the rectangle type that implemented the shape interface we never had to explicitly write anywhere on the
rectangle struct that it was intended to implement the shape interface because it satisfied all the requirements of the
shape interface it just kind of automatically implemented it and that's fairly unique to go in a language like
Java we might have to write something like this um we could take a look at this little example here we've got this
employee interface and a contractor struct if we wanted the contractor to implement the employee we would need it
to still fulfill the interface right by implementing all the methods but we might need to also explicitly type
something like implements employee right we explicitly say that we
intend to implement that interface in go it's done automatically let's hop into the ass assignment it says at texo we
have full-time employees and contract employees we've been tasked with making a more General employee interface so
that dealing with different employee types is a little simpler add the missing get salary method to the
contractor type so that it fulfills the employee interface Okay cool so we have the employee interface we have the
contractor struct um and if we look at the full-time struct it looks like it
already implements the employee interface so we just need to add the Missing Method
because right now a contractor has a get name but does not have a get salary Okay cool so let's go ahead and do that we'll
do employee get salary it returns an INT and a contractor's salary is their
hourly pay multiplied by how many hours they work per year okay so be something like
c. hourly pay multiplied by c. hours per year okay and just because I'm curious I
want to go look at how a full-time salary uh employee works yeah so you can see the the way that a full-time
employee their salary is calculated is is totally different like their their salary is actually just stored probably
because the way we think of full-time employees is like you know you make $60,000 a year you make $70,000 a year
whereas contractors often get paid by the hour so it make sense that they have a different calculation um and then down
here in the test Suite um this looks very similar to the last assignment where the test function is able to take
as inputs any type of employee right so we can pass in both full-time and contractors here uh let's go ahead and
run that Jack Bob and Jill we can see all of their salaries even
though Jack is full-time and Bob and Jill are both contractors so that looks good to me in go types Implement
interfaces implicitly because it kind of decouples the definition of the interface from the definition of the
type the type doesn't even really need to know that it implements a certain interface and that's really really cool
because it means it's easy for a type to inter to implement lots of different interfaces so the quiz question here is
how is an interface fulfilled answers are type has all the required interfaces methods defined on it or a struct embeds
the interface in its definition and the answer is going to be it has all the methods defined the next question is can
a Type fulfill multiple interfaces or Implement multiple interfaces and yes why not another quiz question go uses
the blank keyword to show that a type implements an interface and the answers are fulfills implements inherits and
there is no keyword in go and the answer is that there there is no keyword in go interfaces are implemented
implicitly in the next question it says in the example given the blank type implements the blank interface let's
take a look so example here we've got the shape interface circle struct circle has an area method so it
looks like the circle type implements the shape interface like we talked about before or a type can Implement multiple
interfaces it just needs to have all the methods for all of the different interfaces for example the empty
interface so that's this this definition right here this is an interface with no methods required is actually always
implemented by every single type in the go programming language now it's often not a very useful interface because you
can't really do anything with an empty interface you have no methods to call let's jump down into the assignment it
says add the required methods the email typee implements both the expense and printer interfaces Okay cool so we got
these expense and printer interfaces two different methods we need to implement and we have this email type with an is
subscribed Boolean and body string cool all right cost method if the email is not subscribed so
if not e do is subscribed I believe is what the field was then the cost is 0.05 times
uh the length for each character in the body so the length e do body now remember go is strongly typed and we
can't multiply an INT by a float directly so we need to cast this integer sorry I think I highlighted those
backwards the length of the body is an integer this is a float so we need to cast the length of the body to a float
so we can we can multiply a float by a float um otherwise we'll turn uh it's
0.01 times that same thing the length of the body cool uh the print function should just print to standard out the
emails body text easy enough fm. printline e. body okay so now our email struct
implements both of these interfaces and if we come down here to look at the test function this is interesting the test
function takes an expense and a printer right so we can use both of those uh methods
there and actually the email struct so we're creating instances of emails here we're actually passing it into test as
both the expense and the printer right because it makes sense it implements both cool let me run
that oh what did I screw up 05 time FL 6 4 length value of type float 64 is not used let's
see 0.05 time float 64 length of e body what did I screw up value of type float 64 is not
used I forgot my return cool let's try that again okay
so printing with cost 11 cents hello there printing with cost $1 I want my money back yeah that looks that looks
pretty good let's submit it up until this point we haven't really been naming the arguments of our
interfaces method signatures um but we totally could so if we take a look at this this is The copier interface it's a
uh copy method that it requires which takes two strings and returns an INT um the interesting thing is when you look
at this method signature it's kind of hard to tell what the intention behind the interface is it's like great it
takes two strings but what are those strings supposed to represent right so here we can take a look at what I would
consider a better interface definition where The copier interface actually has a copy method that specifies the source
file is kind of the name of the first parameter and the destination file is the name of the second parameter and
then byes copied is the integer that's returned so you know the functionality here is identical but now we have much
better I I mean I would consider it documentation of what the intention behind this interface is so the question
is are you required to name the arguments of an interface in order for your code to compile properly uh no no
it'll work fine either way next question is why would you name your interfaces methods parameters right like we don't
need to why would we do it um execution speed memory savings or readability and Clarity uh it's going to be for
readability and Clarity type assertions so type assertions are something you'll see every once in a while I would argue
they're probably not super common but you will come across them um and the whole purpose of a type assertion is so
that you can take an interface and kind of cast it back into its underlying type so in this code sample here we can kind
of assume that s is an instance of a shape interface and at this point in the code because it's just an interface we
don't know NE neily if it's a circle or maybe a square or maybe some other type so what we do is we do a type assertion
to cast it to the circle struct and basically what happens here is on the left hand side of the um this uh short
declaration operator here we get back the instance of the circle so this is going to be an instance of the circle
struct again the underlying struct um behind the shape interface if it was a circle right because we we can't be sure
if we had a shape we're not sure if it was a circle or maybe something else but this okay variable this is going to be a
Boolean and if it's true then it was a circle and we should have a valid kind of filled out Circle struct if okay is
false then we kind of have to discard the circle um because we we weren't able to parse out um the shape as a circle
because it wasn't a circle so let's move on to the assign it's an email then it should return the email's two address
and the cost of the email if the expense is an SMS then it should return the sms's phone number and cost if the
expense is any other underlying type just return an empty string and a 0.0 for the cost Okay cool so we've got this
get expense report that takes an expense as input and my guess is yep expense is this interface so our job is to kind of
try to cast it into the potential underlying types email and SMS so first we do um email
okay uh expense Dot and we'll cast it to an email type if okay so if it's an email return the
emails to address so uh return email. to address as that string there and also so it's cost so email dot looks like cost
is a method email. cost okay uh next we can do s okay uh e do cast to an SMS so if it's
an SMS we can return sms's to phone number to phone number and the SMS Dot cost I believe
that's also just yep just a method okay and it says otherwise if it has a different underlying type return
empty string and 0.0 for the cost okay cool that
feels at least like we followed the instructions let's see what we get not enough arguments in call to sms.
cost let's take a look at sms. cost cost H have
want what did I screw up here ah should be s. cost I I named I named this truck the struct S all right
let's try that again report the email going to John do will cost 11 email the sms okay cool and
invalid expense perfect that I mean that feels right I can come down here and check the test Suite yeah invalid okay
cool let's submit that when we want to do a lot of successive type assertions in a row there's actually a better way
and that's with type switches so here's a Syntax for a type switch uh basically we have an interface and we cast it um
at the very top of the switch and then we can actually check and kind of pattern match against different possible
types if none of the types that we've specified are the matching underlying type then it will kind of fall down to
the default case so let's write some code and see how it works um down in the assignment it says after submitting our
last snippet of code for review a more experienced gopher or term for a go developer uh told us to use a type
switch instead of successive assertions let's make that Improvement implement the get expense report function using a
type switch Okay cool so kind of the same thing though if it's an email return a to address and a cost if it's
an SMS then we should return a phone number and a cost blah blah blah right the difference is we're going to use a
different syntax this time okay so we're going to be using this switch syntax so switch value colon equals e.type
okay Open brackets case um we are interested in if the expense is an email so case email now the the
interesting thing is with now within this case block V is an email so I can treat it like an email so I can do
return V Dot oh what was it two address I think yeah two
address b. cost a SMS return B do two phone number I think
it was two phone number b. cost and default
return uh I think it was empty string just zero values right empty string and 0.0 okay cool let's see what
happens with that oh am I forgetting nope I've got oh I'm indenting a little
weird there we go let's run that cool the MS SMS is going to be is going to this number with this
cost got some emails and costs okay that's looking good to me now that we've learned what interfaces are and how we
use them let's talk about how we can use them more effective and in a sort of idiomatic and clean way if you forget
all of the other advice that we're going to go over when it comes to writing clean interfaces don't forget this one
and that is to keep interfaces as small as you can the best the cleanest and the most useful interfaces typically just
have one or two methods defined on them imagine a simple Stringer interface it has one method defined on it called
string that simply returns a string you can take that interface and implement it on basically any type and now you have a
super useful interface for logging out string representations of different types there isn't a hard and fast rule
about exactly how many methods you should be defining on your interfaces but what you really should be doing is
looking for kind of the minimal Behavior necessary to accurately represent an idea or concept right so for example
here's a slightly larger interface from the standard Library again normally we'll see interfaces in the standard
library with maybe just one or two or three uh methods this one has five but this is kind of still the minimum um
amount or or the the the minimal necessary behavior that we need to describe a file like a file in your file
system right we need a way to close the file we need a way to read it we need a way to seek to individual uh kind of
sections of the file um so this is an example of a slightly larger interface right I would say five methods is
definitely a on the larger end um but it's still a good interface because it's using as as little Behavior as it
possibly can to describe an operating system file so the question that goes along with this is interfaces should
have as blank methods as possible and the answers are complex few and many and the answer is going to be few the next
mistake that I've seen new go developers make is when they write an interface that sort of knows about the types that
they've intended to satisfy it so for example if you have a car interface and you've defined an is fire truck method
on the car interface that returns a buou you know whether or not it's a fire truck that's probably a mistake you
would actually just want to use a type assertion or a type switch if you really needed to figure out the underlying type
you should not make your interfaces aware of the underlying types this also breaks rule number one right because
rule number one we don't need to know if a car is a firetr for like the minimal behavior of a car I mean you can see how
this sort of design pattern would get out of hand very quickly because we might need to also Define other booleans
like is pickup is sedan is tank right if you start catering to all of the underlying types of a given interface
your interface is going to be really bloated and become very very large so the question here is actually the
reverse it says it's okay for types to be aware of the interfaces that they satisfy if it were flipped around the
answer would definitely be false but because we're talking about the types being aware of the interfaces they
satisfy I don't think that's necessarily a problem um they don't need to be aware of them right because again in go uh
interfaces are satisfied implicitly but I would argue to some extent they are aware of them because the developer had
to go in some cases out of their way to satisfy the implementation of an interface so I'm going to go with true
on this one so now this is the reverse question it says it's okay for interfaces to be aware of the types that
satisfy them no they should not be aware finally I just want to talk about how interfaces are not classes sometimes
this can get mixed up especially if you're coming from an object-oriented background um inter Es are just a very
different idea interfaces are not classes they are much Slimmer right classes have a lot of functionality
going on inheritance is a fairly complex topic um interfaces are honestly just a simpler idea um interfaces don't have
Constructors or deconstructors that require that data is created or destroyed right so that's another way
that they differ from classes that have kind of this in inherent setup and tear down uh situation interfaces are not
hierarchical right there's there's no uh hierarchical inheritance tree when it comes to interfaces and the most
interesting one is that interfaces Define functions signatures but not underlying Behavior right so this is
actually a big difference between interfaces in classes in a class A Child class can inherit behavior from a parent
class which can kind of dry up your code right don't repeat yourself in go that's not the case at all an interface doesn't
dry up your code you still have to go Implement all of the methods on all of the different types individually
interfaces just allow us to use all of those types kind of in the same places later in this course we'll get to talk
about generics which is a way to kind of dry up your code and funny enough kind of uses interfaces um under the hood or
kind of as a part of the generic system to make that happen so finally the question is interfaces allow you to
define a method's Behavior once and use it for many different types uh that's false go has a very unique way of
handling errors really quickly let's review how JavaScript handles errors and the way JavaScript handles errors is
very similar to python or Java it uses a TR catch Paradigm um and let's contrast that with how go handles errors in its
sort of unique way let's pretend that we have a function called get user um and we'll start with JavaScript so we have a
function called get user whoa and it Returns the user so we'll do const user equals get user and we have
to wrap the get user function the call to the get user function and a tri block because we know that the get user
function can throw because let's just say for example uh maybe the user doesn't exist Okay cool so if something
goes wrong we add a catch block and if this function throws execution will stop we'll enter the catch block and do
whatever is in here for now we can just uh console. log the error now let's take a look at
what this function looks like in go we do something like this we do user air colon equals get
user if Air does not equal nil then we could print out the error and return so that we don't continue and
then down here we could you know deal with the user object so uh use user here and for consistency sake we could say
use user here um after the user object is created in JavaScript okay so what's the difference well let's talk about the
first reason why I prefer goes error handling to javascript's error handling let's pretend we need to go get some
data for the user after we've already kind of retrieved the user successfully so let's say we need to go get the
user's profile picture well in go all we would do is call the next dangerous function
let's say get user Pro profile and maybe it takes a user. ID as input right so we need to successfully wait for the user
to come back and then we can go fetch its profile right so this looks very similar
we doing another dangerous function get user profile and it returns a profile and an error if an error with the
profile occurs uh again we'll just kind of print out the error and return otherwise now we have a profile object
okay what would we do in JavaScript well in JavaScript we can't just take this Tri catch and paste it down here and
update some stuff this will not work if we do get user profile user.
ID profile the problem with this code is that user is only available within this
Tri Block it's scoped to the tri block right so this user object here will be undefined so the normal thing to do in
JavaScript would just be to take this line here and inject it within the tri block the original Tri
block right so now I'm doing my second dangerous function after I do my first dangerous function the reason I don't
like this is that the logic for handling the errors is now all in one place if I want want to handle the error for the
get user profile function differently or separately um then I want to handle the error from the get user function I have
to actually Nest the whole Tri catch like this right so now user. ID is accessible
because I'm in the same Tri block but I get a second catch block so we kind of get this nasty nesting if we want to
treat each individual error separately and this kind of brings us to the second reason why I prefer Go's error handling
and it's that it forces us to think about each individual error that's passed back from a dangerous function so
again in JavaScript kind of the normal thing to do unless you really needed separate error handling would be to just
kind of do all of your dangerous stuff in one large Tri block the problem with this in my opinion is that it kind of it
doesn't encourage me to remember which of these functions is dangerous in the first place for example maybe this is a
safe function and this is a dangerous function that can potentially throw an error but looking at this code from um
kind of a calling perspective I don't really know that that's the case whereas in go because a function returns kind of
the valid data and the error with every function call I know for a fact that the get user profile function can throw an
error because it returns an error value this will probably make more sense if I actually write out some of these
functions so in JavaScript the get user function might look just like this function get
user uh we could say uh do some get user logic here and then maybe it returns you know
a user looking at the function signature all I can tell in JavaScript
is that this function takes no arguments and the only way for me to know what it returns is to go find that return
statement see oh okay it returns a user however in go I actually get two super helpful things in the function signature
and remember the function signature is just kind of that first line of a function definition so in go if I were
to write the function definition for the get user function would be something like this
funk get user doesn't take any inputs but it returns a let's say a user struct and an error right and then we have some
kind of do get user logic here right so now just by looking at this function signature I can tell that
it returns a user and that it could possibly return an error as well that I need to go handle whereas again in
JavaScript not only do I not get to see in the function signature what is potentially returned but I also don't
know that this function can throw I have to kind of go digging deeper into the function definition to see if there's
potentially something uh dangerous going on in the meat of the function so again to reiterate because this is actually a
super important point when you're learning about how errors are handled in go the primary reason I like error
handling in go is that it forces me as I write my code to be kind of hyper aware of every potential error and make sure
that I write code that handles it so now that we understand what error handling in go kind of looks like from a high
level let's dive into the detail s errors in go are just values and specifically they're just interfaces so
the built-in error interface is an interface with a single method the error method and that method returns a string
describing what went wrong so how do we actually go about handling errors in code well let's take a look at this
function right here so this is a function in the standard Library it's called asky to integer it takes a string
and attempts to convert it into an integer value and potentially that can be problematic right because you can
write strings that aren't numbers under the hood Okay cool so what do we do we get back an integer and an error and in
essence it's it's it's really simple the error is either nil or it's not nil if it is nil that means everything went
fine and nothing went wrong so if the error is nil it means there is no kind of string representing what went wrong
because nothing went wrong however if the error is not nil that means something did go wrong right so if the
error is not nil in this case then we'll print an error message and return from the function right so we're basically
writing guard Clauses that say something went wrong let's handle this error and get out of here right enough talk let's
write some code so you can see what I mean let me expand this so it's a little easier to see the code okay the
assignment says we offer a product that allows businesses that use texo to to send pairs of messages to couples it's
mostly used by flower shops and movie theaters okay great complete the send SMS to couple function so that's this
function here it should send two messages first to the customer and then to the customer's spouse so use send SMS
to send the message to customer if an error is encountered return 0.0 and the error uh do the same for message to
spouse if both messages are sent successfully return the total cost of the messages added together okay I think
I understand what's going on so we're basically going to send both of these messages one after the other um if any
errors happen we kind of abort early and return the error okay so uh send SMS takes a
message as input so first we'll say uh send SMS and we'll send the message to the
customer and send SMS returns a float 64 and error so we'll say um I think it's the cost
return the total cost okay yeah so cost error send SMS all right if error does not equal nil so if there was a problem
sending the SMS uh return 0.0 and the error so return
0.0 right because this function send SMS to couple Returns the total cost and an error so we're saying if we failed to
send the SMS then it costs nothing and we'll return the error that describes what went
wrong otherwise we'll do it for the message to spouse so we kind of are just going to do this exact same thing so I'm
going to say cost for uh for let's say customer cost for spouse and we'll be sending the message
to the spouse cool all right now if we get to the
bottom of the function that means nothing went wrong so we can return cost for
customer Plus cost for spouse and nil right because if we get down here nothing went wrong so we don't
have an error to return okay let's go ahead and run this code and see what we get and I want to just scroll down and
take a look at some of these test cases okay message for customer thanks for coming into our flower shop message
for spouse we hope you enjoyed your gift error can't send texts over 25 characters Okay cool so I mean if we
look at the send SMS uh function it actually throws an error or I shouldn't say throws because we don't throw in go
right but returns an error value that says can't send texts over blank characters so that looks correct to me
um here we've got message to customer message to spouse total cost okay this is looking pretty good to
me I'm going to go ahead and submit that we talked about how error handling in go is all built around the error interface
in fact let's just review that really quickly uh this error interface is really just an interface that wraps a
method returning a string because if you think about it at the end of the day an error is just a kind of nullable string
either it's a string representing what went wrong or saying what went wrong or it's nothing because nothing went wrong
so being good at handling errors in go has a lot to do with being good at formatting strings or formatting useful
error messages so let's review how we format strings in go most formatting go is built around kind of these formatting
verbs that are defined in the format package the fmt package of the standard Library so for example the S printf
function returns a string where it interpolates the values passed into the function after the formatting string
into the formatting string uh string where where the verbs exist so for example this first percent V is replaced
with name the second percent V is replaced with age percent V is the verb we use for sort of the default format
right if you format an integer using percent V then it kind of just prints the integer in string form um but there
are other ways that we can format stuff for example the percent F verb is used to format floats and you can actually
specify how many decimal places you want to show up in your output string by kind of prefixing the F portion with a point
2 for two decimal places or say A.1 for a single decimal place so let's jump right into the coding assignment
assignment says we need better error logs for our backend developers to help them debug their code complete the get
SMS error string function so that's this one right here um it should return a string with this format Okay cool so I'm
just going to jump right ahead so it should return
turn fmts printf right because we're trying to format a string uh this format let me expand that a little
bit okay so return a string with that format uh cost is the cost of the SMS so I'm going to replace cost here with the
cost formatted to two decimal places so percent Point 2 F right for two decimal places and replace recipient with the
stringified representation of the recipient's phone number which is the string
here so percent v um it's important to point out I could also use S here to format a string but V and S do the same
thing effectively um when you're working with strings
and then we need to pass in as parameters the values that we want to format or that we want to interpolate
into the string so it's going to be cost and recipient going to Dent that because
that is some crazy formatting okay that looks better all right SMS that costs per point2 F to be
sent to percent V this looks good to me all right I'm going to go ahead and run that and see what we
get ah right can't forget to import the formatting package let's run that okay SMS that costs 1.40 to be sent
to the string cannot be sent awesome this looks correct to me I'm going to go ahead and submit that
let's talk about building our own cust error types so remember the error interface is an interface in fact let me
jump back and show you what it looks like again so it's just this interface here and because it's an interface that
means we can build our own types like this user error struct here that Implement that interface which means
they can be used as errors and remember that the error interface just has one single method that we need to Define
right the error method that returns a string and as long as we have that then our type can be used as an error so for
example we could create this user error type that stores a name and then we can use it as an error to format an error
message that contains the name of the user's account who had an error so for example in this snippet here we have a
send SMS function and if we're not able to send a message to a user we can actually just return that user error
struct with the user's name and again that is an error we can return that struct as an error type because it
implements the error interface the caller of this function would then just treat it like any other error the reason
this is useful is that we can store structur data within our errors so if we want to format them a specific way we
have access to sort of dynamic data like a name let's write some code that uses this concept so the assignment says our
users are frequently trying to run custom analytics queries on their message deliverability metrics right so
we're sending lots of messages we want to know are these messages getting delivered they end up writing a bad
query that tries to divide a number by zero it's become such a problem that we think it would be best to make a
specific type of error um for dividing by zero update the code so that the Divide error type so that's this error
type here or rather this struct here implements the error interface its error method should return a string formatted
in the following way cannot divide dividend by zero okay let's write that method so it's going to be a function on
the Divide error type so I'm going to name it de which is a a divide error and the name of the
function must be error takes no arguments and returns a string right so that's that's the function signature we
need to use um to implement the error interface and then we're going to return a string and we're going to need to
format the string so we'll use S printf uh the format package is already imported and this is our template and
dividend in this case is a float 64 and uh you it says here dividend is the
actual dividend use the percent V verb so percent V okay so we're just going to kind of do the default formatting for a
float 64 and let me indent all this so that it's kind
of readable where dividend is the actual dividend of the divider okay so we'll do
de do Dividend that will interpolate there okay that looks correct to me I think
we've implemented the error interface properly let's go ahead and run that uh oh I forgot my
comma um this is kind of an interesting Quirk of the go programming language um if you to add a new line after the last
parameter to a function you have to put a comma so this would work all right
and this would work but this does not work right that's what I did the first time so just kind
of something to watch out for okay so let's take a look at this um dividing 10 by 0 cannot divide 10 by 0
dividing 10 by 2 quotient 5 dividing 15 by 30 5 question okay this is looking like it formatted properly let's submit
that so we've got a quiz question it says what is the underlying type of an error is it an interface is it a struct
is it a string well it could technically be a struct or a string right like we saw in the last uh the last assignment
but the most correct thing would be to say an interface because it must always be an
interface right it must always implement the error interface it could also be a struct or a string um but it will always
be an interface next question is can a Type be an error and also fulfill another interface well errors are just
interfaces and we know that types can fulfill any number of interfaces as long as they have all of the required methods
so yeah there's no problem with this let's talk about the errors package so the standard library and go has an
errors package that exposes a few useful functionalities but the one we're most interested in right now is the errors.
new function so the useful thing about the errors. new function is allows us to create a new error from just a string so
we don't need to kind of you know Define a new struct or a new type and then have it explicitly implement the error
interface that can be a lot of code if all we want to do is kind of return an error with a very specific string let's
go ahead and use it the assignment says twilio software Architects may have over complicated the requirements from the
last coding assignment yeah all we needed was a new generic error message that returns a string no dividing by
zero when a user attempts to get us to perform the taboo right the taboo of dividing something by zero complete the
divide function use the errors. new function to create an error when y equals z that reads no dividing by zero
okay so this is pretty straightforward basically if Y is zero which means that the the number that we divide by would
be zero which is obviously a huge problem in mathematics you're not allowed to do that in go or most
programming languages that I'm aware of um so we need to return an error here uh so that this operation never happens so
we'll return errors. new looks like the errors package is already imported for us and we just need to return an
error that says no dividing by zero okay let's go ahead and run that and see what we
get oh I screwed up this function returns a float 64 and an error so we should return a zero value for that
first value when we're returning a non-nil error let's go ahead and run that cool dividing 10 by Z no dividing
by Z all of this looks good to me let's talk about loops in go so if you are
familiar with Loops in other languages Loops in go syntactically are very similar to say Loops in JavaScript uh
the main difference is just that we don't use the parentheses around uh kind of the the signature of the for Loop um
which again is very similar to how if statements in go work we're basically just dropping those uh parentheses
syntactically um the initial portion runs at the beginning so for example in this uh snippet of code where We're
looping over the integers 0 through 9 UM in the initial section we're just initializing a variable I and setting it
equal to zero um in the condition section uh we're checking and making sure that I is less than 10 so at the
end of every uh kind of iteration of the body which in this case we're just printing I in the body um we're going to
check and make sure that I is less than 10 if it is we'll continue on to the next iteration of the loop otherwise
we'll be done um and then at the end of every iteration we'll also be running this after section so we'll be
incrementing I and that happens before uh the condition is run so for example if we increment I to 10 and then I is no
longer less than 10 we will not continue on to the next iteration of loop which is why this prints 0 through 9 and not 0
through 10 10 let's jump right into the assignment says at texo we have a dynamic formula for determining how much
a batch of bulk messages cost to send so we need to complete the bulk send function it takes a number of messages
as input and returns a float 64 um which it looks like will be the total cost of the batch of messages okay each message
costs 1.0 plus an additional fee the fee structure is so it's going to be first message is 1.0 plus 0 so 0 is the fee
second message is 1.0 Plus 01 third message is 1.0 plus 02 okay cool and then our job is to use a loop
to calculate the total cost of all of these messages for you know the number of messages that we've been given okay
let's let's just start writing some code so total cost I'm going to just start it out at 0.0 and then we'll use a loop or
I equals 0 uh to do like num messages iterations so you know a number of iterations equal to num messages so um
start I at zero uh and then we'll do I is less than n
messages and I Plus+ okay so this uh kind of body of the for Loop should execute numb
messages times and we're going to want to add the total cost so plus equals um and we're going to use this formula so
it's going to be 1.0 plus this fee and how do we calculate that fee well it looks to me like it is
0.01 times the basically the message number right so for the first message at index0
I equals 0 um it's going to be a fee of zero so if we just use I this should work because if I is zero and we
multiply that by 01 anything multiplied by 0 is 0 right so we'd get 1.0 + 0 for the second message I should be one so
we'll get 1 plus 01 because anything multiplied by one is itself um and so on and so forth um that's looking correct
to me at the end of the function we'll just return a total cost cool let's run that oh untyped
float constant truncated to int okay so I is an INT and we cannot multiply an INT by a float 64 so I'm going to cast
um I to a float 64 let's try that again and take a look at some of these numbers Okay cool so the cost for 10
messages is 10.45 and and that sounds about right right because each message costs
one plus this like fractional part that grows over time so like the 10th message would have cost like
0.10 and it would decrease over time so that looks about right to me 50
messages yep okay I'm going to go ahead and submit that one interesting thing about four Loops in go is that each
section of the for Loop the initial the condition and the after are actually optional and we can omit any of them so
for example if we omit the condition then the for Loop will just run forever so let's see why this might be useful um
let's jump into the assignment the assignment says complete the max messages function given a cost threshold
it should calculate the maximum number of messages that can be sent um and then it looks like the fees for each message
are going to be identical to the last assignment so let's go ahead and just get started um so max messages we have a
threshold a cost threshold right we want to see how many messages we can send while keeping the total cost under the
threshold so I'm going to create a total cost variable and set it equal to 0.0 and then this is going to look
really similar to the last assignment um but basically we'll set I equal to zero to
start um I'll skip the condition for now because we don't know to what number we're kind of looping up to right we're
trying to calculate that so I don't know where stop yet um but I do want to increment I with every uh iteration of
the loop and then I basically want to um update the total cost by adding to it um
and use uh this formula over here right so it's going to be 1.0 plus
0.01 time time I which I need to cast with 64 okay so that formula is looking again
identical last time now if I want to return the the maximum messages that I
can send while keeping the cost under the threshold then basically I need to check and say if the total cost is
greater than the threshold then I should return IES that sound right so for example
let's say that right off the bat the threshold were I don't know is it negative one right so I'm not allowed to
say send anything I would want to in that case return zero right cuz I can't send any messages so what would happen
we'd enter the loop I'd calculate the total cost for the first message which would be
1.0 and then because the total cost is already higher than the threshold I would return I which would be zero so
that works right however if the threshold were a little higher let's say the threshold were 1.5 uh then what
would happen is we do that comparison the total cost would be less than the threshold
so we'd continue to the next Loop uh iteration of the loop where I would be incremented to one and then on that next
iteration we'd go over okay so this is looking good to me let me go ahead and run this
code okay so with a threshold of 10 I can send nine messages without going over the
threshold that sounds right right because I'd have like nine in some fractional
part let's go ahead and mcy most programming languages have support for a while loop and really a while loop is
very similar to a four Loop except it doesn't have that kind of initial and after statement it it just runs until
some condition is no longer true now because of this similarity the authors of the go programming language decided
to not include an explicit while loop with a while keyword instead the for Loop is a while loop or just basically
both those side um kind of sections are omitted and we just have a condition that follows the four keyword so in
other words if we have a four keyword and then a single expression then it is a condition um that while true will
continue to kind of run the body of the loop over and over and over again until the condition stops being true let's
take a look at an example um here we have a variable called plant height we start it uh equal to one and we've done
this outside of the for Loop right and then within the for Loop we just have one section where we're comparing the
variable plant height to the number five right and while it is less than five we'll print this message and then at the
end of the loop we'll increment plant height now you might notice this looks just like a for Loop where we've
essentially taken the initial statement and moved it up outside of the for Loop body and we've taken the after statement
and moved it within the for Loop body and that is what we've done right but it's to demonstrate that this is valid
syntax let's jump right into the assignment okay so the assignment says we have an interesting new cost
structure for our SMS vendor so at texo we have a vendor that we have to pay to send text messages through right so
we're a software service that makes sending text messages easy but we do have to pay some kind of uh maybe
Hardware service that actually does the sending of uh the text messages kind of over the wireless network okay um they
charge exponentially more money for each consecutive test text we send let's write a function that can calculate how
many messages we can send in a given batch uh given a cost multiplier and a Max cost in pennies okay so given a cost
multiplier and a maximum cost our function will return basically the number of messages that we're allowed to
send um that's under that that Max cost so it says in a nutshell the first message costs one penny Okay actual cost
and pennies starts at one and each message after that first message uh costs the same as the previous message
multip by the cost multiplier okay so that's happening here um gets expensive uh there is an infinite Loop in the code
okay so on line 10 here we have this four with no body and then an open curly bracket um let me show you what happens
when we run that our codee's going to sit and run and execute the body of that for Loop over and over and over and over
with no exit condition uh this is obviously a problem we don't want uh an infinite for Loop there so our job is to
exit before incrementing Max messages to send if the cost of the next message would go over the Max cost so all we
need to do is check and only execute this Loop while the actual cost is less than or equal to the Max cost in
pennies um and we're going to have to cast looks like so this is a float this is an inch so we're going to cast this
to 64 now this should work because Max message to send starts at
zero so assuming the actual cost is less than the Max cost then we go ahead and increment say we can send one more
message and then we take a look at the next cost and if the next cost is still less then we'll essentially get to add
another message so we'll keep kind of looking ahead and calculating the next cost right and as soon as the next cost
goes too high we stop okay so I'm going to go ahead and run that cool so with a multiplier of
1.1 and a Max cost of five we can send 17 messages that sounds about right right the actual cost starts at
one the Max cost is five multiplier of 1.1 yeah that sounds right um so the multiplier goes up Max cost goes up now
we can send nine messages that all looks good to me let's talk about the modulo operator how it works and go and kind of
how it works uh generally so the modulo operator is a percent sign so it looks like this right and the modulo operator
essentially calculates remainders remainders so what do I mean by that well let's jump into an example
uh let me switch colors so four 4 / 3 in sort of a normal uh floating Point division uh equals like
1.33333 forever right um we have this fractional part however that's how math Works normally and that's how math Works
in floating Point division in a language like go if we're doing integer division then we can't have a floating Point
result so the result of four divided 3 and integer division is actually just one it's essentially the number of times
that three can be divided evenly into four and then we chop off the remainder the interesting thing about the modulo
operator is that 4 modulo 3 doesn't return the number of times that three can be divided evenly into four it
Returns the remainder after that division so actually in the case of Four modulo 3
the remainder is also one so it's the same but for example five modulo 3 is
two and 6 modulo 3 is zero again because three divides evenly into six right six integer
division by three is two right because three goes evenly into six twice but the remainder is zero that's why 6 mod 3 is
zero let's just do a couple more examples feel free to pause the video um in between when I give the question and
provide the solution um so that maybe you can practice a little bit okay so let's do
12 mod 4 we'll do in fact maybe I'll just write out all the problems first let's do
16 mod 5 let's do
22 mod 8 and let's let's do 27
mod 6 I think those will be those will be good okay 12 mod four so does four
divide evenly is 12 uh yes 4 * 3 is 12 right so the remainder is zero all right how many times does five
divide evenly into 16 the answer is three right 5 10 15 and the remainder would then be one
how many times does 8 go into 22 8 16 24 so 24 doesn't work so it's going to be 16 and then 22 subtract 16 is 6 is the
remainder right and then 27 mod 6 um 6 goes into 27 four times 6 * 4 is 24 so a remainder of three so an important thing
to note here is if you're trying to figure out if a number divides evenly into another number then you can just
check if say a mod b equals 0 right if aod B equals z then that means B divides into a an even
number of times so a is a multiple of B so in go the module operator is just that percent sign right so seven mod 3
um this expression is going to evaluate to one we'll also need to know about the logical and operator and the logical or
operator which are double Ampersand and double bar uh respective The Logical and operator operates on two Boolean values
and only returns true if both sides are true right this and that the or Operator just needs at least one side to be true
right in order for the entire expression to evaluate to True let's jump into the assignment it says we're hiring
engineers at texo so it's time to brush up on the classic fizzbuzz game coding exercises have been dramatically
overused in coding interviews around the world complete the fizzbuzz function that print the numbers 1 to 100
inclusive each on their own line but substitutes multiples of three for the text Fizz multiples of five for Buzz and
multiples of three and five for fizzbuzz okay so we need a for Loop for I colon equals 0 I is less than 100 actually
less than or equal to because it said inclusive right i++ okay um we need to think about the
order in which kind of these things can happen happen so we're checking for multiples of three multiples of five and
multiples of three and five so we actually should check for multiples of three and five first because something
could be that and one of the other two conditions right it could be a multiple of three and five and just a multiple of
three that makes sense maybe it'll make sense when I type it out so if I mod 3
Z and I mod mod three zero oops not three and three
three and five then we'll print bizbuzz right else
if I mod 3 0 then we'll just print
is else if I imod five is zero we'll just print Buzz
otherwise we'll print I
right if they're both multiples Fizz buzz if it's just three Fizz if it's just five Buzz okay let's try
that oh cannot forget to import the formatting package okay let's
see fizzbuzz one two Fizz four Buzz five Fizz seven eight fiz Buzz one oh ha one through 100 that would have been close I
almost didn't follow instructions I was like why do we have a Fizz Buzz up front that doesn't uh that doesn't make sense
Okay cool so one two Fizz four Buzz right five is a multiple of five 10 is a multiple of five
15 is a multiple of 5 and three so this is looking correct to me let me go ahead and submit that so in the last
assignment we kind of uh did a if else chain within our for Loop but there's another way that we can write guard
Clauses within Loops um so that we don't have to necessarily do that if else chaining um if we don't want to uh the
continue keyword stops the current eration of Loop and continues to the next iteration so continue is a powerful
way to use guard classes right so we write our for Loop and and then if some condition happens um we can kind of bail
out of the body of the for Loop early and just continue on to the next iteration the break keyword is similar
in that it stops the current iteration but instead of continuing on to the next iteration it just ends the loop entirely
moving on to the assignment it says as an Easter egg we decided to reward our users with a free text message if they
send a prime number of text messages this year because textio is is run by a bunch of nerds complete the pr the print
primes function it should print all of the prime numbers up to and including Max Max it Should Skip any numbers that
are not prime okay so here's the pseudo code print Prime Max so let's convert this pseudo code into real go code so um
for n in range 2 to Max plus one so we're going to do 4 n colonal 2 N is less than Max + 1
n++ okay if n is two if n is two n is prime print it fmt do print line
n and we need to continue right continue to the next iteration basically saying okay this n is prime we'll print it and
then we can move on all right if n is even so so if n mod 2 is zero right that's an easy
way to check if something is even um and not make your way onto our programmer humor if n mod 2 is
zero n is not prime skip to the next n so uh we do not print and we just skip
okay next we do a nested for Loop here for I in range so for I CL equal 3 to the S < TK of n + 1 okay this
is actually interesting I could use the math. square root function here that would be a valid way but if I want to
stay in integer land which I think I do I can do
um I can instead Square I so I * I I * I and see if it's less than n Plus
right does that make sense so instead of doing I is less than the square root of N I can do I SAR is less than
n right that that makes sense to me cool um because basically we're just saying
we only need to check up to the square root of n we know that if we go higher than that um but like we don't care
about numbers higher than the square root um cool next one is
i++ if I could be multiplied into n so if n mod I oops if if I goes into n evenly so if
that results in a zero n is not prime skip to the next n okay so I can't continue here because
if I continue here here I'll just skip to the next I and I want to skip to the next n so I think what I do here is I do
something like is prime true here I set is prime is
false and break and then at the end of the for loop I can say if not is prime
continue okay okay uh if n is not prime skip to next n yeah so we'll break out of this
Loop right we'll break out of this Loop and then we'll continue
cool now I just want to be clear there are other ways to write this function I'm kind of uh on purpose using lots of
continues and breaks uh so that we can get some practice with it cool um n is prime printed so if we get all the way
to here without any of these kind of guard Clauses being triggered then n is just Prime so fmt print
line n let's run that and just sanity check um our code primes up to 10 2 3 5
7 yep right that's two is a prime number it's kind of like the only even prime number and nine is an odd number before
10 but it's not prime because it's evenly divided into by three right maybe I should have even explained what prime
numbers are in the first place in case you're not familiar um there is uh there is a link here if you want to go read
more about them but basically a prime number is any number where the numbers that multiply evenly into it are
anything except one in itself so if it has any multiples or if it has anything that multiplies into it that isn't just
one and itself it's Prime right so seven you can't you can't multiply two into seven you can't multiply three into
seven you can't multiply four into seven right um so it's Prime um let's just uh look at a couple
more of the examples so up to 20 we got 2 3 5 7 11 13 17 19 right again we're skipping 15 because it has the multiples
three and five primes up to 30 um again skipping 25 um and also skipping 27 because 9
divides evenly into 27 as as just three cool so hopefully that makes a bit of sense and then just kind of as a as an
explanation of of why this pseudo code Works we're skipping even numbers because they can't be prime right if it
if two divides into something it's not prime um we only check up to the square root because anything higher than the
square root has no chance of multiplying evenly into n right so like for example take the number 16 its square root is
four nothing over four could possibly evenly divide into 16 that the square root couldn't right
so like for example 8 divides evenly into 16 but that's only because four and two already
do okay uh we start checking at two because one is not prime one's kind of a special case
number okay so this is looking correct to me I'm going to go ahead and submit it
let's talk a little bit about how arrays work under the hood so if you're familiar with the idea of arrays from
JavaScript or lists from python arrays and go are similar you can think of an array is just an ordered list of items
so we usually denote arrays with square brackets and an array of say three integers might be something like 2 3 1
right so we've got three integers stored in our array the first thing is the integer 2 it's stored at index zero
the next one is a three it's stored at index one and finally we have a one stored at index 2 now here's the big
difference between arrays in go and arrays in JavaScript or lists in Python in go arrays have a fixed size so the
type of this array would be three int so inside of the square brackets we
kind of indicate the size of the array and then after we indicate the type of thing in the array so this array's type
is an array of three integers in languages like JavaScript or again like lists in Python arrays are kind of
dynamically resize they don't have a fixed size you can add things onto the end you can push stuff onto the
beginning but in go arrays are always fixed so to show you what this looks like in code um basically we can create
a new array of 10 integers like this it will initialize all of the indices in the array to the zero value so this
would be an array of 10 zeros basically um if we know what we want to store at each index in the array then we can use
an initialize literal here so we're saying we have an array of six integers and in the first index I wanted to two
and then a three and then a five and so on so let's jump into an assignment and see how this works the assignment says
when a message is not responded to we allow our clients to have up to two additional messages that are sent as
nudging reminders get message with retries returns an array of three strings where index zero is the first
message if the first message is not answered by the recipient we send the second and then we'd send the third
update get message with reti to return the three following strings in an array click here to sign up PR please click
here we beg you to sign up Okay cool so this is pretty straightforward here we're just going to
return an array and you can see the return type up here it's a string array of size three so we just need to create
an array literal and return it and in this case we want an array of strings of size three so array string size three
and we can use these curly brackets um to be kind of where we put the string literal so the first string
is going to be click here to sign up right this is the first message that's sent pretty please click
here and we beg you we beg you to sign up and then just remember that in go you do have to put that last comma um if
you're going to use a new line okay um pretty straightforward right let's see how that
works sending sending to Bob click here to sign up they responded sending to Alice
click here to sign up pry please click here they responded okay this is looking this is looking good to
me going to kind of get a peek at the uh at the test Suite cool let's submit it and see how
we did so we've talked about how arrays are fixed in size and you might be wondering well that's not very useful
why would I care about an ordered list of things if I can't even add to or remove from the list well that's where
slices come into play so let's draw out a simple array I'll do it in yellow let's just
say it's storing some numbers we might have some numbers like six 3 2 6 five and I'll draw the indices let's
do that in pink so the index of the first item is zero and then 1 2 3 4 right and the size of the array is five
which in go we would write like like this we would say we have an array of five items and they are integers Okay
cool so we understand what an array is but what's a slice well slices in go are written with this syntax open close
bracket int and you'll notice that the size is missing okay so a slice is a dynamically sized flexible view into an
array so slices are built on top of arrays so that means for example that I could create a slice that just looks at
this kind of middle portion of this array if I were to write this in code I would basically say if
this array is named a so a is this yellow array here if if I want to create a slice on top of that array then I
could write B colon equals a from we're using square brackets here
index one up to but not including index 4 okay so the first the first number is
inclusive the second number is exclusive and now B is this slice of kind of just that middle view of the array and here's
the important thing to understand in go we actually almost never deal with arrays directly 99 times out of 100
you'll just be working with slices because slices provide a much better developer experience they're built on
top of arrays for kind of memory management reasons which we'll talk about in just a second but you really
want to be working um for the most part with slices because you don't have to worry about that fixed size problem so
just review slices sort of in code um we can create an array literal like this right this is an array because we have
we have the uh size of the array there six integers and then we can create a slice on top of the array like this cool
um let's jump into the assignment so you can kind of get a feel for how this all works okay the assignment says retries
are a premium feature now textos free users only get one retry message while Pro members get an unlimited amount
complete the get message with retries for plan function it takes a plan variable as input that's a string
matches up to one of these um if the plan is a Pro Plan return all of the strings from get message with retries
Okay cool so get message with retries it looks like returns an array of three
strings okay let's just jump right into it if plan is plan Pro then we'll return all messages right
return all the strings yep and a nil error otherwise if the plan is plan
free return the first two strings right the original string and kind of the one the first retry message so return all
messages and we're going to slice it from index zero up to but not including index two so that'll be indexes zero and
one right two two strings and then we'll also return nil and finally if it's neither of those
return an error that says unsupported plan so return nil errors. new unsupported plan and nil um is just kind
of the zero value of slice cool um this is looking good we need to make sure we import the errors
package let's go ahead and run this and see what happens o we got an error cannot use all
messages variable of type array size three string as slice of string Okay cool so we have an
array all messages is an array not a slice right so we actually need to slice this to change its type but we want all
of the values inside of the array so we're just going to use this colon syntax to get access to everything in
the underlying array let's run that again cool sending to
osar click here to sign up pretty please click here so that's two two messages with no
response um and I'm guessing because yep ozer is on a free plan Jess Jeff is on a Pro Plan so he gets all three messages
this is looking correct to me these next few questions will reference kind of excerpts from the effective gobook which
is definitely a book I recommend reading although it is a bit out of date it was written a while ago um and the authors
have basically made the decision not to update um the book over time kind of keeping it as a snapshot so it has a lot
of great stuff in there and go has very strong backwards compatibility so it's still a great read um just kind of be
aware that it hasn't been updated um in a while cool um that said everything we explained here I'll obviously provide uh
context um for so the thing that's important to understand in this section is that slices are references to kind of
what goes on underneath the hood with arrays so what that really means to you as a developer is when you use slices
and more specifically when you pass them around say into a function you're actually passing a reference which means
if you change the values in that slice within a function the caller the person who called your function or I should say
the bit of code that called your function will actually have access to those changes it will see those changes
even if you don't explicitly return the slice again now this is different um from sort of normal primitive values
which are passed by value you might remember us talking about pass by value earlier in the course typically if you
pass in say a string or an integer into a function and then within that function you change it the caller won't see those
changes you have your own copy of that data that's that does not hold true with slices so just understand that when you
pass a slice into a function it might get modified okay cool um so which
references the other is the question do arrays reference slices or do slices reference arrays and the answer is that
slices reference arrays the next question is can multiple slices point to the same array is that true or false
multiple slices point to the same array um that is true remember slices are just kind of a view into an array so it makes
sense that you can have multiple views into the same underlying array so multiple slices can point to the same
array now here's that question um that we talked about earlier a function that only has access to a slice can modify
the underlying array the answer to this is that is true even if the function doesn't return that slice it can modify
the values in the underlying array let's talk about how slices work um kind of specifically how work in relation to
your computer's Hardware or your computer's Ram now Ram just stands for Random Access Memory it's where
variables are stored the best way to think about Ram is just a mapping of addresses so
address to data right it gives us a place to store stuff so for example at address
Z we might have some data that represents I don't know the number four right and then at address one we might
have some data representing the number five I don't know I'm just making stuff up but you get the idea we have
addresses and we have data associated with that address slices and the arrays that they are built on top of are stored
in contiguous memory basically what that means is a slice or an array is an address in memory where where the slice
or array starts so for example at address zero we might say this is the start of a
slice and the slice actually continues for the next you know several uh kind of bytes of data let's say that it's a
slice of length three so it actually would reach across all three of these addresses so if you have an array or a
slice um and its address is zero and its length is three then you know kind of how many uh stores of data in memory um
your slice or array will use now this is primarily important for performance because all of the data is stored next
to each other in memory it's going to be faster to kind of iterate over all of the values in our slice if we stored
each index in kind of random places in memory it would take a lot longer to go collect all of that data just you know
from a hardware perspective however there is a downside to having to store all of our memory um kind of next to
each other let's pretend that we have this slice or this array that starts at address zero and has a length of three
so it's using kind of these three addresses in memory or these three bytes of memory and let's say here at address
3 we're storing a different value maybe we're just storing I don't know the number six um but this is a different
variable this is some other variable the problem is if we want to expand our slice say
add another value to it we're going to run into the next thing in physical memory that's a huge problem we don't
want to overwrite some other variable just because we're growing our slice so this is why arrays are fixed in size if
we just can't grow them then we'll never have this overwriting problem so the the question of course is how do slices do
it right well slices are built on top of rays and basically what happens is I can draw this out for us when we want to
grow a slice whose underlying array has run out of room right so we have our slice um
built on top of this length three array and we want to grow it into a length of four what happens under the hood and
this all kind of happens without you seeing it as a developer you'll you'll see later when we get to the syntax it's
actually really simple um what happens is this data is copied into a new location memory so we just take this
four we put it over here five over here uh actually I'm going to use the same colors that would probably be easier to
understand so four comes over here five comes over here three comes over here and then let's say that we um we know we
want to grow our slice significantly so the new underlying array let's say will have a length of
six now so it had three here now the new array has allocated pre-allocated right we've essentially reserved memory for up
to six spaces let me draw all of that out cool and these addresses over here are going to be totally different right
maybe it's address 15 address 16 17 18 19 and 20 cool so all the data from the old
array gets copied over and now we're able to expand our slice our slice to a length of
four right so here our slice was length three and the underlying array was length three now we've copied over the
data we've created a much larger underlying array and now we have a slice of length four maybe we wanted to append
say the number two to the end of the slice so again as the developer you're not going to have to do all of this
array management manually but it's important to understand that this is what's going on under the hood because
it has performance implications copying data isn't super efficient if you're copying data from one section in memory
to another um over and over and over again it can slow down your programs so now that we've covered all of that
memory stuff and how it works under the hood what does that mean in code right well this is how we can create a slice
without explicitly creating an array under the hood right so this will automatically create an an array under
the hood for us if we use this syntax so this is basically saying I want a new slice of integers I want its initial
length to be five so the length of the slice right the length of the view into the array will be five and the capacity
will be 10 so the capacity is kind of the total space that we have to grow the slice until we need to allocate a new
array under the hood so you could almost think of this capacity is just the length of the underlying array now I
told you that you don't need to think about the size of the under the Ling array and that is true typically you'll
actually use this syntax where you don't even specify a capacity so if you do not specify a capacity it defaults to the
length so the kind of length of the underlying array for a slice of integers with length five will just be five and
if you grow it past that the memory copying will happen and you'll create a new underlying array now you might be
thinking oh that's terrible for performance in reality it's not that bad the only reason you would start to kind
of fuss with specific capacities and optimizing your memory copying is if you're having performance problems
generally speaking the convenience of keeping your code simple and easy to understand is going to outweigh kind of
the performance cost that it'll have it's it's a very small one generally speaking and then it's also just worth
pointing out that we can also create slice literals right so this just creates a new slice slice of length
three um and initializes these three values into that slice rather than what would happen up here which is that we
create kind of all of the zero values um to fill out the slice kind of of the given length so this would be a slice of
five zeros right and then to just point out two more things regarding syntax there is a built-in length function that
Returns the length of a slice and a built-in cap function that Returns the capacity so now that we are experts on
slices let's jump into the assignment says we send a lot of text messages at texo and our API is getting
slow and unresponsive so I just mentioned how you probably shouldn't worry about performance well here's a
case where you should worry about performance right you shouldn't worry about performance until well it starts
to become a problem so we're starting to have a problem with um this memory copying being slow so we've been asked
to preallocate our slices it says um if we know the Rough Side of the before we fill it up we can
make our program Faster by creating the slice with that size ahead of time all right so complete the message get
message costs function it takes a slice of messages and returns a slice of message costs FL 64s right reallocate a
slice for the message cost with the same length as the message slice let's go ahead and start off with that so
costs we'll make a new slice and it's going to be a slice of float 64 and a length of length messages right
same same length cool um now we want to fill the co fill the cost slice with cost reach message so let's go ahead and
iterate over each message so for I colum equals 0 I is less than length of
messages i++ let's get the message at that index messages
zero oh not a zero excuse me at I okay the cost in the cost slice should correspond to the message and the
message slice at the same index the cost of a message is the length of the message multiplied by 01 so cost equals
the length of the message multiplied by 0 01 that's going to need to be cast to a
float 64 perfect cool and then we just need to save that into the costs slice so costs
at I equals cost right correspond with the same index and we know that that um that
index already exists because we pre-allocated it to the correct size cool so now we can just return costs
let's go ahead and run that and see if these make sense okay these costs line up with the
length of the messages so this is is looking good to me let's review the difference between the length and the
capacity of a slice sometimes this can be a little bit confusing the length is the thing that you're going to care
about most often the length just tells you how many things are in that slice right if I have five items then the
length of the slice is going to be five the capacity the capacity of a slice reports
the maximum length the slice can assume right before it gets kind of reassigned into a new array so capacity is really
again only something you're going to care about if you're worried about performance but the length you'll be
concerned about just for kind of normal business logic reasons right just because you want to know how many things
are stored in your slice so to answer this question it says what does the cap function return answers are the last
last element of a slice or the maximum length the slice of the slice before reallocation of the underlying array is
necessary the answer is going to be the maximum length the next question is what does the length function return current
length of the slice or the maximum length of the slice before the reallocation is necessary and the answer
is going to be the current length of the slice and we haven't really talked about this yet but I'll just kind of inform
you as we answer this question says what do length and cap the two functions do when a slice is nil do they Panic or
return zero and the answer is that they return zero they are safe functions to call um they won't make your code panic
and error and crash right um I don't know how much we've talked about panicking up to this point in the course
but panicking just means runtime error that's unrecoverable unrecoverable um generally speaking you
don't want your code panicking and you want to write your code in such a way that it can't Panic um so again length
and cap here um are safe they will never Panic if um a slice happens to be um the zero value which is nil time for some
vartic functions um this sounds like a really complex thing but we've actually been using vartic functions up to this
point in the course because the S printf print F print line all of those functions are actually vartic so a
vartic function receives the vartic arguments as a slice let's take a look at the syntax so here we have a sum
function and its function signature is just maybe a little bit different than you're used to basically we have this
nums uh parameter which is of type dot dot do int now here's the thing dot dot do
int as far as the function definition is concerned is just a slice we treat this just like we would if it said you know
square brackets int it's it's just a slice of integers so you're probably sitting there thinking well why can't I
just use a slice of integers why can't we just keep it simple why do we have to do everything differently ah okay well
uh don't don't worry too much um the difference is on the caller's side so the function definition is the same
whether you use dot dot dot int or square brackets int but if you use a vartic function then the calling code so
the code that uses the sum function can actually pass in kind of any number of arguments and they'll come into the
function as a slice of integers so here we could call for example sum 1 2 3 and the sum function gets a slice of
integers with you know the values one two and three in the first three indexes of the slice um this means that the
caller could also call some one comma 2 right and we'd have a slice of length two so it kind of just gives the collar
of the function a different syntax um and specifically kind of a more flexible syntax uh for how they're able to pass
in sort of a dynamic number of arguments into the function now again we've already been using vartic functions you
probably remember the print line function right this is how we print text to the console um it's a vartic function
dot dot dot interface right so it can take any number of arbitrary inputs and it sort of prints them all out with new
lines in between each element now along with vartic function definitions we also have another operator called the spread
operator and the spread operator is kind of like the inverse of a vartic function by using the spread
operator we're able to take a slice of values and pass them into a vartic function so this isn't the intended use
case 100% of the time otherwise you just Define your your function to take a slice of strings and you'd pass in a
slice of strings but if you do have a function that is already vartic and you want to pass in a slice as the variable
part then you can use this spread operator this trailing dot dot dot to sort of spread out in this case this
name slice into the vartic function let's get our hands dirty with this so it says we need to sum up the costs of
all individual messages so that we can send an end of Monon Bill to our customers complete the sum function so
that it Returns the sum of all of its inputs cool so this is going to be very similar to this sum function right the
the difference here is that we're using float 64s and we're going to write it from scratch uh so that we get our own
our own crack at it okay so first things first uh let's create kind of the default sum which is going to be
0.0.0 so uh let's say total now remember we can treat nums as just another slice of float 64s so 4 I
colon equals 0 I is less than length of nums i++ total plus
equals nums at I right return
total cool pretty straightforward um let's go ahead and run that so summing three costs bill for the
month $6 5 costs 15 10 costs 55 do these match up looks like yep that should be
six 6 + 4 + 5 that's 6+ 9 that's 15 Yep this is looking good I'm going to go ahead and submit that now I told you
that resizing slices is possible we just haven't really done it yet um that's what the append function is for so the
built-in append function is actually a VAR adic function but it allows us to just add new things to the end of a
slice and automatically takes care of adjusting the length and the capacity of the slice accordingly right allocating
new underlying arrays as necessary now here are your syntax options for using the append function if you just want to
append one thing then you'll use this top option let's say we have a slice called slice we want to append a
variable called one thing to it basically we call the append function we pass in the slice we want to append to
as the first uh item we pass the variable that we want to append onto the end as the next thing and then we
reassign back into that same slice now because append is a vartic function we can append multiple things right so um
if we want to append the first thing and then the second thing after that um we could do it like this and obviously we
could also use the spread operator if we want to to be 100% honest though 99% of the time you'll be using this first one
because you'll just be appending items one at a time let's hop into the assignment it says we've been asked to
bucket costs for an entire month into the costs that occurred on each day of the month so complete the get cost by
day function it to return a slice of float 64s where each element is the total cost for that day okay so we have
like this giant list of costs and we need to kind of uh condense all the costs that happened on a specific day
into one index for that day makes sense to me the length of the slice should be equal to the number of days represented
in the costs slice including any days that have no costs up to the last day represented in
the slice Okay cool so if we have costs just for say the first month or the first day of the month and the fifth day
of the month then we should have um kind of five indexes in our resulting array with kind of a non-zero value in the
first and the fifth indexes zeros in the middle and nothing after that okay here we've got an example
um given this input so day cost so we have costs on days on days 0o one and five this would be the resulting array
right $4 on the first day 5.2 on the second day because we sum those right and then on that last day we'd have
2.5 cool let's write the function so first things first we're going to need
um we're going to need a slice to append into so costs by day we'll make a new uh a new slice of float 64 and here I'm
just using the slice literal syntax um instead of the make function they're pretty dang
similar now we're probably going to want to iterate over all of the cost so for I equals 0 I is less than length
costs i++ costs at I okay so a cost is this structure here
and we can take a look at the day there um we're going to need to figure out basically when we're appending to
this costs by day slice and when we're just adding to an existing index so I think the easiest thing to do would
basically be to say um if day is greater than the length of
costs by day actually it's going to be a four so like while the day is greater than the
length of cost by day we're going to append cost by day equals append cost by day
0.0 right so in effect this for Loop here just says if I've encountered a day that I don't yet have room for I'm going
to grow the slice by just appending zeros until I have enough room so once we're done with that Loop we should be
able to assign directly into the costs by day I basically saying cost by day plus
equals cost. value right because it starts at zero so
then we can just add we just add the cost and again if if the day is less than cost by day then we
just don't do this step we just skip over this for Loop so that's looking correct to me I'm going to go ahead and
return costs by day and let's run that code see what we get undefined day what did I screw up
here yeah needs to be cost. day right access that field invalid operation cost by day plus
equals cost. value so cost. value is a float 64 costs by
day is a slice of flow 64s right because this is a slice so costs by
a at at cost. day
right right because our the slice that we're returning um the indexes in each or the
indexes of the slice represent each day in the month so we need to index back into the
day okay try that whoa what do we got Panic runtime error index out of range
zero with length zero okay what did we screw
up so somewhere we are indexing into a slice where the value doesn't like the
the the index that we're indexing with is outside the range of the slice so um we haven't really talked about this this
is probably great that we ran into this bug um if you try to index which is this operation here so like this is indexing
into index five right but here we're indexing into cost. day which holds an integer um if you try to index into an
index that is outside the length of the slice so you have a slice of length say three and you try to access index six
then you'll encounter this error that that's what we're seeing here so it looks like that's what we are doing
so for cost. day is greater than the length now let's think about that if the day is five and the length is
five we are not going to do our growth so I'm actually I think I have an off by one error here right I actually what I
actually want to do is say if the cost if the if the cost. day is greater than or equal to the length right because an
array or a slice of length four actually only has three um kind of indexes I mean it has
four indexes but they start at zero so like length four the indexes are 0 1 2 and three right let's go ahead and run
that again cool no more Panic day one $1 or day Zero $1 day one
510 day 2 250 let's see if that lines up with the test Suite so here it looks like Day Zero
should have one day one should have yep 5.1 if we add those together and day 3 should have 5 point what's that
6.3 yep 6.3 okay this is looking correct to me slices can hold other slices right
this kind of creates a 2d Matrix um of values right so for example here a 10x10 Matrix of integers would look something
like this where the First Slice is just a slice of slices hopefully that makes some amount of sense if not don't worry
we'll get into the assignment here in just a second so it says we support various graphs and dashboards on Tex
that display message analytics for our users the UI for our graphs and charts is built on top of a grid system let's
build that grid logic so this is super common in graphics development right we're kind of building on 2D screens so
we need sort of an X and A Y um cell within a matrix which is again often represented as a slice of slices the
assignment says complete the create Matrix function it takes a number of rows and columns and returns a 2d slice
of integers where the value of each cell is I * J where I and J are the indexes of the row and column
respectively okay cool so create Matrix we get two integers representing how big we want the Matrix to be and we just
have to return the Matrix Okay cool so let's go ahead and start building
that out first we need to create um or initialize the Matrix so let's say Matrix slice let's use make make slice
slice in right and we can just initialize it to a length of zero it doesn't matter
we're going to grow it and four I col equals 0 I is less than length of rows
i++ and then we're going to need an inner for Loop manage J which is going
to deal with the number of columns columns j++ okay so what do we do
um when we get to the first row well the first thing we need to do is make a new slice to represent the row so we'll say
row make and we can just do a a single slice of integers again row uh length of Zer is fine um we could
pre-allocate but it probably doesn't matter too much at this point um and then for each J a we're now
going to append into the row so we do row equals append
row I * J I believe was the formula right we want every cell to be the result of I * J so the values will sort
of grow out um as the Matrix gets larger and larger cool so that's adding the like
this inner loop is adding values to each individual row and then we just need to append the row to The Matrix so
Matrix equals append Matrix row and then when we're done appending
all of the rows to The Matrix we can return the Matrix itself go aad and run
that invalid argument row variable of type int for length see what we got here where did I screw up
this time right I don't know why I'm checking a length here Rose is just an integer it's
not a slice with a size let's try that again cool creating a 3X3 Matrix does that math check
out 1 * 1 is 1 1 * 2 is 2 2 * 2 is 4 yep that looks good cool let's submit it I want to show you a common Pitfall that
you might fall into uh when dealing with slices and how you can just outright avoid it um this is something you should
pretty much never do when you want to append to um other slice in this case you want to append elements to other
slice you should always reassign into the same slice so this should read other slice equals append other slice element
you don't want to be pending um kind of into one thing and reassigning into a separate slice you'll run into some bugs
and we're going to talk about how that works right now so in this first example we have these slices a b and c and by
the way I would highly recommend coming here on bootd so that you can actually kind of look and puzzle through what's
going on in the code rather than just seeing it on the screen um but basically we have these slices and when we append
to them we're we're breaking the rule basically we're uh appending for example here um
four onto a but then returning the value the value returned by a pend we're kind of saving it into a new slice
called B which again you you generally should not do you should just save it back into the same slice a um but
anyways we're going along um kind of doing that pattern and at the end you'll notice that nothing too terrible
happened um basically we ended up with an a with three zeros B had four appended to it properly and C had five
appended to it properly so you could kind of take away from this example well maybe there's nothing wrong maybe we can
just kind of uh break the rule and append into new slices seems like everything's working like I'd expect and
it's worth pointing out that we can even print out the addresses in memory of the slices B and C and see the addresses are
different in other words when we called this append functions where we append five onto um you know the slice a and
save it back into the variable C we can see that actually C is being kind of copied into a new location so again
everything's kind of working as we'd expect now it's really in this example two that something very strange happens
if we look down here we're a pending onto J the value four and then we're
appending onto G here the value five but the interesting thing is that after we append five to G if we print out J again
we can see that J was actually changed kind of under the hood right up here J had four in its fourth
index and then we never touched J again directly but by appending onto G we actually screwed up J we mutated J and
the reason for that is because in this example because of the way we've sloppily used the append function G and
J actually point to the same address in memory so mutating G changed J now I already mentioned that we were doing the
same thing up here right we were using the same kind of sloppy use of the a pend function that I'm recommending to
avoid but we only had the bug in example two and the reason for that is because the original slice in example two has a
capacity of eight while the original slice in example one had a capacity of three so
what that means is when we used the append function in the first example once we went over the capacity we
allocated a new underlying array which is why we got different memory addresses and so when we mutate C here C is in a
different place in memory than b so they're kind of operating independently which is again what you usually want in
your code however because the capacity was already large enough in example two
there was no need for the append function to create a new underlying array so G and J point to the same the
same array in memory again which means if we mutate G under the hood we just mutating J so again the way you can
avoid all of this headache is to just not do this right append onto the same slice pretty much
every time unless you have like some crazy specific reason not to do so which like I'm skeptical that exists I
certainly haven't run into it in you know many years of writing application code in go so the question for this quiz
is why is five the final value in the last index of array J right so why why did we have a five here even though we
appended for um and the answers are J and G point to the same underlying array so G is aend over OJ the go team is
trolling I think that's obviously not it uh and because a pend only works properly when the number of elements is
less than 10 uh that would be very silly so it's it's definitely this middle one we're overwriting the same location in
memory next question on the same topic is why doesn't the bug regarding slices J and G in example two occur in example
one as well and the the answers are because there are fewer elements and goes runtime can't handle more than
eight elements that would be awful um or the array's cap is exceeded so a new underlying array is allocated and and
that one is the answer so the next question is how can you best avoid these types of bugs don't use the append
function always assign the result of the append pend function back into the same slice or always assign the result of the
append function to a new slice well it's not using a new slice that's how we got into this trouble in the first place and
we definitely want to use the append function it's pretty useful so again always assign the result of the append
function back into the same slice you'll avoid a lot of headache that way finally some syntactic sugar to help us iterate
over the elements of a slice you've probably been wondering for the majority of this chapter uh do I have to do this
I equals z i is less than the length every time I want to iterate over the elements of the slice other languages
have syntactic sugar to make it easier so does go so the syntax is pretty simple um this is it right here and
basically by writing it this way index at each iteration of the loop will be equal to the index in the loop starting
at zero right so 0 1 2 3 and then element is the value associated with that Index right and then obviously
slice here is the name of the slice so range is a real the interesting keyword that allows us
to iterate over everything stored in a slice to give a more concrete example here we have a slice of strings called
fruits and we can arrange over the fruits and if we print I and fruit in this example that we print zero apple
one banana two grape and so on let's jump into the assignment it says we need to be able to quickly detect bad words
in the messages that our system sends complete the index of first first bad word function okay so that's this um it
finds any bad words in the message if it finds any bad words in the message it should return the index of the first bad
word in the message slice this will help us filter out naughty words uh from our messaging system if no bad words are
found will return negative one instead use the range keyword Okay cool so the bad words
themselves are defined for us and passed into our function and then the message itself is already broken up into words
it looks like and passed in is an array of strings right so we can I'm kind of just figuring this out by looking uh
looking down here so we have a slice of bad words and a slice of words in the message hey there John Okay
cool so um let's start by iterating over all of the words in the message right seems like a
reasonable place to start so for I word colon equals range message and then we want to check and
see if if one of these words is equal to one of the bad words so we're going to use a
nested Loop actually we'll do J bad word in range bad
words right and then here we can say if word equals equals bad word that means we found the bad word
and we can return we want to return the index so we return I right that would be the index
of the bad word otherwise if we don't find a bad word we'll just keep going um in fact
we'll just keep going through all the bad words and then we'll keep going through all the rest of the
words and if we get to the end of everything without finding any matches then we can just return negative
1 makes sense right cool let's run that J declared not used right um so
we've come across this syntax before we can ignore variables with an underscore right let's run
that let's take a look at our test Suite scanning message hey there John for bad words index negative one that means none
were found right which makes sense we didn't have any scanning message uh oh my freck for bad words brick is a bad
word index 3 okay this is looking good to me I'm going to go ahead and submit that if you're familiar with object
literals in JavaScript or python dictionaries then maps and go are essentially the same thing maps are just
a way to associate a key with a value so let's take a look at this example we create a new map with this syntax here
we're going to use this built-in make function and then pass in the type of the map so map of string to integer so
we're mapping strings to integers and we're going to say inside of the ages map we're going to set the key John to
the integer 37 so again we're mapping that name John to the value 37 rather than creating an empty map and then kind
of adding key value pairs one at a time we can also declare the entire map up front and use kind of this colon syntax
to separate the keys and the values and then we can also check how many keys I guess keys and values are in the map
by using the built-in length function similarly to how you would use it on a slice um so in this case we create a new
map we create two keys each with their Associated values and then by printing the length we'll just print the number
two so let's jump right into the assignment that's usually the best way um to get an idea for how all the syntax
Works assignment says we can speed up our contact info lookups by using a map look up a value in a map by its key is
much faster than searching through a slice so when we look up something in a map by a given key that's going to be an
instant lookup if we had stored it in a slice then we'd have to search through the
entire slice sort of index by index looking for the value that we want so Maps can be a great way to make our code
more efficient the assignment says complete the get user map function it takes a
slice of names and a slice of phone numbers and returns a map of of name to user structs and potentially an error
okay so let me expand our coding window here okay so we're returning a map of string to user structs looks like the
user struct is defined right here if the length of names and phone numbers is not equal return error with the string
invalid sizes the first name in the name slice matches the first number and so on Okay cool so first things first we're
going to have to create a new map so we'll do um user map colon equals and I'm just going to
create a literal a new map literal actually no I won't I'll do I'll use the syntax from up here this works just fine
so we'll use the make function so make map of string to user right so that's now an empty
map and then what we're going to do is check and make sure that these are the same length so if the length of names
does not equal the length of phone numbers then we'll return nil so nil is
the zero value of a map we could also return an empty map of the same type but I'd say you should
prefer nil um for slices and maps and things um and then the error should say invalid sizes so
errors. new invalid sizes we'll import the errors package
Okay cool so if we get down to line 13 now we should know that both of these slices names and phone numbers are of
the same size which means we can Loop um like this so four I starting at zero and I is less than the length of it doesn't
matter because they'll be the same right length of names
i++ then we can say name is names at I and we can say fun phone
number phone numbers at I
okay and then we're going to want to insert values into the user map so we'll do user map at the key in this case the
key is the name right so key is the name and we'll set it equal to a new instance of a user struct so
user and the user ruct has two Fields name which will just again be the name and the phone
number uh which will just be this phone number here
cool so that should fill the entire user map with all of the names and phone numbers and then by the end we should
just be able to return user map nil okay cool I think we got everything
let's go ahead and run that and see what we get so creating map key John value name number key Bob value
name number this is looking this is looking correct to me here we have creating map with invalid
sizes um if we take a look at the tests uh yep that makes sense the second test has two names but three numbers so
I would expect it to get that invalid sizes error cool let's uh let's submit that so the primary way you interact
with maps is just by setting and deleting uh values at a given key right they're very associative right a value
maps to a key but there's no other ordering Maps aren't ordered from you know index 0 1 2 3 uh like a slice is
everything's unordered you're literally just mapping values to Keys Okay cool so let's take a look at some of the
different syntax that we can use to interact with maps so we can insert an element by just setting the key equal to
the value um we can get an element out just by accessing it directly at its key we can delete Elements by using the
built-in delete function where we're passing in the map itself and the key that we want to delete notice that we're
not passing in the value at all we're just passing in the key and then we can also check if a key exists by parsing
the return value of kind of this access syntax into two separate values where the first value will be the element
itself and the second value is a Boolean if the Boolean is true then the element will be whatever element was stored of
that key however if the Boolean is false that tells you that that key didn't exist in the map and the element will
just be the zero value for its type let's jump into the assignment so it says in fact let me uh let me resize
this a bit says it's important to keep up with privacy regulations and to respect our
users data we need a function that will delete user records okay complete the delete if necessary function um it takes
a a map of users and a name which I'm going to guess is the key in the map and it will return a Boolean saying whether
or not the user was deleted and then an error if something went wrong okay cool so if the user does not exist in the map
return the error not found oh interesting so if we're trying to delete something and it's not there um that's
an error if they exist but aren't scheduled for deletion then return deleted as false so we this will be
false um and there won't be any errors um but if they exist and are scheduled for deletion then we'll return uh the
deleted Boolean as true with no other errors okay that's making sense to me and then a note on how maps are passed
into functions is that like slices maps are actually passed by reference so even though this function delete if necessary
does not return a map if we mutate this users map it will be mutated for the color of the function so that's how
we're able to delete something from the map even though we're not returning the map once we're done with it so the first
thing we need to do is just check and see if the name that we're trying to delete exists so let's do this if blank
okay colon equals users at name not not okay so uh this is that
kind of special if statement syntax right um where we're uh actually ignoring the value at self um we're just
interested in that Boolean value telling us whether or not this um this key exists in the map and if it does not
then we're going to go ahead and return uh return the error not found so false because we always do zero values for
everything besides the error and then errors. new not found and we'll make sure to
import that errors package okay cool and again I'm just I'm just uh using this syntax here to check
if a key exists cool moving on okay so if we get past uh past that guard Clause to line 12 then
we know um that the user exists so um at this point ah we need to check if they're scheduled for deletion so I'm
actually going to switch up my syntax here the user struct has the scheduled for deletion Boolean so I'm going to go
ahead and do this on a separate line and I will save that user so I'll do um existing user
and then we'll just do if not okay so that down here now I can use that existing user struct so I can say
if existing user. scheduled for deletion so if they exist and are schedul for deletion return deleted as
true with no errors and delete the record from the map so we need to use this built-in delete function so we'll
delete users name and then we'll return true n all right so if they are scheduled for
deletion we'll delete them and report back that we deleted them cool um otherwise if they're not scheduled for
deletion return deleted as false with no errors so return false
no okay that looks right to me let's go ahead and run that attempting to delete John deleted
JN attempting to delete musk not found Santa not found attempt to delete Kade did not delete Kade Okay cool so I mean
all that look it looks like all the cases were covered let's just come down here and take a
look okay so we had a John and JN was deleted and then attempting to delete musk there's not actually a musk in the
map so not found makes sense um there's also not a Santa and Cade was not scheduled for deletion so he did not get
deleted and then the final map still has those keys that looks correct to me in the this next section we're going to be
covering some of the material from the go blog now I'm not going to read all of this out uh that would be a little dry
but I definitely recommend reading this over if you have the time otherwise I'm going to cover kind of the key points so
the first question is what makes a type qualify to be able to be used as a map key okay so any type can be used as a
map value but not every type can be used as a map key and that's because map Keys make may be of any type that is
comparable so things like strings booleans um numbers all those can be compared for a quality what cannot be
compared for equality are slices maps and functions and kind of one of the reasons for that is slices and maps and
functions they're kind of just pointers to addresses in memory so if you compare one slice to another slice you're not
really comparing the underlying values you're actually comparing kind of where those two slices are stored in your
computer's RAM and because they're stored in different places you'll actually get two
slices with maybe the exact same values stored in them say slice one has the numbers one two three in the first three
indexes and the second slice has the same they might still be compared as unequal because they're stored in
different addresses in memory so I mean all that being said long story short you just can't use slices and maps maps and
functions as map keys so to answer the question what makes type qualifi to be used as a map key the type is comparable
this next question is interesting and kind of plays on the idea that we can Nest Maps um kind of one inside the
other or we could do something that's arguably simpler let me show you what I mean so here is an example of a map of
strings that maps to another map which maps from strings to integers so like the first string kind of maps to the
second string which maps to an integer if you want to think about it that way if you're familiar with Json objects
this would be like mapping Json objects kind of down multiple uh levels of keys so in the example given in the go blog
essentially what's being said is we're mapping um two strings to a count and so what that looks like in code is if we
want to get access to the count we actually have to go to the map index the nested map with a key and then index
into that next map with another key before we get the value out now this looks simple on the surface but what
actually happens is when you need when you go down those nested levels you actually can't be sure that the inner
map exists and So to avoid panicking your code will actually Panic by the way if you try to access a key in a map that
is nil So to avoid panicking you have to check and make sure that those nested Maps actually exist which just results
in a lot of extra code so while nesting Maps definitely works and I've used it quite a bit there can in some instances
be a simpler way which is to actually use a struct as a key notice that a struct was not named as one of the
non-comparable types structs are comparable so if you want to create a map that kind of has multiple keys that
kind of combine together to form a composite key then just create a new struct right with two values inside of
it and those two values kind of unique together create their own key so this simplifies a lot of things where now we
can have one map and we can use that struct key to create kind of uniqueness across two different values this might
be useful for example if you're trying to create a map That's Unique for first last name combinations right you want
Lane Wagner stored in two different fields first name and last name to be unique together so to answer the
question which is simpler to use a struct directly as a key or to Nest maps and um the authors of the go blog and I
would agree argue that using a struct directly is going to be simpler let's jump right into this next assignment
says we have a slice of user IDs okay so those are strings and each instance of an ID in the slice indicates that a
message was sent to that user we need to count up how many times each user's ID appears in the slice to track how how
many messages they received implement the get counts function okay so that's this one it should return a map of
string to int so that each int is a count of how many times each string was found in the slice okay cool simple
enough so let's create a new map so this will be the counts map and it's going to be a map of string to
integer and then let's Loop over all of the user IDs so four blank user ID in range user
IDs we're just we're going to ignore the index because we don't care about it and the first thing we're going to do
is check to see if um a value in the map for the given user ID already exists so count okay colon
equals user IDs sorry counts at user ID
right cool um in fact now like now that I type that out I'm not actually sure that I need this because if I don't use
this this access will still work it's just if if the key didn't exist count will be zero which is fine so actually I
I don't even think I care um cool so we get the count it'll be zero if it didn't yet exist and then we just increment it
by one so count Plus+ and then we'll save it back into the map so counts at user ID equals count so
we're just grabbing it out incrementing it by one and putting it back that seems like what we want to do right and then
at the end we'll just return counts cool let's go ahead and run that and see
what we get Jing counts for 10,000 user IDs Counts from selected IDs 0 0 has 31 f F
has 27 DD has 37 okay and if I look down here it looks like the IDS are being generated
randomly cool so let's go ahead and submit it because this feels right cool we're good to go this next
section is about a piece or an excerpt from the effective go book um that is a book that i' highly recommend reading um
some of the stuff's a bit outdated they've made the decision not to continuously update effective go but it
is an open-source book you can go read the whole thing for free um and the link is right there so go check that out if
you're interested um that said you don't need to I'll be talking through um the parts that we care about here so Maps
can have at most blank values associated with the same key the answers are one any number of three and two well maps
are associative right they map a single key to a single value and you can't have duplicates of the same key in a map that
wouldn't make any sense right then when you put in a key you would maybe get back a slice like that doesn't quite
work so the answer is Maps can have at most one value associated with the same key the next question says attempting to
get a value from a map where the key does not exist Returns the closest Value panics or Returns the zero value um
that's covered here in the section on missing keys but basically to summarize if you attempt to get a I mean we
actually covered this in the last um the last coding assignment but if you attempt to access a key in a map where
the key doesn't exist you'll just get back the zero value so for example if it's a map of strings to integers and
you access a string that doesn't exist you'll get back as the value zero and that's kind of nice just because
accessing values in a map is a safe operation your code will not will not panic so to answer the question it
Returns the zero value and it's also worth pointing out really quickly that if the map doesn't exist so not that the
key that you're trying to access doesn't exist but if the map itself doesn't exist if it's a nil map then your code
will Panic so that is a dangerous operation you want to make sure you're always accessing values in maps that
have been initialized the next question is a function can mutate the values stored in a map and those changes blank
the colar affect or do not affect the collar now the answer to this one is in this first section that Maps like slices
are references so when we pass a map into a function that function can change what's in the map and those changes will
be visible outside of the function this is different from Primitives right strings integers um booleans those are
all passed into functions by value if you mutate them within a function uh the the the CER of that function will not
see those changes unless you return those values but the same does not hold true for slices and maps so to answer
the question uh a function can mutate the values uh stored in a map and those changes they do affect the CER the next
question says what does the second return value from a retrieve operation in a map indicate so let's take a look
at this code example here so we've got this time zone map where we're accessing the map at a given a given key and it
can return two values the first value is the value associated with the key and the second value is a Boolean right we
used this in the last coding assignment so a Boolean that indicates whether the value at the key is a nil value nope
that's not accurate a Boolean that indicates whether the key exists that's the
one let's practice with some nested Maps so like we talked about earlier Maps can contain other Maps as their values not
as not as their keys right so let's jump into the assignment says because texo is a glorified customer database right I
mean we're sending sms and email messages to a giant list of customers or basically just a big customer database
um we have a lot of internal Logic for sorting and dealing with customer names complete the get name counts function it
takes a slice of strings names and returns a nested map this is going to be very similar to the last
assignment we did um but this time it's going to be a nested map where the first key is all the unique First characters
of the names and the second key is all of the names themselves so this could be useful if for some reason we wanted to
get access to all of the names that start with a very quickly so to kind of visualize this we've got this example if
the input names slice is this slice here Billy Billy Bob Joe it would create the following nested map so the first key is
just a letter the second key is the full name and then the value is um the number of
times that name showed up in the original list and then it's worth pointing out here that the return value
is a map of runes to a map of strings and integers in go we often just represent individual characters as runes
rather than strings of length one it just gives us a little more um kind of assurity in our type system okay so
let's start by creating a new top level map so we'll call it counts and we'll make that full map now it's important to
understand because this map contains Maps inside of it we we will need to continuously initialize new maps I'll
show you what that looks like so now let's just uh loop over all these names so
for um we don't care about the index name and range names Okay so we've got a name the first
thing we need to do is actually check and see if we already have a map associated with the first character of
this name so let's get that first character let's do um if we probably need to do some safety checking so like
if length of name actually the easiest thing to do would just be if name is the empty
string then we'll continue we don't care about blank names they they don't do anything for us right
so we'll just we'll just skip them great if it's not the empty string then we can say first character is name zero okay so
that gives us the Rune at um the first index in the name and then what we can do is look up
in our map so counts at first oops counts at first character um we can see if the inner
map exists so if it does not exist not if not
okay let's just keep it simple if it doesn't exist we'll just initialize it so if it does not exist we'll say counts
at first character equals and we'll initialize the new inner map so we just need a new
map of string to integer at that first character key right right in fact to be explicit I'm
going to ignore the inner map there so by the time we get down here to line 18 we should be 100% certain that
counts at first character contains an initialized map so then we can just simply do counts at first character
at name Plus+
right because we've we've made sure that the inner map exists and if the name key doesn't exist it will return a zero
which we could then increment and save back into the map so that should work just
fine at the end we can return counts cool hopefully that makes sense and we've got some test cases down
here Matthew George Drew Philip Bryant and then a big list okay cool let's run it and see what
happens whoops cannot use first Char variable of type bite as Rune value right so when you you index into a
string right so name is a single string in go then it is a bite type um but we want a rune type
so going go ahead and cast it and run it again cool uh Jing counts for 50 for the
first 50 names count for M Matthew is three G George is One D Drew is four we don't have any pan X so these counts
look totally plausible to me let's go ahead and submit it it's time to talk about first class and higher order
functions which are just really kind of confusing complex terms for a much simpler idea which is functions as data
a programming language is said to support first class functions if it allows you to pass around functions just
like you pass around any other variable storing essentially an entire function at as a value and then a function that
uses that first class function so a function that accepts another function as a parameter or returns a function as
a return value is said to be called a higher order function let's take a look at a concrete example okay so here we
have two functions add and multiply um these are very simple functions you should be able to understand uh what
they do just by looking at them for a second um but here we have kind of an interesting function it's called
Aggregate and it takes as input three integers a b and c and it takes as input an entire function right so it takes a
function and we're calling it here arithmetic it's the fourth parameter to the aggregate function and this function
has a specific signature this function arithmetic must take as its parameters two
integers right and return return an integer itself and then the aggregate function just returns an integer so what
does the aggregate function do well it calls the function it was given Twice first it calls it once with A and B and
then it calls it again with the results of A and B and C so for example if we call aggregate with the numbers 2 3 4
and the function add then it will add all three of those numbers together right which would in this case print n
and then we can also use that same aggregate function with the same numbers 2 3 and four but this time pass in that
multiply function and here we'll get 24 right because we're multiplying um all of the variables together instead of
adding them it's okay if this is a bit confusing uh if this is the first time you've worked with functions as data
take a second to really kind of stare at this code and and figure out these this crazy function signature um it's
actually not that complex once you kind of get past the scary syntax um but but don't feel bad if you have to pause the
video and take a good look let's get our hands dirty with this assignment says texo is launching a new email messaging
product maleo I guess uh SMS and email right um fix the compile time bug in the get
formatted messages function the function body is correct but the function signature is not okay so this formatter
function here looks like it will be a problem let's go ahead and run the code and see what kind of an error we get so
this is a compile time error uh saying we have a syntax problem and that makes sense because here we're we're basically
saying well the formatter input is a function but we're not saying what type of function it is and that doesn't work
because get formatted messages is going to use the formatter function so it needs to know essentially what
parameters it takes as input and what it's going to return right uh kind of like uh the arithmetic function here
takes two integers and returns an integer uh we need to update this function signature to kind of inform the
get formatted messages function what this formatter function actually does so if we look at the code because we know
the function body um is correct it looks like formatter accepts a messages input which is a
string and it's going to return whatever should be appended to this formatted messages slice which is a string so it
it looks like it takes one string as input and and returns one string as output that kind of makes sense for a
formatter function um so we say takes a string and it returns a string let's go ahead and run that and
see what we get cool at least it compiled says thanks for getting back to me which return thanks for getting back
to me kind regards Okay cool so the ad signature ads kinds to the end and add greeting
thanks for getting back to me hello thanks for getting back to me cool let's go ahead and run
that awesome so this might seem like an exercise in complexity right like why do I need to pass around functions as data
that just seems to add a bunch of well needless complexity and for the most part you're right you really should only
use higher order and first class functions if you have a very good reason to do so so the question is kind of what
are the good reasons to do so well first class and higher order functions are very often used for um on the back end
side of the stack HTTP handlers right so if you have some front-end code that needs to reach out to a backend server
and get some data those handlers are typically first class or higher order functions because we need some code
right the Handler function to run like in the future we don't want to call it now right we want to call it when
something happens in the UI World kind of on the on the front end side of the stack uh they're often used in onclick
handlers right so I write a function and I don't necessarily call it when my program starts but I call it when
something interesting happens like the user clicks a button right when a button click happens I want to call you know
this function and so typically in code we can represent that with a higher order function we say onclick do this
and then we kind of give it the name of a function to call and then just to review the definitions uh very quickly a
first class function is a function that is kind of being passed around as data and a higher order function is the
function that's using that first class function right it's a function that accepts a function as input or returns
it um as one of its return values so the question is what is a higher order function um it is a function that takes
another function as an argument or or returns a function um but it's not a function that is first in the call stack
or a function with Superior logic so the next question is what is a first class function um it is a function
that is treated like any other variable right it's a function that we're going to pass around as as data to probably be
called sometime in the future function currying is kind of like a special kind of higher order function it's a function
that accepts another function as input we're kind of familiar with that idea but that also returns a new function as
its output so it's kind of a way of like enhancing a function with new Behavior it's kind of a weird concept to think
about abstractly so let's jump into this example okay so here we have a function called self math and self math is the
curried function it takes a math function as input that accepts two integers and returns an integer and it
returns a new function that only takes a single integer as input and returns an integer and then what it does again it
returns a function right that takes a single integer an input and returns an integer and it calls the math function
that it was given with the same input twice right so we're kind of mapping a function that accepts two uh different
integers into a function that kind of forces both of those integers to be the same integer so like what does that do
in practice well basically we can use our self math function to convert a multiply function into a square function
right so multiply takes X and Y and multiplies them together this new Square function that we created dynamically
only takes a single value and multiplies it by itself right um same with the add function we can kind of use the self
maath function to convert it into a function that just doubles its input right so we Square Five we get 25 we
double five and we get 10 so when would currying be used in the real world uh to be honest I don't use it very often um
but in backend server land I do sometimes use it for middleware functions so a middleware function is a
function that basically changes the HTTP Handler of a backend server um and just as a spoiler we will be covering this in
the project at the end of this course um but it's for kind of injecting some additional logic into a function so say
we have an HTTP Handler that accepts as input a user ID and returns an entire user object with you know say their
first name their last name that sort of thing um a middleware function might do
something like require an authentication token right so we can write all of our HTTP handlers that serve different data
sort of independently and then we can use a curried function to kind of require authentication Logic on all of
our HTTP handlers um in fact in go currying is very often used to handle the sort of middleware problem now if
all of that went straight over your head again that's okay it's kind of hard to talk about um something that we're not
working on at the moment so again we're going to cover middleware and HTTP handlers in the project at the end of
this course so stick around for that so jumping into the assignment it says the maleo API needs a very robust error
logging system so we can see when things are going arai in the back end we need a function that can create a custom logger
a function that prints to the console given a specific formatter Okay cool so this function get logger should return a
logger which is a function right it's a function that takes two strings as input and apparently prints them because it
doesn't return anything right and get logger takes as input a formatter which accepts two strings as input and returns
a single string okay so it says complete the get logger function it should return a new
function um the inputs should be passed into the format or function the order they given to the loger function okay
so let's start writing this so we're going to return a function that takes two strings as input
right and I'm going to reference up here for the syntax we're interested in so I'm going to do a
string B string all right and this function should return nothing so we can just go straight into the
body and it says this function prints the formatted inputs so formatter returns a single
string so we can just do fmt do print line and we're going to need to oh we already have fmt imported perfect so we
need to print the result of the formatter functions it's going to be formatter and we'll pass into the format
A and B right so we're returning a new function a logger that accepts two
inputs formats them given the formatter that we were given right and then just prints it to the console cool let's go
ahead and run that error on database server out of memory error on database server CPU is
pegged this all looks good let's see how the test site actually works it looks like these are the
formatters colon delimit and comma delimit and if you take a look at our messages some are delimited by a colon
some are delimited by a comma so that's how that is working very cool all right I'm going to go ahead and
submit that I think this is correct time to talk about the defer keyword uh this is a really kind of
unique thing to go um if you're familiar with any other programming languages it's unlikely um that you're familiar
with something similar to the defer keyword um at least I've never used it or used a concept uh that is similar to
the defer keyword in another language um fairly go specific okay so the defer keyword allows us to
execute some function at the end of the current function or when the current function exits the defer keyword is very
often used as sort of a cleanup step um for example in this in this function here copy
file we open a source file from the file system and then we defer closing the file right so every time you open access
to a file in the file system you need to remember to close that file otherwise you're sort of wasting Computer
Resources um the problem is closing the file at the end of the function is a little tedious because we have multiple
return statements here so we'd kind of have to close it before both of them if that makes sense by using the defer
keyword we can just tell the go programming language hey I want to call source.
close right before the copy file function ends and just defers it kind of until the end and it will only call it
once um no matter where the function actually returns from on to the assignment it says there is a bug in the
log and delete function fix it let me expand this a little bit so we can see it this function should always delete
the user from the users map Okay cool so we're given a user's map we're given a name and we know that Maps uh are passed
by reference so if we delete a map uh it will be deleted in the callers code as well um it should return log string that
indicates to the caller some information about the user's deletion okay so delete should always happen and then the kind
of the appropriate log message should be returned from the function that makes sense um but it looks like there's a bug
okay so let's go ahead and run the code and see what we get so initial users Brianna Elon John
Cade attempting to delete John deleting Santa deleting Cade okay so John's still there the
problem is that we're trying to delete JN but Jon's not actually getting deleted right so we need to fix that
bug and if we take a look John is an admin and here we are returning log admin but we're not deleting admins so
so that's that's the real problem right the users should always be deleted now here's the problem we can do
this um this will work if I run this my guess is this should fix the bug yep JN is no longer there he was deleted
successfully but this is kind of gross right we we're calling delete three different times um what if in the future
we add another case and we forget we forget to add the delete again we'll have another bug um well what we could
do what we could do is just delete at the top once
right but the problem is if we do that we delete the user from the users map too soon and now this okay variable
that's checking for existence so it can change which log is returned like that logic won't work it will just always be
not okay so we'll always just return log not found so that's a problem what we can do is defer the deletion so this
code says we'll call this delete function right before login delete returns so it it's almost like um the
equivalent of you know adding this at every step of the way so let's go ahead and run that make sure it
works John's there John's gone perfect and we have different logs getting returned so that looks good I'm going to
go ahead and submit that this chapter is not called Advanced functions uh for no reason let's talk about closures
closures are um I don't want to like intimidate you and say they're really hard but they're a little weird so let's
let's take a second to understand uh how they work okay a closure is a function that references variables from outside
its own function body the function May access and assign to the referenced variables so in this example the concat
function returns a function that has a reference to the enclosed doc value okay so concat is a function it returns a new
function right and that's what's happening here and it's enclosed using this doc
string within the function so it's getting initialized outside of the function and then getting used within
the function and what happens is basically when we call concat to to make this new like concat
function we're saving a reference to this doc variable so every kind of concurrent not concurrent every uh
subsequent call to this function that is returned we'll keep adding on right plus equals word we'll keep adding on to the
same doc variable so let's look at what that uh kind of looks like in usage so we're calling concat and we're getting
back this Harry Potter aggregator right that's what we're assigning this function we're assigning this function
into this variable so Harry Potter aggregator is a function right it's it's specifically it's this function right
here cool and then we're going to call it with mis and then we'll call it again with and and Mrs dersley Right we're
calling it over and over and over and what's happening under the hood is we are appending those words Mr and Mrs
onto that doc variable so at the end when we finally print what's being returned which is the
doc variable itself we get the full sentence Mr and Mrs Durley of number four privet drive now closures are one
of those Advanced things that you to be honest again don't usually use in production code maybe every once in a
while um but they're very important to understand because it is very common to have kind of bugs surrounding closures
so if you don't understand what's going on under the hood it can be really hard to debug kind of complex code that might
even be using closures by accident so the assignment says keeping track of how many emails we send is Mission critical
at maleo complete the adder function it's return a function that adds its input an integer to an enclosed sum
value and then Returns the new sum in other words it keeps a running total of the sum variable uh within a closure
Okay cool so it's going to be very similar to this right so we'll start by creating a sum value so sum colon equals
zero right we're we're working with integers not flows and then we need to return a function with the signature so
return Funk X int returns an INT oh not not returns an X returns an INT and then here we need to add X to
sum right so sum plus equals X and then we need to return the new
sum right and then if we go take a look at this test case let's see okay so we're adding these email
bills with a bunch of different numbers the test Ates over the bills ah okay so we're creating two adders here
so we're creating a count adder and a cost adder and then let me expand
this going to just make that basically full screen so the test actually creates two
different adders two different instances of our Adder function right and one of them is going to count how many bills
there are and the other one will keep a running total of the cost of all of the builds
right so we're actually using our function twice for two different things and they'll each have their own enclosed
some count that they keep track of differently right the count will have its own the cost will have its own the
count is simple it just adds one every time the cost will add the actual cost in pennies cool let's go ahead and run
this so um and these values are getting interpolated into this message so youve sent one email cost 45 cents two emails
77 cents three emails 120 cents so like all of these are going up that makes sense I mean we could even go verify
right 45 + 32 77 that looks right to me let's go ahead and submit it all right we're on to a little quiz about closures
can a closure mutate a variable outside of its body uh yes that's basically the entire point of a closure another little
review question here says when a variable is enclosed in a closure the enclosing function has access to blank a
copy of the value or a mutable reference to the original value well if it was a copy then our sum never would have
worked right because we'd be working with a new copy of the sum variable every time so it's actually a mutable
reference to the original value we've already been using Anonymous functions kind of all throughout this chapter um
now let's kind of just talk about what they are so Anonymous functions are exactly what they sound like they're
just functions that do not have a name they have no name um Anonymous functions are really useful when you're kind of
just oneoff maybe creating a closure you're returning a new type of function um if if you're not defining the
function for like use across the entire program but you're more using it as a value using it as a first class function
um that's when you're going to see Anonymous functions use the most so as an example um here we have an anonymous
function declaration right we're creating a new function we're defining its internal logic it's its function
body um but we're not giving it a name right there's no name uh for this function that we're passing into do math
it is an anonymous function the assignment says complete the print reports function call Print cost report
once for each message okay let's take a look print cost report looks like it takes as input a function cost
calculator great okay um call Print cost report once for each message pass an anonymous function as the cost
calculator that returns an int equal to twice the length of the input message okay let's go ahead and do that
so um it says to do it once for each message yeah so four
message range messages call Print cost report print cost
report and print cost report takes a cost calculator which is a function that takes us string and returns an
INT said to use an anonymous function right so funks we need to actually Define our X string int we need to
define the body um that returns an INT equal to twice the length of the input message so X is a string in fact I'm
going to name this um message and we'll return the length of message time
2 right twice the length of the input message and then print cost report takes us second parameter which is the message
itself right I think am I am I reading this correctly cost report FES the calculator
and the message itself yep because it's going to it's then going to call the cost calculator and then and then print
kind of a little report okay cool um that's looking pretty good to me we don't return anything from print reports
okay let's go ahead and run this so message here's Johnny cost 28 cents go ahead make my day 42 cents you had me
at hello 38 cents that's looking correct right there's 19
28 not 19 what's half of 28 14 14 characters there looks good to me okay let's go ahead and submit
that let's talk about pointers and in order to understand pointers we need to um talk a little bit about Ram or memory
right Random Access memory which is basically the part of the hardware in our computer that stores data right
because pointers and variables are all about kind of how we store data in the memory of our computer let's start with
something simple let's create a new variable in our
program and let's call it X and we'll set x equal to 5 under the hood what happens
automatically when we create a new variable uh and set it equal to five is somewhere in our computer's memory that
variable's value is going to get stored so let's say uh for the sake of the example that it's stored here in memory
address 169 Now Memory in your computer you could think of it as fairly simply a memory address that stores a value so
we've got kind of you know millions of different memory addresses in which we can store data and somewhere in memory
uh that value needs to live so let's just say it gets assigned again automat aut atically um to address 169 great now
what happens if I create a new variable let's call it Y and set it equal to the current value of
x y actually gets a new copy of the value so X lives here at address 169 y let's just say is going to live at
address 170 and we get a copy of that five so down here in this table this is where we're going to keep track of all
of our variables we'll say that X so X is the variable name lives at address 169 and it stores the value
five now y lives at address 170 and stores also the value five at
its most basic a pointer is just a variable that stores a memory address so let's say for example that we create a
new variable called Z and we set it to point which is uh which uses the Ampersand I can't draw an
ampersand pretend that that's an ampersand uh the the the a reference to X right it points to x what that does is
that we're creating a new variable called Z it's going to get a new address
anytime we create a new variable it's going to live in a new address in memory let's say it lives at address
171 but its value instead of being five instead of being a copy of X because we used kind of the pointer syntax the
reference syntax the value is going to be the address of
X so we're going to store 169 which is the again the address of X as the value and Z is going to be a
pointer type so kind of the way the go programming language works works is it knows that Z is effectively pointing to
the value five right because its address is 171 which stores a pointer to 169 so we
can look up the value five at address 169 so what this means is if we update the value that Z points to under the
hood we're updating X so we can do things like pass pointers into functions change the underlying value and the
value outside of that function is also changed which again in a language like go doesn't happen normally because
typically if you pass a variable like X into a function and then change it those changes are not seen outside of the
function you would need to return the updated value and then assign it into a new variable so pointers basically allow
us to change the value of something from within a function right from within a different scope let me show you what
that looks like in a quick example so remember that Z stores the value 169 which points to this
address which stores the value five so if we want to update the value of x without having access to the
original variable x what we can do is use the dfference operator which is an asterisk we can dreference Z now this
dfference operator essentially follows this chain and finds the value right and we reassign into it let's say the value
six so this becomes six and this location and memory is updated now to hold the value six so we've updated the
value of x without even without even using right the name X so now that we've kind of covered what a pointer is let's
look at just a little bit more of the syntax in code so the type of a pointer is not the type of the underlying value
alone so if I want a pointer to an integer um I actually have to use this syntax here I'm creating a new variable
called p and it's being initialized as a pointer to an integer so a pointer is a specific type in go now to be fair um
it's not super common that you're creating blank pointers and the zero value for a pointer is nil um more often
than not what you're going to do is have a concrete value like this my string hello and then you'll create a pointer
to that value by using the Ampersand like we talked about so in this case the type of my string is string and the type
of my string pointer is a pointer to a string which would be syntactically star string there kind of two primary reasons
that you would use a pointer in the go programming language the first is the more common reason which is you want to
be able to pass a value into a function and change the value and have those changes kind of persist outside of the
function right because normally when you pass a value into a function a copy is passed in so if you want to pass in sort
of the original value so it can be changed and updated uh you might use a pointer the second reason is if you're
very concerned about the performance of your program every time you create a copy of a variable in memory you have to
copy that variable in memory right which takes some time so if you're dealing with lots and lots of data and you're
trying to be very performant you can make micro optimizations and kind of use pointers under the hood if you want to
avoid all of that memory copying now I will say that you usually won't want to do this upfront because pointers are
dangerous and they can lead to bugs if not used properly so generally speaking I would recommend against making those
performance optimizations unless you really need them let's jump into the assignment says fix the bug in the send
message function it's supposed to print a nicely formatted message to the console containing an sms's uh recipient
and message body however it's not working as expected okay so let's run the
code and we get these kind of weird weird looking values that are getting printed here in the two and the message
Fields now this is heximal it's not binary it's not decimal it's hexadecimal and this is the default formatting for a
memory address so we have these amp perents here they're creating pointers to the underlying values that's not what
we want it's not what we want here right that's why this looks disgusting uh we need to dreference or or sorry we need
to remove the reference so I guess D reference um we need to remove the amperian so
that we are no longer creating pointers so run it again and we get the values themselves which is what what we've been
asked to do let's recap some syntax sometimes it can get a little confusing between the
asterisk and the Ampersand what each of them mean and in what context so when we're talking about the type of a
variable in go a Pointer's type is star and then you know the type of the underlying value so a pointer to an INT
is star int an ampersand is used to reference a value so if we want to create a pointer to the my string
variable or the my string value then we use an ampersand so the Ampersand followed by the name of a variable
creates a pointer to that variable now here's where it can get just a little bit tricky the asterisk is again used to
dreference a pointer so the asterisk is used in a Pointer's type it's also used as an operator to dreference a pointer
so when we say say asterisk my string pointer this refers to the underlying value so we can actually change the
underlying value Say by assigning it to in this case a new string so in short ampersands to create new references or
new pointers to a value and the asterisk is used to dreference a pointer and get back at that underlying value let's hop
into the assignment it says complete the remove profanity function it should use the strings. replace all function so
this is a a built-in uh function in the standard library in the strings package uh to replace all instances of the
following words in the input message with asterisks it should mutate the value in the pointer and return nothing
do not alter the function signature Okay cool so remove profanity takes as input a a a a message variable which is a
pointer to a string so because it's a pointer we're going to be able to mutate it without returning anything explicitly
okay let's jump into it so first thing we're going to do is dreference the message pointer and store its value
in a new variable called message value and then we'll just update this um by using that strings. replace
all functions strings. replace all we want to replace the values in message value we want to or I should say
we want to replace subrings from message value uh we'll look for the word dang and we'll replace it with four
asterisks and we want to do the same thing twice more with shoot which has five letters so let's add an asterisk
and heck which has four so that should be good cool let's let's run this I need to import the strings package before I
forget and see what happens so in its current state we actually did not update anything right shoot is still
there dang is still there that's because we actually need to point the
message pointer to a string back at the updated value so we can do dereferenced message
equals message value let's run that perfect let's submit it now we've got a pointer quiz so the
question is what is the value of Ampersand y after the code on the left executes so we've got X set to 50 Y is a
pointer to an integer okay well in this case it's just going to be 100 because we're explicitly
setting it to 100 on the last line of the code so that one was pretty easy now this question is a little trickier says
what is the value of x after the code on the left executes well X is set to
50 Y is a pointer to X and then we dreference y instead of equal to 100 so that's actually also going to be 100
because we're setting X through Y which is a pointer to X I mentioned earlier that pointers can be dangerous and that
is definitely the case um if a pointer points to nothing then its zero value is nil right so this is the same for
interfaces or errors right in go fairly often you'll be checking at runtime if a value is nil or not and the thing about
pointers is if you ever try to dreference a pointer that doesn't point to anything your code will Panic so
pretty much any time you dreference something you should be checking before you dreference it to make sure that the
pointer actually points to a valid location in memory so this assignment says let's make our Prof profanity
Checker safe update the remove profanity function If the message is nil return early to avoid a panic okay so let me
run it in its current state and you'll see we get this nasty Panic here it says invalid memory address or
nil okay so what we need to do here is if message equals equals
nil return nothing right if we've been given an invalid input we'll just bail early um another way to do this um if
the assignment expected something different we could return an error here right we might do something like return
errors. new uh invalid input I think that would also be a good way to write this function uh but we've been asked to
just do a naked return so we will do that let's submit it so let's talk about how pointers are used in conjunction
with methods so you'll very frequently see that methods actually take a pointer receip
rather than a non-pointer receiver and typically that's done because the method will be making changes to the instance
of the type itself in this case a struct right so we have this car with a color field and the set color method on the
car is going to change the color right and so here if we create a new car we set the color to blue and then print it
you can see it's been updated to Blue instead of white now contrast that with a non-pointer receiver
this acts like a normal function right we don't have a pointer so we don't have a reference to the location in memory so
if we update the cars color to Blue it actually just stays white it doesn't persist that change so that's why I say
that it's uh more common that you'll see pointer receivers on methods than non-pointer receivers but but you will
definitely see both so uh the question is which is more widely used in go and the answer is pointer receivers now when
it comes to pointer to receivers one thing that's important to understand from like a syntactic point of view is
that even though the uh the input on the left hand side right the receiver is a pointer when you actually call the
method you can call in this case right the grow method you can actually call it on just a normal value or a pointer it
will sort of cast the value under the hood to a pointer if it isn't one already right so C in this case is just
a circle it's not a pointer to a circle but when we call c. grow the pointer to the circle is passed into the method so
we didn't need to kind of explicitly you know cast c to a pointer to a circle by you know adding an Amper sand right here
okay so with that understood um let's move on to the assignment so the assignment says fix the bug and the
codes that set message sets the message field of the given email structure and the new value persists outside the scope
of the set message method Okay so we've got this email struct we've got the set message uh
the set message method if I run the code in its current state we've got before message this my
first draft Sandra Bullock after looks identical right and if we go and take a look at the test Suite then we can
see that set message is being called this is my second draft so we would have
expected this message to say this is my second draft instead of this is my my first draft the reason it's not
happening is because this function essentially doesn't do anything this method does nothing because this is not
a pointer to an email let's go ahead and run that again first draft second draft perfect
let's submit that everything we've done up to this point in the course has been in the
browser we've been writing code right in our text editor on boot Dev now we're going to break out of that environment
and do some local development on our own machine we're going to figure out how to use the go tool chain to build real
production go programs but before we do that let's talk a little bit about packages we need
to understand packages in order to build our own go programs now you've probably noticed that up until this point in the
course every coding assignment has had the words package main at the top of the file that just means that we've been
writing code within the main package and that's actually really important the main package is a special
type of package it's a package that runs as a standalone program so anytime you're writing an actual application
that you need to run say on the command line or as a server you'll be writing code within the main package a package
with any other name so any name besides main is a library package and basically what that means is it's imported by
other libraries and application code so some oftentimes it's just imported by a main package so that it can use it if
you're familiar with npm from JavaScript or pip from python that's effectively what a non-main package is in go it's
just some code that you can publish as kind of a standalone library that then other developers can use in their main
packages in their actual applications so let's take a look at an example here so you're probably familiar with this style
of code it's what we've been writing uh all throughout this course we've been writing within the main package because
we've been writing executable scripts right code that actually runs and does something and then we have this import
statement where we've typically just been importing from the standard library but the standard library is made up of
Library packages right like the fmt package or the math SL random package the interesting thing about the main
package is it always has a main function and that main function serves as the entry point to the program so packages
that are not main packages their Library packages will just export functions named functions to be used Again by
other libraries and application code only main packages will have a main function that runs when the program
starts so to jump into the coding assignment it just says fix the bug in the code so let's go ahead and run the
code and see what happens nothing happens right we're stuck in this infinite Loop apparently apparently
this code compiles and then does nothing it's kind of a weird Behavior right um in order to fix this all we need to do
is update this to be a main package so that we get as output for our code a runnable
executable a script that we can actually execute in the browser so let's go ahead and run that and we get starting maleo
server stopping maleo server kind of as we would expect uh based on uh based on this code here so I'm going to go ahead
and submit that so we're familiar with main packages but what about Library packages well by convention a package's
name is the same as the last element of its import path so for example the math Rand or math SL Rand package from the
ghost standard Library um has files that begin with package Rand so the Rand package lives at math SL Rand now it's
important to point out that the standard Library actually has another Rand package at crypto Rand so they're both
kind of the Rand package but they have different import paths when a package isn't part of the standard Library when
for example you make your own Library package the import path is typically the same as the remote URL that you you'd
use to go look at that library's source code so for example in our fictitious maleo products right part of the textio
conglomerate of messaging products uh they might have their own GitHub namespace it's slmo so github.com is
their GitHub organization and then maybe they create their own package or their own excuse me repository on GitHub
