Learn Go Programming - Golang Tutorial for Beginners

**Understanding Go's Channels**

And that allows you to balance the performance between senders and receivers. So if you can generate data 10 times as fast as you can process it, then you can create 10 times as many receivers. And that way you can balance the workload out between senders and receivers.

We then talked about how to restrict data flow. Buying default, channels are bi-directional constructs, so you can send and receive data into a channel. Now very often what we want, though, is our go routines to be designed to handle channel data only in one direction. So we saw that we can do that by passing in the channel. But then on the receiving side, we can actually specify the direction that we can work with by again adding that arrow, and we either add it before or after the chain keyword. Depending on what kind of channel that we want to make, we can make a send only channel by putting the arrow after the chain keyword, and we can make a receive only channel by adding the arrow before it.

We then talk about buffered channels, and how buffered channels contain internal data stores that allow us to get around this limitation of channels that by default, a channel will block the sender side until a receiver is available, and the receiver side will be blocked until a message is available to come out of the channel. So you can actually block a goroutine on the sending side or the receiving side of the channel. So if there's no position available in the channel to add the message, then the sending side will be blocked until a space does become available. And if there's no message in the channel, then the receiver side is going to be blocked until a message becomes available for it to work with.

So in order to decouple that we can add an integer as a second argument to the main function. And that's going to allow the channel to have an internal buffer to decouple your senders and receivers, just in case there are situations where data is generated faster than it's received. So just like it says here, we want to use buffered channels when sending and receiving have asymmetric loading. So if we can generate messages faster than we can receive them, then a lot of times a buffered channel is a really good way to go.

We then moved onto talk about four range loops, and specifically how to work with them with channels. And we learned that they basically work the same way. But there are a couple of subtle differences. The first thing is the first parameter that you're going to receive from the four range loop when working with channels is the value itself, not the index, like we saw when we were using for range loops over arrays, slices and maps. And we saw how we can use for range loops to monitor channel and process messages as they arrive.

So the four range loop is just going to keep pulling messages as they come in off the channel. And it'll process them as they come. Then when the channel gets closed, the four range loop is going to detect that and it will go ahead and exit the loop.

Finally, we talked about SELECT statements, and how they work kinda like switch statements, but they work only in the context of channels, and how they allow a go routine to monitor several channels at the same time. Now if they block if all channels are blocked, so if there's no messages available on any channel, then the select statement will block by default. And then when a message comes in, it will go ahead and process that on the proper case.

If multiple channels receive value simultaneously, then the behavior is actually undefined. So because of the highly parallel nature of many go applications, you can get into situations where messages arrive on two channels at virtually the same time. So one of those cases will get the nod from the Select block, but you can't be sure of which one's going to get it. So there is no rule like in switch block where the first one that matches is going to get it, it could be anyone.

So the ordering of the cases in your SELECT statements really doesn't matter from the standpoint of how those conflicts are going to get resolved. Now if you do want a non blocking SELECT statement, remember that you can add that default case in there. So if there are no messages on any of the monitored channels, then the default case will go ahead and fire and so the select statement will process and execution of the go routine will continue from there.

Okay, so that wraps up what I have to talk about with channels. And really it brings us to the end of the discussion that I have for this introduction to go series for now. This is Mike VanSickle wishing you luck in all of your gopher endeavors. Take care

"WEBVTTKind: captionsLanguage: enSo I wanted to start out with an introductionto the go language itself. Now I know thatthis is much storied territory, but I haveto start somewhere. And this is going to serveas a stepping off point for us to go througha survey of the entire go language, as wellas hitting some of the key libraries alongthe way. So the first thing that we need toknow is that go is created by a small teamwithin Google. And that team was made up ofRobert griesemer, Rob Pike, and Ken Thompson.Now these guys have been around the softwareindustry for a little while, for example,Ken designed and implemented the first Unixoperating system, as well as had a key rolein the development of Unicode. So when theseguys got together and decided that they wantedto create a language, we had a lot of talentin the room as soon as these guys got together.But one of the questions that we need to understandis, why create a new language at all? Well,to understand that, we have to look at thelanguages that are common inside of Google.And that's the time that go is being designed,there were really three languages that werekey. The first is Python, then Java, and thenC and c++. Now, each of these languages inand of itself is very powerful. However, thego designers started to recognize that therewere some limitations that Google was runninginto, that might not be able to be fixed,given the history and the designs of the existinglanguages. So for example, when we look atPython, Python is very easy to use, but it'san interpreted language. And so it can bea little bit difficult to run applicationsat Google scale that are based on Python,it can certainly happen. But that is one ofthe challenges that you can run into in verylarge Python implementations. Java is veryquick, but its type system has become increasinglycomplex over time. Now, this is a naturaltrend that a lot of languages go through.They start out very simple, but it has additionaluse cases and additional features are layeredinto the language, it becomes increasinglymore difficult to navigate things C and c++is quick as well. However, it suffers froma complex type system. And additionally, itscompile times are notoriously slow. Now, thetype system has been receiving a lot of attentionlately in the C and c++ communities. However,they still have the burden of needing to manageall that legacy code. And so similar to Java,it's very difficult for them to move pastthe history that they have, because c++ applicationswritten 10 years ago still need to compiletoday. And the slow compile times are anotherlegacy issue that C and c++ have inheritedas well. When C and c++ were designed computershad nowhere near as much memory as they dotoday. So the decision was made to optimizethe compilers to use a minimum amount of memory.And one of the compromises that that broughtabout was the compile times can be a littlebit sluggish. In addition, all three of theselanguages were created in a time where multithreaded applications were extremely rare.Almost every application that was createdreally had to focus on a single thread ata time. So concurrency patterns built intothese languages are patched in at best. Andso working in highly parallel, highly concurrentapplications like Google often runs into canbe a little bit challenging when working inthese three languages. So enter go, What doesgo bring to the party in order to addresssome of these concerns? Well, the first thingthat we need to understand is gozi, strongand statically typed language, so it inheritsthat same feature set from Java and c++. Sowhat do we mean by strong and statically typedwith strong typing means that the type ofa variable cannot change over time. So whenyou declare a variable a to hold an integer,it's always going to hold an integer, youcan't put a Boolean in it, you can't put astring in it. And static typing means thatall of those variables have to be definedat compile time. Now, are there ways aroundthat? Yes, go does have features that allowyou to get around its type system, but 99%of the time, you're going to be living ina world that's strong and statically typed.And getting all the benefits that come withthat. Now, if you come from languages suchas Java, then you might be a little concernedthat strong and statically typed languagestend to be a little bit verbose. Well, we'llsee as we get into some go syntax, how there'sbeen a lot of effort taken to make the compilerdo as much work to understand what you'retalking about with a variable. So you don'thave to explain to the compiler every timewhat your variable types and things like thatare.In addition, go like a lot of recent languageshas a strong focus on the community that supportingit. Just because go is an excellent languagedoes not guarantee success. Because thereare so many languages out there, that it canbecome difficult for a new developer to rampup in any one. As a result, there's a strongcommunity built up around go that's reallyfocused on making sure that the go languagekeeps moving forward, and that new developershave an easier time as possible ramping uponto the language. So what are some of thekey features of the language itself? One ofthe first and I would argue most importantfeatures that go has is a recognition thatsimplicity is a feature. So as we go throughand start learning about the go language,you're going to run into some features andyou're going to ask yourself, Well, why doesn'tthis exist? Or why don't we have that feature?And a lot of the reasons Come back to thisfeature. There's a recognition that if thego language recognizes simplicity is important,then that means that we're going to have todecide to leave out some other features thatmight be very useful, but would add complexityto the language. Additionally, go focuseson extremely fast compile times a lot of moderndevelopment environments to write code fastto build it fast and to test it fast and getfeedback back to the developer as quick aspossible. Well, if you've got a 45 minutecompile time, it breaks that cycle. And developershave a very hard time staying in that designbuild test loop. And so go focuses on keepingthose compile times down, even though it'sgoing to yield us fully compiled binariesat the end, go as a garbage collected language,which means that you're not going to haveto manage your own memory. Now you can manageyour own memory. But by and large, the goruntime is going to manage that for you. Andthe reason for that gets back to this simplicityargument, there is a recognition that a garbagecollected language does have challenges whendealing with certain use cases. For example,real time trading systems for stock marketsystems have a very hard time when you'redealing with garbage collection. However,the advantages on the developer of not havingto manage their own memory all the time, weredeemed to be more important. Now, that doesn'tmean that the delays that a garbage collectorincurs haven't been paid attention to, ifyou go back through the history of the golanguage, you'll actually see the past fewversions have had a huge emphasis on reducingthe amount of time that the application hasto pause during a garbage collection cycle.And at this point, they're actually reallyfast, almost to the point that you don't knowthat a garbage collection happened, in orderto address that concern of concurrency godoes have concurrency primitives built rightinto the language. And we'll talk about thatas we go through some of these videos. Butinstead of having a library that we're goingto have to import, in order to work with concurrency,we're going to be able to do concurrent developmentright there in the base language. Finally,go compiles down to a standalone library,which means when you compile your go application,everything is going to be bundled into thatsingle binary that's related to the go applicationitself. So the go runtime is going to be bundledin there, any libraries that you're dependingon, they're going to be compiled in there.So you don't have to worry about reachingout to external libraries and DLLs, and thingslike that in order to make your applicationwork. And what that gives you is version managementat runtime becomes trivial. Because you simplyhave one binary, you deploy that binary, yourun it, and all of its dependencies are there.Now keep in mind, when I say dependencies,I mean to go dependencies, if you're goingto build a web application that has HTML,resources, and CSS, those have to be bundledalong with the binary, but the binary itselfis standalone and self contained. Okay, thenext thing that I'd like to do is show yousome of the resources that are available toyou, as you start to explore the go language.One of the most useful resources that you'regoing to be able to take advantage of as you'reramping up on go, is goes website here@golang.org.Now, why is it go lang.org? Well, if you takea minute to think about a language calledgo, that doesn't really lend itself to uniquesearch results in Google or Bing. So go lang.orgit is, as a matter of fact, a lot of placesthat you see go mentioned, you're gonna seeit actually described as go Lang, becausethat makes it a little bit more unique whenyou're looking for search results. So thefirst thing that you might notice, as we gointo the homepage here is this isn't reallylaid out like a lot of traditional home pages.This, in my opinion, is very much an engineeringhomepage. So instead of a lot of design aesthetic,this gets right into the engineering aspectsand shows you how to start working with thelanguage. So this yellow box over here isgoing to be your entry point for your firstgo application. So if we go ahead and clickthis run button, you see that we almost instantlyget an application sent back to the server,it gets compiled, and it gets run for us.So we can start playing around with go codewithout installing anything on our local machines.And we're actually going to take advantageof that through this first few videos. Asa matter of fact, if I make a small changehere, so maybe if I will say, Hello, YouTubepeoples and run that again, then I'm sayinghello to y'all, so Hi. So it's as simple asthat in order to get started with the go program.Beside that window, we see this download GObutton. And that's going to take you to resourcesthat you're going to be able to use in orderto download the latest go binaries, as wellas download older versions of the runtime.And if there's an unpublished version, forexample, at the time I'm recording this go1.8 is at RC two, you can go ahead and downloadthat install that and check that for bugsand play around with new features in the language.If we come across at the top, we see thisdocuments link here. And this is going tobe another critical resource as you're startingout with the language. As a matter of fact,you're going to refer back to this page quiteoften. But if you really want to walk throughon the website, a tour of the go languages,and I would recommend you go to this GettingStarted link. This is going to get you starteddownloading and installing the go compilersand tools and things like that. And you cansee as we navigate there, it's going to showyou the different architectures that you'regoing to be able to use with go and there'squite a few and how to get started on eachone of those. If we keep continuing down thetour of go is kind of an introduction to thego language that takes you through a gradualintroduction. So it's going to start out withsome very simple Apple And then build up moreand more and more and help you understandwhat's going on with go concurrency and thingslike that. Effective go is a very useful article,especially as you start to mature in yourunderstanding of the language, and reallyunderstand how the go language is used. SoI would encourage you to go into that it'sa pretty lengthyread. But you should consider this requiredreading if you're actually going to startbuilding non trivial go applications. Butwe're not going to worry about that rightnow. We got plenty ways to go before we needto get through all of this stuff. And thendown here at the bottom is some referenceinformation. This is more advanced documentationthat you're probably not going to need rightaway. But for example, the command documentationgives you a lot of information about the gotool itself that you're going to use for localdevelopment with go, there's a lot of thingsthat the go program does. And this is goingto help you understand how to navigate that.The packages link is perhaps where I spendthe most time on gos website. And this givesyou documentation for all of the librariesthat are built into go. So when you installgo and you install the go binaries and tools,you're going to get all of these librariesavailable to you. So just scanning down, youcan see that we've got different librariesthat are targeted at working with archives,we've got some cryptography libraries, databasedrivers, continuing to go down, we've gotsome things for working with HTML, and networktraffic. Now, some things that you might findmissing here are we don't have any GUI libraries.That's because at this point go really isn'tfocused on the use case of client applicationdevelopment. So go is really targeted at buildingservers and web applications. And so that'swhere a lot of the libraries are going tobe focused on. There are some projects thatare working on mobile applications using goas well as client side applications usinggo but they're not officially supported atthis point. If we come over to the projectlink, we're going to find some informationabout history of the project, what releaseshave come out and when, as well as some linksto mailing lists and resources that you cantake advantage of. If you want to keep trackof the development of go as a language aswell as if you find an issue in the go language,you can see some information here on how toreport that issue. And then we've got theHelp link here. And this is going to be oneof your more important links as you get startedhere because this is going to be your on rampinto the community. Now the two most activein my experience are the go forum, which isa nice discussion forum that allows you topost your questions and get people to answerback. But if you want something a little bitmore real time then the gopher slack is aSlack channel specifically targeted at NGOdevelopment. And there's multiple sub channelsin there for new developers for library developers.Even a lot of the NGO meetup groups have theirown sub channels on the gopher slack. So ifyou want to get on the gopher slack that Iwould encourage you to come over here to anotherwebsite called go Lang bridge. And this iswhat I consider the on ramp to the NGO community.Because NGO Lang bridge is specifically thereto advocate for the go language, and to makesure that the community is healthy and strong.As I said, one of the key aspects of the golanguage is a focus on having an excellentcommunity. And really, it's go Lang bridgeand the awesome people that support it, thatare making that happen. So if you scroll downa little bit, you can see some links to theonline communities. If you want to join theSlack channel, you do have to receive an invite.So this link here is going to take you tothe forum that's going to allow you to getthat invitation. And there's no problem gettingthe invitation, the only thing that they askedyou is to read the community guidelines, thereis a code of conduct that just make sure thateverybody is going to be treated respectfullyin the community, just to make sure that we'reall here trying to help each other out. Andthe last thing that I want to show you onthe website is this play link here. Now thislink just flies out an editor. And this isreally nice, because this is available throughoutthe site. So if I come to the packages, andlet's just say I dive into the network package,and I'm learning about some network function,then I can go ahead and pop into the play,I can create a real quick proof of conceptgo application in order to make sure thatI understand how that's working. And again,just like we saw on that homepage, if I clickrun that I can go ahead and execute that.Now there are some limitations, obviously,this application is sent to the back end.And there are some limitations, you're notgonna be able to read the file system at theback end, for example.But a lot of the things that you want to playaround with, you can play around with in thisonline environment. Now another place to getat this playground is over here at play thatgolang.org. And this is the last thing I wantto show you in this introductory video. Sothis is going to be the environment that we'regoing to focus on. And actually, let me makethat a little bit bigger. So maybe it's alittle easier for you to see. But this isgoing to be the environment that we're goingto focus on as we start to learn the go language.So we're going to learn the basics of go applicationhere, we're going to start playing aroundwith how we're going to work with variablesand logic and looping and things like that.Now, eventually, we'll get to installing alocal environment. And you can certainly takeadvantage of the other resources on go Langwebsite, if you want to get there before Icreate a video on it. But I think that there'sa lot that we can talk about without makinga commitment to setting up a local developmentenvironment by just going through this playgroundhere. So if we take a second look at thisapplication, we see some of the key aspectsof any go program. And the first thing thatyou see at the top is this statement packagemain. Every go application is structured intopackages. So every go file that you're goingto have is going to have to declare what packageit's a part of nain is a special package,because main is going to be the entry pointof any one of your applications. Down belowthat we have an import statement. And thisis the statement that we're going to use inorder to import additional libraries. So thislibrary is called thumped, which Yeah, youactually say that in the NGO community, Ican't bring myself to say that. So if I callthat FMT, I hope that you'll forgive me. Butin the NGO community, you will often hearthis called thumped. And this is the packageis going to allow us to format strings. Soyou see down below here and our main function,which is the entry point of our application,so the main function in the main package isalways going to be our application entry point.And this is going to be where we're goingto contain our first code that's going torun in go. So we're going to call into theFMT library. And we're going to pull out itsprint ln function, and that print ln functiontakes one argument, and that argument in thiscase is a string. So we're going to printout Hello playground. Now if I go ahead andrun this, then down below at the bottom ofthe screen, here, you see Hello, playgroundgets printed out. And then it says programexited. If we have an error in the application,say if I delete this quotation mark and run,then you're going to get a compiler errorprinted out at the bottom, that's going tohelp you debug your application. So this onlineenvironments going to be very good for youto get started, because it's going to helpyou through understanding what's going on.So for example, we see here in line eight,it got an unexpected semi colon or new line,when instead it was expecting a comma or aparenthesis. And the reason for that is becausethis closing parenthesis actually became partof the string. So the line terminated early,and it didn't have an end to the functioncall. So if we go ahead and re add the quotationmark, and run, we're good to go. And we'vegot our first start in a go application. SoI hope that this was helpful for you a littlebit of background in a language that you'regoing to be learning I always find is a littlebit valuable, it helps to understand the motivationsfor the creation of the language and the majorfeatures, in order to understand what problemsthat language is going to try and solve andhow it's going to go about trying to solvethem. What I want to show you is how to getstarted setting up your own local developmentenvironment to program with go. Now I knowin the last video, I showed you this websitehere, and I said that this is what I wantto use. That is the website it played@golang.org.And I said that this is the website that Iwant to use in order to show you a lot ofconcepts that you're going to need to be familiarwith with the go language. And that's stillmy plan. But as I thought about it, I decidedthat it doesn't really make sense to forceyou to use the playground, just because it'sa really good place for me to demonstrateconcepts. So I want you to have all the toolsavailable to you to set up your own localNGO development environment. So you can playaround with creating your own applicationsas you learn about this wonderful language.So the first thing that we're going to needto do is we're going to need to download andinstall the go language in the go tools itself.And so in order to get started with that,we're going to start over here@golang.org.And we're going to click on this downloadgo link here.Now this link is going to take you to allsorts of different versions of go. But ingeneral, if you're getting started, just pickthe latest stable version that's availablefor you, and it's not going to steer you wrong.Now, if you're a Windows user, then I wouldencourage you to click this MSI link, it'sgoing to download an installer and go is goingto be put on your system automatically. However,if you're using os 10, or Linux, then I wouldrecommend that you go to this installationinstructions link here and follow the instructionshere. Now, you're still going to need to downloadthe go binaries. But if you scroll down justa little bit, you're going to see this commandhere. And this is going to give you the tarcommand that you're going to need to use inorder to unpack the go binary and installit onto your system. So I've already donethat. And I can show you that by opening upa terminal here. And the default locationto put go is in the user directory underneathlocal and then in a folder called go. So ifwe look at there, and they look at the contentsthere, I see that I have all the go toolsinstalled. So with the Windows Installer,all it's going to do is it's going to placethese into C colon backslash go. And I wouldstrongly encourage you if you can accept thesedefault locations that you go ahead and dothat because it's going to make setting upyour environment just a little bit simpler.Now after we get go installed, we have a littlebit of configuration in our environment inorder to be able to use go effectively. SoI'm going to come back to my home directoryhere. And I'm going to make a change to mybash RC file. Now I'm on Ubuntu. So it's goingto be in my bash RC file. If you're an LS10, it's going to be your bash profile. Ifyou're in Windows, basically what we're doingis we're setting environment variables. Soif I open that up and come down to the bottom,then I see that I've got the pre generatedbash script. I'm not going to really worryabout that. But there's a couple of variablesthat we're going to need to set. Hello, everybody,I need to pause the video here for a secondand make an important announcement. So assoon as I originally released this video,Dave cainy came in within a couple of minutesand he expressed a concern about one of thethings that I'm about to talk about. Now I'mabout to talk about setting a couple of environmentvariables here and showing you how they work.Now one of those variables is called go route.And setting go route has been shown to causeproblems as you move through different versions.Go, especially if you've got multiple versionsof the language on your system. So if youwant more information about that Dave hasa really good blog post here that you cango to to learn more information about it.But for now, please keep in mind, if you'reable to install go at its default locations,which on Unix or Mac is going to be slashuser slash local slash go in on Windows isgoing to be C colon, backslash go. If you'reable to do that, please do that. And thengo ahead and avoid setting go route. Now youwill need to set the PATH variable to go rootsbin directory in order to access the go executable.And you will need to set go path which we'regoing to talk about after that. But if youcan avoid setting the go route environmentvariable, it's going to save you a lot ofheartache. So well, I'm going to show youhow to do that in case you do need to setit please avoid that if at all possible. Okay,at this point, I'm going to resume the videoand continue talking about the environmentsetup. Now the first variable that you'regoing to need to know about is called go route.Now, if you've installed go to its defaultlocation, you're not going to need to worryabout this. But if you've decided to installgo somewhere else, for example, maybe you'veinstalled it in your home directory, thenyou can go ahead and set go route. And that'lltell the environment where to go to find thego binaries. Now the next thing that we wantto do is we actually want to set a PATH variableto the go binaries themselves. So I'm goingto go ahead and export a PATH variable. Andthat'll Of course, start with my existingpath. And then I'm going to add on to thatgo route. And then I'm going to whack on thepath slash bin. So there's quite a few binariesthat we're going to be using on a regularbasis. And those are in go route slash bin,so you're going to want to make sure thatthat's part of your path. Now once I do that,let me go ahead and save this. And then Iwill use the source command in order to getmy shell to reread the bash RC file. And thenI should be able to test to make sure it goesavailable by typing go and version. And youcan see here I'm running go version 1.8 releasecandidate three.Okay, now there's one more thing that we needto do in order to get our environment fullyset up. And I'm going to go back into my bashRC file in order to do that. And that is thesetting of a second environment variable.So we have go route. And that's going to tellthe environment where goes installed. Butwe're also going to be downloading a lot ofpackages as we work with go, because we'regoing to be taking advantage of librariesthat other people have published in orderto build our own applications out. So thoseapplications as well as our own source codeare not going to be located with the go binaries,they're going to be located in a path thatwe're going to specify with a another variablecalled go path. Now go path is either oneof the most awesome or one of the most horriblethings about go because it gives you thisreally nice way to specify where you go projectsare located. However, it does kind of pushyou towards having this monolithic repositoryof all these binaries and your applicationstied together. So I'll show you a little bitof a hint on how to work with that. But fornow, let's go ahead and set a go PATH variablein my home directory. And then I will callthis go lib. Now just like with go route,we might have some executable binaries thatare going to be stored in our gopath. So I'mgoing to go ahead and export on our path again.And we'll start with our existing path. Andthen I want to add on go path slash bin. Thatway if we install any libraries that haveexecutables, and we will be installing librariesthat have executables, then we're going tobe able to track that. Now just to show youreal quick what that's going to do. Let mego ahead and save this out resource, my bashRC file. And then I've actually already createdthis folder here called go live. Now if Igo into go lib, and I look at the contentsof that it's currently empty. But I can changethat by using a tool called go get. So ifI get a library that's at github.com slashNSF slash go code. This is historically thelibrary that people use to provide autocompletefunctionality in their go applications. Soif I go ahead and hit Enter, and wait a second,then go back into my go lib folder. And look,now I've got some content here. So if I lookin the bin directory, I've got this go codeexecutable. And if I come back to the sourcedirectory, then I see that I've got this GitHubcomm folder. And inside of that is NSF. Andinside of that is go code. So if I come inhere, here's all of the source code for thego code library. So when I'm working withgo code, it actually downloads the sourcecode and compiles it into the go code libraryfor me. And that's what that go path is goingto do for you. The problem that you mightrun into is that this use of gopath tendsto drive towards monolithic repository. Soyou're going to have go code, you're goingto have your own code, you're going to haveall sorts of other libraries, all put intothis one location. Now when I create my courses,that creates a lot of visual clutter. Andso that isn't exactly the form of gopath thatI use. What I do is coming back into my bashRC file is I actually use a capability ofgopath to create a compound gopath. So insteadof a single path here, I'm actually goingto re export go path and I'm going to addon an additional path to this and I'm goingto add Home mic and code. Now a lot of timeswhen I'm teaching courses, I only need togo pants. And so I've got go live. And that'sgoing to be where all my third party librariesgo. And then I'm going to have another folder,that's going to be what's called by workspacelocation. So let's go ahead and write thisout. We'll source this again. And now I'vegot the full gopath. So if I come into myNGO lib, and I remove everything from it,now it's empty again. So if I go ahead andgo get that repository, again@github.com slashNSF slash go code. And now you see that itactually goes into my go lib folder. If Icome into my code folder, which I also createdearlier, that's still empty. So the firstsegment of your go path is going to be usedby go get in order to store your files, butall of the segments of your go path are goingto be searched for source code. So that'sgoing to really help us as we're setting upour workspace. So speaking of which, that'sthe next thing that I want to do. So a workspacein go isn't anything special, the only thingthat you need in order to create a workspaceis to have a single directory called SRC init. So if I add a directory called SRC intomy code folder, then I've got to go workspace.Now SRC, as you might expect, is where you'regoing to keep your source code. So when Iset go path to slash home slash Mike slashcode, it's going to look for an SRC directoryin order to find my source code. Now, thereare two other directories that you might findin a workspace that are interesting. And wefound one already when we installed that gocode library. And that's been. So anytimewe're working with a project and a binaryis created, it's going to be put into thatbin directory. And that's also why we addedthat bin segment to our path,the last directory that you might find inyour workspace is a pkg directory. So forcompiling something, and it's going to generatean intermediate binary, which means it's notgoing to be a fully compiled application,it's going to be an intermediate step. Sofor example, if we're taking a third partylibrary, and we're integrating that into ourapplication, then the pkg directory is wherethose intermediate binaries are going to bestored. And the reason those are created isso that they don't have to be recompiled everytime. So when you compile your go application,go is going to check to see if any of thesource files in that directory have changedsince the last time it compiled them. If ithasn't, then it's not going to recompile thatpackage, it's just going to go ahead and linkthem into the binary that it's creating foryou. Okay, so let's go ahead and clear thisout. Because that's getting a little bit cluttered.And now let's set up an editor to work withgo code. Now, there's a lot of editors thatare out there. And so what I'm going to showyou is just one, but feel free to explorethe option for your favorite editor, becausethere's probably a go plugin for it. And allof these plugins are really good right now.So I'm going to show you the one that I'vebeen using lately, which is Visual Studiocode, now might be a little bit surprisingMicrosoft, oh my goodness, they're doing allthis really awesome stuff for the open sourcecommunity. But it's really true, one of thebest development experiences that you're likelyto come across with go is in this Microsoftproductrunning on Linux. So I've already installedit. But if you do need to install it, youcan come here to code Visual Studio Comm.It's going to give you the binaries. In thiscase, I'm running Ubuntu. So I would downloadthis.db file and install that onto my system.And then I'm going to be able to run thatby simply typing code in my application launcher,or I've already set up a shortcut over herein my taskbar. So as soon as I run that, I'mgoing to be presented with this. Now you cansee I've already opened up a folder here,you can open up a folder to your workspaceby simply file open, and then pick your folderthat you want to be working with. So we'regoing to be working with code. But there isone setup step that I need to go through beforeVisual Studio code is quite ready to go. Andthat is I need to install the plugin that'sgoing to allow us to work with go code. Andso if I click this button down here calledextensions, then I have a list of all sortsof extensions that I can add in for VisualStudio code. Now, right here is the go extensionby Lu COVID. Now there's a couple of go extensions,but I would strongly recommend you use thisone here by Luke, because it is really amazing.It offers a lot of capability, it really makesVisual Studio Code of first class environmentfor developing go, okay, and as fast as thatthat's been installed. And it was that fast,because it's been cashed from earlier. Andthen I'm all set and ready to build my firstgo application. So let me go ahead into thissource directory. And I'm going to createa folder that's going to contain my sourcecode. Now your first temptation might be tojust plug in your source code right here inyour src folder. But I wouldn't recommendthat the standard structure that you're goingto use in a go application is to mirror whereyour application is going to be in sourcecontrol. And that makes it go gettable. Soin this case, if I was going to keep thisfile on GitHub, I would create a folder calledgithub.com. And then underneath that, my GitHubaccount is vi n si Mk II don't ask long storyabout why I called it that. And then I wouldhave an application name. So I would callthis maybe first app, and that's the folderthat I'm going to store my application in.And the reason for that is if you think aboutif I check this into GitHub, and observatorycalled first app, when I go get that, it'sgoing to recreate this structure. And so youwant to create your applications followingthat structure. So now I'm ready to createmy first file, and I will call that main go.And then I can start adding in my source code.So the first thing that I'm going to add ispackage main. And then when I save this, oh,it looks like I expected something to happenhere. And it's not happening. I think it'sbecause yeah, he told me that I needed toreload the environment. Good. So I'm goingto go ahead and do that. So I'm going to exitout of Visual Studio code, and it should kickright back off, but it has to initialize theplugin. So now I see what I was expectingto see. And that is that the NGO plugin hasrecognized that they don't have all the toolsthat it needs in order to provide me all thesupport that it can, because the NGO pluginfor Visual Studio code is actually like alot of the plugins and other languages, ittakes advantage of these language services,for example, it's talking to me about go land,go lint isn't available on my system. Andso it's not going to be able to provide lintingcapabilities. So basically, the go plugin,cause up to all these language services. Thenice thing about that is if you decide toflip between different editors, your experienceis pretty much the same, because they're allrelying on the same language services in orderto provide you the capabilities that you havesomething go ahead and install all. And youcan see there's a pretty long list oflibraries that it's installing for me. Okay,and now they're all installed. So that tookabout a minute to install all those dependencies.So I expect that you'd probably have a similarexperience in your own environment. Now, beforewe start adding anything else, I actuallyput some quotation marks around this package,that's not going to be correct. And now I'mready to start actually building out my program.So let me go ahead and put in an import statementhere. And we haven't talked too much aboutimports. But we have mentioned a little bitabout packages, packages, or how code is organizedinto sub libraries inside of NGO. So for example,if I want to build a web application, thenI might pull in the net HTTP package thatyou see here, in order to set up my web requesthandlers. But for now, I just want to do asimple Hello, go example. And so I'm justgoing to import the FMT package. But you noticethat the NGO plugin for Visual Studio Codegives me autocomplete, so any library that'savailable on my go path is going to be foundhere. So now I'm going to create a simplefunction called main. And inside of that,I'm going to access the FMT package. And noticethat I get autocomplete functionality here.So I can go ahead and say I want to call theprint ln function, and it gives me the signaturefor that function. So I can go ahead and addthat in here. And the I just want to say hello,go. Okay, so in order to run this, insideof Visual Studio code, you do have the abilityby pressing Ctrl backtick, you can open upa terminal right here inside of the editor.And there's a couple of different optionsthat I have in order to run my application.So the first thing that I can do is I canuse gos run command, as you see here, andI can give the path all the way through tomy source code. So if I come and I just keeptapping through, then I'm going to get sourceslash github.com, slash vianne, si MKE slashfirst app slash main.go. So if I run that,then it will compile that temporarily, andrun that for me. And it also compile in anythird party libraries. So the FMT packagewas compiled in as well. Now, that's a reallygood way to get a really quick run. Anotherway that you have available to you is to usego build and go build takes the actual packagepath. So all we're going to do here, is compilethe first app package. Now, if it finds amain package with a main function, then it'sgoing to compile that as an executable, likeyou see right here in my home directory. SoI can go ahead and run that. Now the lastbuild tool that I have available is go install,go install is actually expecting to be pointedto a package that has an entry point. Andit's going to install that into your bin folder.So let's go ahead and see that work. So we'llgo to github.com, my username for GitHub,and then first app again. So notice I'm usingthe package address, I'm not using the folderpath. If I run that, notice, I don't get anythingin my main directory. But if I come into thisbin folder here, now I've got first app overhere. So if I come back to my terminal, binslash first app, run that I get Hello, goprinted out again. Okay, so the last thingthat I want to show you if I come back overto the terminal, so you see how we have allthe packages that we're working with locallyover here in this code directory. If I cometo the first element of my go path, whichis go lib, and look at that, you'll see thatthis is starting to become a pretty busy placehere. Because now I've got three source folders.And if I come into GitHub comm, you'll seethat I've got quite a few packages. So nowall of these third party dependencies aren'tcluttering up my main workspace. They're allin this go lib folder, and then I can focuson my development in my code folder. Now thelast place that you're going to see packagesis over in the NGO installation directory.So if I look in that folder, you see herethat I have this directory called source.So the ghost source code itself is in facta valid go workspace. So if I come in herelook If the folders in here, you see, theseare exactly the go standard library. So here'sFMT. Here. You see if I scroll down a littlebit the net package, if I go into the netpackage, you'll see that that contains theHTTP package. If I come into the HTTP package,and list those contents, you see all of thesource codes for all of the modules that youcan see over here at go. Lang Comm. So ifI follow that through, over here, scroll downto net, and HTTP, you see all of the capabilitiesthat are available in here? Well, all of thoseare provided by this source code here. Soif you have any questions about how any oneof those libraries work, or how something'sconfigured, you can jump right into the sourcecode and see how it's all put together. Overthe last couple of videos, we've laid somefoundation by talking about the history andthe features of go and working through settingup the local development environment. Whattoday is the day that we're going to starta discussion about the go language itselfby discussing how to work with variables.So in order to fully cover how to work withvariables, there's several topics that we'regoing to need to cover. We're going to startby learning how to declare variables. Thenwe'll move into a discussion about how goconsiders read declaration variables. Andthis concept of shadowing. After that, we'lltalk about visibility, where we're goingto learn how we can control what aspects ofour program can see a variable that we create,then we'll talk about naming conventions.And finally, we'll wrap up with a brief discussionabout how to convert variables from one typeto another. Okay, so let's go ahead and getstarted. So I'm going to be using the playgroundin order to host our conversation. That way,you can follow along whether you've set upa local development environment, or just wantto follow along online. So as you can see,when we first load up the playground, we'vegot this one statement program, which is justprinting out the string hello playground tothe bottom of the screen, when I click thisrun button here. Now this statement can onlyever do one thing because we're passing ina string literal. And similarly, if I passin the number, say 42, and run it, then weprint 42 out. Now no matter what we send tothis print ln function, it's only going toever do that one thing. So in order to providea little bit more flexibility to our application,we're going to introduce variables. So there'sactually three different ways to declare variablesand go and we're going to go through eachone of those. And then we'll talk a littlebit about where you might use each format.So the first thing that we can do is actuallydeclare the variable itself. And that'll bedone using this kind of a statement. So we'regoing to start with the var keyword, thenwe're going to have the name of the variableand the type of the variable. Now if you comefrom another strongly typed language, thismight look a little bit backwards, becauseyou might be expecting to see something likeint i. Well, that's not actually how go works.And if you think about it, the way gos structures,things actually looks more like how you readit. So I'm going to declare a variable calledI that's going to be of type integer. So theway you declare variables and go, it's prettymuch the same way that you speak. And so itmight look a little bit odd when you firststart working with it. But it very quicklybecomes intuitive. Now that we have our variabledeclared, we can go ahead and assign a valueto it. And we'll do that simply with the equalsoperator. So once we have that set, we cango ahead and replace this with an AI, runit and we print the value 42 out. And sincevariables in go can vary, we can go aheadand change the value of that to say 27. Whenwe run again, we get the value 27 printedout. So now even though the statement canonly ever do one thing at a time, the programthat runs up above this FORMAT statement canactually influence the value that's printedout to the console. Okay, so let's go aheadand get rid of this, and then explore anotherway to initialize this variable. Because oneof the things that we trying to do and gois we want to keep things simple. So if weneed multiple lines, then we'll go ahead anddo that. But we don't want anything to bemore verbose than we have to. So we can actuallycombine these two lines in one like this.So we can initialize the variable i as aninteger and assign the value 42 in the sameline. So if we go ahead and run this, thenno big surprise, we get the value 42 printedout. Now, this is actually still making uswork a little bit harder than we need to.Because since we're assigning the value 42to this, the go compiler can actually figureout what data type it needs to have. So wecan go ahead and tell it to figure this outfor us by replacing all this text with thistext here. So if we just say I and then usethis colon equal operator and the value 42,and run this, then we get this really niceconcise way of setting things up. Now, whereare you going to need each one of these? Well,let me go ahead and add them back in so wecan talk about them. So we'll set var I integeri equals 42. And then we'll use var j integerequals 27. And then we'll set up K and that'sgoing to be equal to99.So we have these three different formats.So when are you gonna want to use each oneof these? Well, the nice thing about thisfirst format is there are going to be timeswhen you want to declare a variable but you'renot ready to initialize it yet. So for example,if you want to declare variable in the scopeof this main function, and then you have aloop or something like that, that sets upa local variable scope. And that's where thevariable is actually going to be assigned,then you can go ahead and use this first syntax.The second syntax is valuable, if go doesn'thave enough information to actually assignthe type that you really want assign to it.So to show that, let me go ahead and add anotherprint statement here. And I'm going to needto come up these other lines out in orderto make things happy for us. So let me goahead and change this print ln to a printF. And what this is going to do is it's goingto allow us to provide a formatting stringand print things out that way. So we'll printout the value, and then I'll print out thetype of the variable, and then I'll pass inj twice. And so if I run this, then we getthe value 27. And no big surprise, it's aninteger. But what if we want this to be, forexample, a floating point number? Well, withthis syntax, it's as simple as just changingthis to float 32. When we run this, then gounderstand that we want to use 27 as a floatingpoint number. Now, if we tried that with K,down here, so let me uncomment the K, switchthis over to use K and run, then you see thatthe number 99 is inferred to be of type integer.And we can influence that a little bit byadding a decimal point. And that's going togive a hint, making that a float 64. But there'sno way to use this colon equals syntax toinitialize a float 32. For example, go iseither going to decide it's an integer type,or it's a float 64 type. So if you need alittle bit more control than the second declaration,syntax is going to be valuable for you. Nowthe next thing that I want to show you ishow we can declare variables. Now we've beendeclaring variables one at a time and insideof a function. Well, another way that youcan declare variables is up here at the packagelevel. Now when you're doing it at the packagelevel, you cannot use this colon equals syntax,you actually have to use the full declarationsyntax. So we can declare a variable is aninteger set equal to 42. So that works, comedown, wipe out this code, and then replacethis with I. And you see that we have thevalue 42. And it's of type integer. So justlike we have before, we can go ahead and tellit to declare that as a float 32. And thecompiler recognizes, well, I can make 42 afloating point number, that's not going tobe a problem at all. Now, of course, if youtry something like this, the compiler hasno idea how to convert the string food toa floating point number, and so it's goingto fail on you. Another thing that we cando at the package level is we can actuallycreate a block of variables that are declaredtogether. And to show you why that's valuable.Let me just drop in some variables that wecan play around with. Okay, so as you cansee, I've got a little bit of Doctor Who hasa brain here, and say I'm writing a programthat's going to print out some informationabout the doctor's companions. So here wegot a variable actor name, that's going tobe Elisabeth Sladen, then we got her companionname, which doctor she worked with, and whatseason she was on. So by declaring these variableslike this, things are actually a little bitcluttered. Because again, we want things ingo to be as clear and concise as possible.So all these var keywords are actually clutteringthings up a little bit. So what we can doinstead of this is actually wrap this wholesection with a VAR block. And then we canactually get rid of the use of this var keyword.And now all of these variables are going tobe declared because they're inside of thisvar block. And we can actually show that they'rerelated somehow. Now they don't have to berelated. That's a design decision. But wecan do that. So if we had another set of variablesthat were related to a different context.So say, for example, we had a counter, andthat was going to be an integer initializedto zero, then we can have multiple variableblocks at the package level. And that's justgoing to allow you to organize your applicationa little better, and keep your code a littlebit cleaner. Now the next thing that I wantto show you and let me just drop in some codehere is how variables work when you're tryingto read Eclair them.So you can see in this application, we'redeclaring the variable i up here in the packagescope, then I'm declaring it here inside ofthe main function, and then I'm re declaringit here on line 11. Now, if I try and runthis, I'm actually going to get an error.And the error comes on line 11 here, becausethere's no new variables here. So I can reassignthe value of i 13. But I can't declare a newvariable here. And that's because the variablesalready declared on line 10. And you can'tdeclare the variable twice in the same scope.However, if I get rid of this line, noticethat the application runs just fine, and usesthe value 42. So even though I is declaredtwice in my application, once at the packagelevel, and once inside of the main function,that's okay. And what happens is that thevariable with the innermost scope actuallytakes precedence. So this is called shadowing.So the package level is still available,but it's being hidden by the declaration inthe main function. And I can actually showthat by copying this line up hereand running this. And now you see I get 27,which is the package level scope. Then I createthe shadow Variable setting equal to 42. Andwhen I print out I again, then I get the newvalue of i. Now another interesting thingabout variables in go is that they alwayshave to be used. So let me just drop in thisexample here. And let's walk through it. SoI'm declaring a variable, I'm setting it equalto 42, then I'm instantiating, a variablej, setting it equal to 13. But I'm only usingi. So what happens when I run this? Well,if I do, I actually get yelled at, becausej is declared and not used. And this is oneof the things that's going to keep your goapplications nice and clean. If you have alocal variable that's declared and not used,then that's actually a compile time error.And the reason that's really valuable is asyour application grows and evolves, and newfeatures are added and old features are deprecated,you're very likely to end up with some oldcode hanging around inside of your functions.Well, if any of your old code or variablesand those variables are no longer used, thenthe compiler is going to detect that for yousee that you can make sure that you can cleanthose out. Now another important thing toknow about when you're working with variablesis how to name them. And there's actuallytwo sets of rules that you're going to needto keep track of. One is how naming controlsvisibility of the variable and go. And theother is the naming conventions themselves.So notice that I've been creating lowercasevariables. Well, that actually isn't alwaysthe case in NGO, because if I'm declaringa variable, and let's just declare it at thepackage level, and I declared as an integer,with the name I, and I go ahead and work withthat, then that lowercase variable name actuallymeans that this variable is scoped to thispackage. Now, this is a main package. Andso this doesn't really matter so much. Butwhen we get into working with packages, thisbecomes extremely important. So lowercasevariables are scoped to the package, whichmeans anything that consumes the package,can't see it and can't work with it. But ifI switch to an uppercase letter, then that'swhat's going to trigger the go compiler toexpose this variable to the outside world.So it's a very simple naming convention. Andthere's really only three levels of visibilityfor variables in go. If you have it at thepackage level, and as lowercase is scopedto the package, so any file in the same packagecan access that variable. If it's uppercaseat the package level, then it's export atthe front of package and it's globally visible.And the third scope is block scope. So whenwe have this main function here, it's actuallyestablishing a block scope. So when we declarethis variable I write here on line 10, thatvariable is scoped to the block. And so that'snever visible outside of the block itself.Now beyond that, it's important to understandthe naming conventions in go, and there'sa couple rules that we need to follow. Thefirst is that the length of the variable nameshould reflect the life of the variable. Sofor example, in this example, we're declaringa variable i, and we're using it right away.So having a very simple variable name is perfectlyacceptable. And this is going to be especiallytrue if you're declaring counters and forloops and things like that, it's very commonhave single letter or very, very concise variablenames, because the lifespan of that variableis very small. And the amount of time thatyou have to keep the meaning of that variablein your head is very small as well. However,if you're declaring a variable that you usefor a very long time, then it's best practiceto use a longer name. So for example, if thiswas going to represent the season number thatour companion was on, so say, season 11. Andwe use that throughout this entire main function,then we'd want to use a name something likeseason number. Now, if you're working witha package level variable, and that packagelevel variable is going to be used in quitea few other places, then that's where you'regoing to want to use the most verbose variablename. Now, you still shouldn't get crazy,you shouldn't have 50 character long variablenames if you can avoid it. So keep your namesas short as you can. But make sure that thenames of those exported or package level variablesare clear enough so that somebody who's outsideof that source file understands the meaningof it. The other thing I'd like to talk aboutis how to work with acronyms, because in otherlanguages, you might see variables like theURL, and then maybe this is http google.com.You might see a variables name like this.Well, the best practice and go is actuallyto keep these acronyms as all uppercase. Soif you're working with a variable called theURL, the URL should be all uppercase. Similarly,HTTP and any variables like that. So anytimeyou see an acronym, make sure that that'sall uppercase. And the reason is for readability,it's very clear, we're used to seeing URLand HTTP all put together. So when you readthis variable, it's very clear that you'retalking about an HTTP. Maybe you're talkingabout an HTTP request that reads a littlebit cleaner than if you go ahead and makethose lowercase. So just some rules to keepin mind as you're creating variables. Nowthe next example that I want to show you ishow we can actually convert from one variabletype to another. So notice that I have twovariables here. I've got variable i on lineeight, and then I'm declaring as an integerwith the value 42. And then I've got thisvariable j, that's going to be a floatingpoint number. Now what I want to do is I wantto actually treat it as a floating point numberand assign that value to J. So the way thatI do that is using this conversion operator.So if you look on line 12, it looks like floatis being used as a function. And in fact,it is. And this is a conversion function.So when I run this program, you see that thefirst print statement on line nine, prints42, as an integer, the second print statementstill prints the same value 42. But now it'sbeen coerced into being a floating point number,they have to be a little careful with this.Because if you go the other way, so for example,if we go from a float 32 to an integer, andthen I convert that and run it, it looks likeeverything's okay. But keep in mind, a floatingpoint number can have a decimal on it. Sonow I've actually lost information to theconversion. So the important thing about thisis I have to explicitly convert, because ifI just tried to do this, then I'm actuallygoing to get a compile time error, becausego is not going to risk the possibility oflosing information through that conversion.So you have to do an explicit conversion,when you're changing types. That way, it'syour responsibility to understand if you'relosing information or not. Now, the otherthing that's important to know is if I decideto work with strings, it's a very common usecase, to try and convert an integer into astring, say, for example, you want to printit out to a log file. Well, if I run this,I get a pretty odd result. The first lineprints out Okay, 42. And that's an integer,that's okay. But then I get an asterisk. That'sof type string, what the heck happened there?Well, in order to understand that, you haveto understand how strings work with go, astring is just an alias for a stream of bytes.So what happens when we asked the applicationto convert the number 42 into a string isit looks for what Unicode character is setat the value 42. And that happens to be anasterisk. So if you want to convert back andforth between strings and numbers, then you'reactually going to need to pull in the stringconversion package, which you can find ongo Lang under packages. If you scroll downa little bit, you see string conversion here.And this is going to expose all sorts of functionsthat are going to make it a lot easier toconvert back and forth between strings andother data types. So in this case, what we'dwant to do is use the string conversion packagesI to a function, which converts an integer,that's the I two, that's the two and thento an ASCII string. So if we go ahead andrun that, now you see that it properly convertsthe integer 42 into the string 42, and printsit out for us. So if you need to work withconverting between numbers and strings, thengo ahead and use that string conversion package.But if you're converting between numeric types,just keep in mind, you can't implicitly dothat conversion, you have to explicitly doit. And if you need to do that, then you cango ahead and use the type as a function. Okay,so we just covered quite a bit of ground.So let's go through a summary of what we'vetalked about throughout this video.Okay, there's quite a few things to keep inmind as we're working with variables. So let'sgo through and review what we talked about.The first thing that we talked about is thethree different ways to declare variables.So we saw that we could declare a variable,and then initialize it later, we saw thatwe can declare it and initialize it at thesame time. And then we also saw that we canuse this colon equals syntax as a shorthandversion of declaring and initializing a variable.And then we let the compiler decide what typeto assign to that. Now normally, you're goingto use this third version, the only time you'rereally going to use the second version iswhen the compiler is going to guess wrong.And then the first version is sometimes useful.If you need to declare the variable in a differentscope than you're actually going to initializeit. We then talked about how we can't readdeclare a variable. So within the same scope,we can't initialize that variable twice, butwe can continue to reassign values to it,but we can shadow them. So if we declare avariable in a package scope, for example,we can read declare that in a function scope,and that's going to shadow the variable atthe higher level of scope. All variables mustbe used in a go application. So if you declarea local variable, and that variable isn'tused in the scope that it's declared or oneof its inner scopes, then that's going totrigger a compiler error. And you're goingto have to go back and clean that up beforeyour application is going to run. And again,the reason that that's really nice is as codeevolves, and it continues to be refactoredand improved over time, and features get retired,you don't have all these old variables hangingaround and requiring allocations of memorywhen they're not being used for anything anymore.We also talked about the visibility rules.The first thing that you need to know is whenyou're working with package level variables,a lowercase first letter means that it's scopedto the package, which means all of the sourcefiles that are in the same package have accessto that variable. If you have an uppercasefirst letter, then it's going to be exportedglobally, and so anything can work with thatvariable at that point. There is no privatescope. So you can't scope it. variable tothe source code itself. However, you can scopea variable to a block by declaring it withinthe block instead of declaring it at the packagelevel. We also talked about the naming conventions.And there are really two naming conventionsthat are used Pascal case, which basicallymeans you uppercase, the first letter, andcamelcase. So when you're naming variables,you don't want to separate the words in thevariables with underscores. You don't wantto have them all in lowercase, you don't wantto do anything like that. Just use standardPascal or camel casing rules. The only exceptionto that is if you're working with acronyms,all of the letters in the acronym should beuppercase. The names of the variables shouldbe as short as you reasonably can get them.So you've got a very short lifespan of thevariable, say, for example, a counter in afor loop, then having a one letter variablename is perfectly acceptable. However, ifyou've got a variable that's got a longerlifespan, say, for example, it's used in afairly long function, or if it's exportedfrom the package, then having a longer moredescriptive variable name is certainly somethingthat you should consider. However, pleasedon't go crazy. Keep those names as briefand concise as possible. And the last thingwe talked about are the type conversions andhow these work a little bit differently thanother languages, a lot of other languages,you would have to put the type in params,and then what you want to convert, in NGO,it acts more like a function. If we want toconvert an integer to a floating point 32number, then we use float 32 as a function,and we pass the integer into it, and it'sgoing to do the type conversion for us, wealso learned that go does not do implicittype conversion. So if you try and take afloating point number and assign it to aninteger, goes going to throw a compile timeerror for that. And that's because of thepossibility of losing information throughthat conversion. So every time you're goingto do a type conversion that might lose information,you're going to have to do that yourself sothat you're making the decision. And thenyou can write whatever tests are requiredin order to make sure that information hasn'tlost. The final thing that we learned is whenwe're working with strings, then type conversionscan start to get a little bit weird. So inorder to handle the conversion between integersand strings, and other data types in strings,we can use that string conversion packagethat offers a series of functions that makesure that the conversions happen the way thatwe expect them to. In today's video, whatI want to do is introduce the primitive typesthat we have available in the go language.Now, we're not going to be talking about everybasic type that you can create. There arecertainly collections and some more complicatedtypes. And we'll introduce those a littlebit later. Today, I want to focus on threecategories of information that we can storeand go. We'll start by talking about the Booleantype, then we'll move on to the numeric types.And in that category, we have integers, floatingpoint numbers and complex numbers. And thenwe'll move on to the text types. Okay, sofairly simple agenda, but we've got a lotto cover. So let's go ahead and get startedby talking about how we can work with booleandata in go. boolean data is probably the simplesttype of data that we can work with and go.And it represents two states, you either havetrue or you have false. So in order to showyou a simple example of working with Booleanvariables, we can create a variable here andmake it of type bool. So that's the data typethat you're going to use when you're declaringa Boolean. And we can set it equal to A values.So for example, we can set it equal to true,and then we can go ahead and print out usingourfancy printf statement here, we can printout the value and type of this Boolean. Andif we run that, we see that the Boolean trueis in fact got the value true. And its datatype is Boolean. So we can also initializethis to false, and run that. And now we seefalse is also a Boolean. Now there's a coupleof uses for Boolean variables in our applications,perhaps one of the most common is to use themas state flags. So for example, say you'recreating an application and you want to storewhether a user is signed up for notificationswhen a report is generated. Well, you canuse a boolean variable in order to store trueif they want that report or false if theydon't. The other case. And perhaps the morecommon case for using Boolean and go is asa result of logical tests. Now, we're notready to talk about logical tests quite yet.But I can show you how Boolean 's are usedin those tests. So if we create a simple variablehere, and we use the equals operator to testif one equals one, and then create anothervariable, and then I want to test if one equalstwo. Now, the double equals operator is calledthe equals operator. And that's basicallychecking to see if the item on the left isequivalent to the item on the right, so oneobviously equals the number one, and one,just as obviously doesn't equal the numbertwo. So if we print out the value of thoseusing these two printf statements, then wesee that a Boolean is actually generated asa result of this equivalency test. Now whenwe get into talking about logical tests, we'llsee that there are actually quite a few otherlogical tests that we can use. But this isa very common use case that we have. So wesee looks like I need to add a new line operatorhere, we see that the first operation doesin fact, generate the Boolean true and thesecond operation generates the Boolean false.Now the other thing that's important to knowabout the primitives is that every time youinitialize a variable, Ingo, it actually hasa zero value. So we're assigning the valueof n and m in this example here, but whathappens is If I just do this and print outthe value of that, well, in some languages,that would be uninitialized memory, and wewould have no control over what printed out,when go, every time you initialize a variable,it has a zero value in the zero value forBoolean is the value false. So you don't haveto worry about initializing variables everytime. If the zero value is acceptable to you,you can certainly leave it like that. So Booleanis a pretty simple. The next type that I wantto get into are the numeric types. And theseare a little bit more complicated, becauseof all of the different types of numbers thatwe can work with in our applications. Go hasa rich array of numeric types to choose from.Now, the first thing that we need to knowabout is the zero value. And the zero valuefor all numeric types is going to be zero,or the equivalent of zero for that numerictype. So let's start talking about the integertypes. So the first type of integers thatwe can work with are the signed integers.And those have several different data types.So we have the general int, which is an integerof unspecified size. And I say unspecifiedbecause every platform can choose to implementit as a different size. Now, the one thingthat you're guaranteed is regardless of yourenvironment, and it will be at least 32 bits,but it could be 64, or even 128 bits dependingon the system that you're running on. Andthis is going to be the default integer type.So if we do something like n equals 42, andthen we print out the type of n, then yousee that we get the value 42. And it's oftype integer. Now, there are other types.So we can have eight bit integers, which canrange from negative 128 to 127. We can have16 bit integers, which can go from negative32,007 68, up to 32,007 67, then 32 bit integers,which can go from approximately negative topositive 2 billion. And then if you need areally big number, you can go with 64 bitintegers. And those go somewhere between plusand minus nine quintillion. So if you needbigger numbers than that, then you've gota very large application that you're workingwith. And in that case, you're going to needto look into the big package from the mathlibrary, which can handle arbitrarily largenumbers. So you can't get a number big enoughfor the big package to not be able to handle.Although working with numbers at large, you'regoing to take a bit of a performance hit andthe numbers aren't going to be quite as easyto work with is using the primitive typesthat we're talking about here. Now relatedto the signed integers are the unsigned integers.So we have the value 42 here, and just soyou can see, we can create an unsigned integer.And I'll just pick at you and 16 and assignthat the value of 42. And then let's go aheadand run that. And you see that now we havea un 16. So there's an equivalent type ofunsigned integer for every signed integer,we have you int eight, which is an unsignedeight bit integer which can go from zero to255. We have a 16 bit unsigned integer anda 32 bit unsigned integer. Now, what we don'thave is we don't have a 64 bit unsigned integer.But we do have a type byte. And a byte isan alias for an eight bit unsigned integer.And the reason we have that is because theunsigned eight bit integer is very common,because that's what a lot of data streamsare used to encode their data. Now withthese integer types, and again, unsigned unsignedintegers are basically the same type, we'vegot several different arithmetic operationsthat we can do. And these are built into thelanguage. So if I just dropped in this example,here, you see that we've got an integer, aset equal to 10. And we've got another integerbe set equal to three. And we're doing thebasic arithmetic operations that are availabletous.So we can add, we can subtract, we can multiply,we can divide, and then this percent signhere is actually used for the remainder. Soif I run this, you see that we get 10, plusthree is 1310, minus three is seven, and soon. So we get the numbers that we expect,then the one that you might not expect isthis a divided by b. So 10 divided by threeis three, remainder one. And so we get theresult three out, because when you dividean integer by an integer, you have to getan integer result. So the type cannot changeduring the operation. So when we do this,we're doing what's called integer division,and we dropped the remainder. Now, if theremainder is interesting, that's what thisremainder operator is for. And then we canpick up that remainder one out of it. Now,just like when we're doing division, we can'tchange the type. So dividing an integer byan integer can't give us a floating pointnumber, for example, we're also not allowedto add to two integers of different types.So if we take this as an example, we've gotthe integer, a set equal to 10, and an eightbit integer set equal to three. And if wetry and add those together, we're actuallygoing to get an error. So even though go mightbe able to do that, theoretically, it's very,very insistent that it's not going to workacross types without your consent. So in orderto make this work, we would actually haveto do a type conversion on one of the variablesto convert it into the type of the other.So just like we talked about in the last video,where go is very, very hesitant about implicitdata conversion. This is another example.Even though these integers are almost equivalent,go is not going to make that assumption foryou, you're going to have to do the type conversion.Now a couple of other operations that we haveare called the bit operators. So if I dropin this example, we see the full Bit operatorsthat we have, we've got the AND operator,we've got the OR operator, we've got the exclusiveOR operator, and we've got the and NOT operator.Now what's going to happen when we run this?Well, let me just run this first and get theseresults. So you see that if we take a andb, then we get two, if we take a or b, weget 11. And these might not make a lot ofsense. So in order to clear things up a littlebit, let me put in the binary representationof these. And then we can walk through whatthese are doing. So 10 is 1010, in binarythree is 0011. Now when we run into an endoperation, that's going to look for what bitsare set in the first number and the secondnumber. So as we can see, we've got four bitsin each one of these numbers that are allocated.Actually, these are 32, or 64 bits long, butI'm ignoring all the zeros at the beginningof these numbers. So let's look at the fourdigits that matter. So if we look at these,then we see actually, if we add these together,we're going to get 0010. And in binary, thatis two, so 10, and three equals two. Now ifwe do the or, or means if one or the otheris set, so we get one, because a has the firstbit set, neither one has the second bit set,both have the third bits set, so we're goingto include that, and then B has the last bitset. So we'll have that. So now we end upwith 1011, which is one plus two plus eight,which equals 11. Exclusive OR means eitherone has the bit set, or the other does, butnot both. So in that case, we're going todo 1001. The only difference between thisand the OR operation is that third bit wherethey're both set to true. And therefore we'renot going to include that. Now in the endnot that's kind of the opposite of or becausewithin not, it's going to be set true onlyif neither one of the numbers have the bitset. So since the first bit is set in a, we'renot gonna include that, neither one has thesecond bit set, so we're going to includethat both have the third bit set, so we'renot going to include that. And B has the fourthbit set. So we're not gonna include that.So we get 0100, which is equivalent to thenumber eight. And that's how these bit operationswork. The last example that I have to showyou with integers is what's called bit shifting.So when we have this example here, the firstprint statement is going to bit shift a leftthree places. And the second is going a bitshift a right three places. So let's run thisand see what that's going to do. And so weget to number 64, and one, so in order tounderstand that, let me go ahead and put inthese values, so we can understand what'sgoing on. So eight is really two to the thirdpower. And when we do bit shifting, we'rebasically adding to that exponent, as longas we're dealing with the power of two, becausereally, what we're going to do is we're goingto take this to to the third and multiplyit by two to the third, which is equivalentto two to the sixth, and two to the six is248 1632 64. So that's how we get the 64.Now, when we bit shift to the right, we'regoing to take our original number, and we'regoing to divide it by two to however hardwe're shifting, so we're going to divide itby two to the third. And in that case, we'regoing to get two raised to the zero power.And any number greater than zero raised tothe zero power is one.And so that's how those operations work. Thenext data type that I want to talk about arethe floating point types. So we have a lotof different integer types. So we can storea lot of different size numbers. But withinteger types, we can only store integers,so they can be positive or negative integersor zero, but we can't store decimal numbers.So in order to store decimal numbers in go,we're going to use the floating point numbers.Now the floating point numbers in go followI triple E 754. Standard. And in that standard,we're going to pull out two of the types.So we've got 32 bit floating point numbersand 64 bit floating point numbers. So if you'reworking with a 32 bit floating point number,you can store numbers between plus or minus1.18 times 10 to the negative 38, all theway up to 3.4 times 10 to the 38. So fromvery small numbers to very large numbers.If you need even more precision than that,then you can use a float 64. And that cango from plus or minus 2.23 times 10 to thenegative 308th, all the way through 1.8 times10 to the 308. So how do we create floatingpoint numbers? Well, here's some examplesof how we can do that. So line eight hereshows you how you're going to define yourflooding point literals almost all the time.So we're going to declare a variable and setit equal to 3.14. And away we go. This nextline here shows that we can use exponentialnotation. So we can use 13.7 times 10 to the72nd. And that's going to be able to use theshort form iE 72nd to stand for that 10 tothe 72nd. So if I run this, we're actuallygoing to get the final result here, of 2.1times 10 to the 14th as a floating point 64.But notice we didn't get any errors. So allthree of those declarations, syntaxes areokay, so that's how we can work with floatingpoint numbers and show you how you can explicitlydeclare these We can use, let me just do varand float 32, for example, and initializethat. And that's how you're going to declarea floating point number. Now unfortunately,this number is a little big, because we canonly go times 10 to the 38th power. So ifI comment that line out, things are goingto run properly. If I come back in and makethis a floating point 64, then we can restorethis number. And that's another thing that'simportant. If you're going to use the initializersyntax on a decimal, it's always going tobe initialized to a float 64. So keep in mind,you can't do arithmetic operations betweenfloat 64 and float 30 twos. So if you're justusing the initializer syntax, you're goingto want to make sure that everything's workingas float 64. And if you forget, don't worryabout it, the compiler will complain at you,and you can quickly go in there and make surethat everything's working properly. Okay.Now, speaking of arithmetic operations, letme jump in a couple of those. And you cansee the arithmetic operations that are availablewith floating point numbers. So if I run this,we get the expected answers of adding, subtracting,multiplying dividing two numbers together.Now, a couple things to notice here, whenwe divided A by B, we did in fact get a decimalresult, because as long as we're working withfloating point numbers on both sides, we canget a floating point result. As a matter offact, we have to get a floating point result.The other thing to notice, we don't have theremainder operator available that is onlyavailable on the integer types. Further, wedon't have the bitwise operators or the bitshifting operators. So if you need to workwith those, you're going to have to work withthe integer types. The last type of numericprimitive that we have available in go isthe complex type. And this is really kindof exciting because this is fairly rare inthe languages that I've worked with, wherecomplex numbers are actually treated as afirst class citizen, and it opens up go tobe used as a very powerful language for datascience. So if we come in and paste an example,you can see a very basic declaration of acomplex number. Now there are two types ofcomplex numbers. There's complex 64, and complex128. And the reason we have that is we'rebasically taking a float 64 plus a float 64,or a float 32 plus a float 32 for the realand imaginary parts. Now here, I've got avery simple complex number, that's one plustwo I, if I go ahead and run that, you seethat in fact, it prints out as one plus twoI and that's complex 64. So goes parser understandsthe I literal as an imaginary number, andit uses that when you're creating your variable.Now we can actually go even simpler than that,because AI is considered special. And we canrun this with just two AI and we get zeroplus two I down here and the result. Now whatoperations that we have available. Well, wecan do addition, subtraction, multiplication,and division again. So I've got two complexnumbers defined here. If I run this, thenI get the expected result, where the realparts are added together, and the imaginaryparts are added together, or subtracted, multiplied,divide whatever operation we're applying.Now, what happens if you need to decomposethis down. So if I come back to our firstexample, here, where we have one plus twoI, not every operation that I'm going to dowith these numbers is going to need this towork as a complex number. So what happensif I need to get at the real part or the imaginarypart? Well, in order to do that, we actuallyhave two built in functions in the language.So let me wrap this in here with a call tothe real function. And then I canfollow that up with its partner, which isthe image function. And what those are goingto do is those are going to look at the complexnumber that you provide. And they're goingto pull out the real part or the imaginarypart. And these functions work with complex64 and complex 128. So if you run this onthe complex 64, then the real and the imagefunction are going to give you float 30 twosout, if you run this on complex 128, it'sgoing to give you float 64 is out becausethose are the data types used for the components.So if we run this, we see that we get float32 is out. If we convert this to a 128 andrun this again, then we're going to get float64 is out. And it's going to break apart thatcomplex number into the real and imaginarypart. So we can work with those however, weneed to know the complement of these two functionsis the complex function. So if you're workingalong in your program, and all of a suddenyou need to make a complex number. How doyou do that, because you can't use this literalsyntax. So in order to do that, we do haveanother function. And that's the complex function.And this takes two numbers. The first numberis the real part. And the second number isthe imaginary part. So let me go ahead andwipe out this line. Get rid of these realcalls here, and then run. And now that wesee that we can take two floating point numbers.In this case, they're considered to be floatingpoint 60 fours because we're making a complex128. And it creates five plus 12 I for us,the last data type that I want to talk aboutis the text type. And texting go falls intotwo basic categories. One, I can talk a lotabout the other we're just going to touchon. So the first text type that we have availableis a string, and a string. Ingo stands forany UTF eight character, so that makes itvery powerful. But that means that stringscannot encode every type of character that'savailable. For that we need the other texttype which we'll talk about in a second. Butlet's just start by introducing a basic examplehere. So here I've got a string literal, thisis a string, I'm going to print out its valueand its type. So if I go ahead and run this,you see that this is a string print out. Andit's of type string. No big surprise here.Now one of the interesting aspects of a stringis I can actually treat it sort of like anarray. Now, we haven't talked about arraysyet. But I can actually treat this stringof text as a collection of letters. So ifI do something like this, I'm actually goingto ask it for the third letter, because arraysand go are zero based. So I'm going to lookfor the 012. That's the third letter in thestring, which is the letter II. So if I runthis, I get an interesting result, I get thevalue 105. And that's a un eight. So whatthe heck happened there? Well, what's happeningis that strings and go are actually aliasesfor bytes. So we can go ahead and convertthis guy back, since a byte is just an aliasfor a string, and we can get our letter Iback. Now, strings are generally immutable.So while I can inspect the second character,I can't do something like this, if I triedto run this program, let me just print outthe full string here and run this, then Iget an error. And there's actually quite afew things wrong with this. The first thingis I can't assign a string to a byte becauseI'd have to do a conversion. The second thingis I can't manipulate the value of the string.Now with the numeric types, I should do thatthere were quite a few operations that wecan perform with it, there is one arithmeticor pseudo arithmetic operation that we cando with strings, and that is string concatenation.Or in simpler terms, we can add strings together.So in this example, I've got the string sand the string s two. And as you can see downin the printf statement, I'm adding s&s twotogether, and then we're going to print outthe value in the type. So if I run this, yousee that it just merges all the strings together,and it gives us the result. Now another thingthat I can do with strings is I can actuallyconvert them to collections of bytes, whichin go is called a slice of bytes. So in thisexample, I'm starting with a string, thisis a string, and then I'm going to do a conversionto this collection of bytes. And I'm goingto pass the string into that. So if we runthat, we actually get this as a string comesout as the ASCII values, or the UTF valuesfor each character in that string. And thenyou see that the result is a collection ofUN eights, which is a type alias for bytes.Now, why would you use this one? It's a verygood question. A lot of the functions thatwe're going to use in go actually work withbyte slices. And that makes them much moregeneric and much more flexible than if wework with hard coded strings. So for example,if you want to send as a response to a webservice call, if you want to send a stringback, you can easily convert it to a collectionof bytes. But if you want to send a file back,well, a file on your hard disk is just a collectionof bytes, too. So you can work with thosetransparently and not have to worry aboutline endings and things like that. So whilein your go programs, you're going to workwith strings a lot as strings. When you'regoing to start sending them around to otherapplications or to other services, you'revery often going to take advantage of thisability to just convert it to a byte slice.Okay, the last primitive data type that wehave to work with is called a rune. Now arune is a little bit different than a stringtype in go. Because we're a string type representany UTF eight character, a rune representsany UTF 32 character. Now, UTF 32 is a littlebit of a weird animal, because while any characterin UTF, 32, can be up to 32 bits long, itdoesn't have to be 32 bits long. For example,any UTF eight character, which is eight bitslong, is a valid UTF 32 character. So there'sall sorts of tricks that they have to do inthe encoding of the characters in order toknow whether the character is one, two orfour bytes long. So that makes things a littlebit tricky to work with and go. Now we'renot going to get too deep into this subject,we're just going to talk a little bit aboutwhat runes are. And then I'm going to pointyou to some things that you can refer to ifyou actually need to work with rooms in yourapplication. So if we look at this example,here, we're declaring the room a. Now noticethe difference here, if we were declaringa string, we would have double quotes. Whenwe're declaring a single room, we use singlequotes. But if I run this, I'm going to getan interesting result. Notice I get the value97. And it's an int 32. Now that might seema little weird. And the reason for this isbecause runes are just a type alias for int30. twos. So we're strings can be convertedback and forth between collections of bytes.Rooms are a true type alias. So when you talkabout a rune and go it is the same thing astalking about an integer 32 name, I think,well, that's just because we're doing someimplicit initialization here, we're usingthat colon equals syntax. So let's specificallyand explicitly declare this as a rune andtry this again. And we get the same result.And again, that's because a rune is an integer32. Now, you might be feeling a little losthere. So if I've got a UTF 32 characters athow do I work with that. Well, the Answercomes from the go API's. So if I jump outto go lang.org, come into the packages. Andlet me just jump to the strings package. Andthen I'll show you this. Notice this functionhere, read rune. So if you're working witha data stream that's encoded in UTF 32, thenyou have special functions that you're goingto be able to take advantage of, that's goingto return those values out. So if we do readbyte, which is going to read a single character,then we're going to get a bite out and a potentialerror. But with read rune go is going to lookat the byte stream, it's going to pull offthe next room that's available to you thesize of that room, and then a potential error.So you're going to have all the informationyou need in order to re encode that integer32 that you're going to have back into itsUTF 32 character. So things are a little bitmore tricky when you're working with runes.And you're going to have to read into theAPI's for the go package that you're workingwith, in order to understand how to work withthem in your application. Okay, so that coversthe primitive data types that you have towork with and go, let's go into a summaryand review what we've talked about in thisvideo.We covered a lot of ground in this video,and I understand that it might take a littlebit of time to process this and really understandall the different options that you have forprimitive data types and go. Now before weget into the summary, I do want to let youknow that a lot of times the default datatypes that you're given are going to be perfectlyfine. So if you're working with Boolean, you'regoing to get a Boolean type, if you're workingwith integers, you're going to get that signedinteger type floating point, it's going togive you a floating point, 64, and so on.So you don't have to memorize every singledata type. If you need a specific data type,then you can certainly refer to the NGO documentationin order to find out what's available foryou. So let's go through and review what wetalked about. The first thing that we talkedabout was the Boolean type of data, we foundout that it can take two values true or false.And it's not an alias for other types. Soin some languages, a Boolean is actually analias for an integer. So you might use likenegative one for true and zero for false whengo Boolean is its own type. So you can't convertback and forth between integers and thingslike that, you're going to have to work withBoolean as their own type. We also talkedabout the zero value for a Boolean is thevalue of false. And so if you initialize aBoolean and don't set a value for it, it'sgoing to receive the value false. Then wetalked about the numeric types. And the firsttype that we talked about were the integertypes. And that broke down into two differenttypes with the signed integers. And thereare two classes, I guess you could say withinthe sign integer type. There's the INT type,which has varying size, but a minimum of 32bits. And this is going to be the most commontype of integer you're going to deal within your applications. But if you need a littlebit more granularity, or a little bit moreresolution, or control over how much memoryis assigned to the integer, then you can goall the way from int eight, which is an eightbit integer all the way up to 64 bit integer,we also have the unsigned integers where thesigned integers have a plus or minus and sothey can't store numbers quite so large becausethey have to store a plus or minus bit. Theunsigned integers can store larger numbers,but they can only ever be positive. And wehave all the way through an eight bit whichwe can use the byte or the UN eight, typefour, all the way through 32 bit unsignedintegers with the un 32. We have various arithmeticoperations that we can perform on both integertypes. So we can do addition, subtraction,multiplication, division, and that remainderoperation. So remember, division with integersis going to give you an integer result. Soyou're going to lose that remainder portion.So if you need that, you can use that remainderoperator to get it. We also have the bitwiseoperators. So we can do and or Exclusive OR,and the and not operations. And the zero valuefor any integer type is going to be the literalvalue zero. So when you initialize an integertype, and don't assign a value to it, that'sgoing to be equivalent to the value zero,you're not just going to get whatever wasin memory when that variable is initialized,and you cannot mix types in the same family.Now, this is going to be true throughout allof the numeric types. If you have a un 16,for example, and a un 32. And you cannot addthose together, you're going to get a compiletime error. The next numeric type that wetalked about where the floating pointnumbers,so they follow the I triple E 754. Standard,there's zero value is similar to the integertypes, the value is zero. And we have twodifferent versions, we got 32 bit versionsand 64 bit versions that we can work with.And we have several different literal stylesthat we can use to initialize them. So wecan use a pure decimal like 3.14, we can useexponential notation, for example, 13 e 18,or two e 10. And it doesn't matter if thate is upper or lowercase, or you can do mixed.So for example, 13.7 e 12 is a perfectly acceptableway to initialize that. We do have addition,subtraction, multiplication and division thatwe can do with floating point numbers. Nowwe don't have the remainder operation, butthe division operation is going to give usa true floating point result. So we're notgoing to lose our decimal portion. The finalnumeric type that we talked about were thecomplex numbers. The zero value of a complexnumber is zero plus zero I and they come in64 and 28 bit versions and the reason forthat is the two components, the real and theimaginary component are either going to befloating point 32 or floating point 64. Sowhen you add those together, that's whereyou get the 64 and 128 bit versions. We haveSome built in functions that we can work with.So we can use the complex function in orderto create a complex number, we can use thereal function in order to get the real component.And we can use the match function in orderto get the imaginary component of a complexnumber. Now what the data type that comesout of that depends on the size of the complexnumber going in. So complex 64 is going togive you a float 32 out from the real in thematch function and a complex 128 is goingto give you a float 64 out of the real inthe match function, we have the same arithmeticoperations as we do for floating point numbers,we can do addition, subtraction, multiplicationand division. The final category of primitivedata that we talked about, or the text types,and in go, there are really two differenttext types. The most common one that you'reprobably going to deal with are strings. NoStrings are represented as a collection ofUTF eight characters, they're immutable, youcannot change the value of a string afterit's been initialized. You can concatenatestrings together with the plus operator. Andthen you can convert them back and forth betweena collection of bytes with this square bracketbyte syntax and passing in the string that'llconvert it to a collection of bytes. And youcan convert a collection of bytes back toa string by using the string conversion. Theother type we talked about is a rune and arune represents any UTF 32 character. Nowrunes are a little bit more complicated towork with because of the multi step processthat it takes in order to encode a characterinto the UTF 32 character set. So when we'reworking with runes as a primitive type, reallyall we're working with is an alias for aninteger 32. Today, I want to talk about constantsand how you can use them in your NGO applications.Now, there are several things that we needto talk about with constants. So like we'vebeen doing, I want to break this down intoseveral categories. The first thing that Iwant to talk about is how we're going to nameconstants in our NGO applications. Then we'lltalk about type constants, followed by a discussionabout untyped constants. And we'll talk aboutthe differences between those two, and theoptions that each one gives us. And then we'lltalk about a method of generating constantsthat are called enumerated constants. Andfinally, we'll end our discussion by talkingabout enumeration expressions, which are goingto build upon the concepts that we're goingto talk about in that first enumeration discussion.The first thing that I want to talk aboutis how we're going to name our constants.So all constants are going to be precededwith the const keyword, that's going to letthe compiler know that that's what we're tryingto work with. Now, if you've come from otherlanguages, you might be expecting that we'regoing to name our constants, something likethis, where we're going to have all uppercaseletters and separate the words with underscores.The problem with that is if we do that andgo, then the first letter is going to be uppercase.And as you remember, from our discussion onvariables, if we've got an uppercase firstletter, that's going to mean that the constantis going to be exported. And we don't alwayswant that. So instead of this, we're actuallygoing to name our constants the same way thatwe named variables.So if we had a variable that we wanted tocall my const, and we didn't want to exportit, then we would start with a lowercase firstletter, or in other words, we would use camelcasing. And if we did want to export thissymbol, then we would simply change that firstcharacter to uppercase. Now assuming thatwe're going to be working with an internalconstant, then we're going to switch thisback to a lowercase first letter. And thenlet's talk about how we can create what'scalled a typed constant. Now a typed constantis created very similarly to a typed variable.So we can start with the const keyword, thenthe name of our constant, and then we're goingto list the type of the content, and thenwe can set it equal to a value, then if wewant to prove that that worked out the waywe expected it to, then we can go ahead andprint out the value in the type of the constant.And we will do that by using this printf statementhere. And then when we run this, we see thatthe constant is in fact created. It's gotthe value 42 that we assigned, and it's gotthe type that we assigned to it. Now the reasonit's a constant and not a variable is it hasto remain constant. So if we tried to do somethinglike this, change this to the value 27, thenthe compiler throws an error, because we'renot allowed to change the value of a constant.Another characteristic of a constant is thatit has to be assignable at compile time. Sofor example, if I wanted to have a constantthat represented the sine of pi over two,then I might be tempted to do something likethis. I'll create a float 64 constant. AndI'll set it equal to the result of the sinefunction from the math library. And I'll passin 1.57, which is approximately pi over two.And then I can run this right? Well, the problemwith that is in order to determine the sineof that value, that actually requires thefunction to execute, which is not allowableat compile time. And so you can't set yourconstants equal to something that has to bedetermined at runtime. And that includes thingslike setting it equal to flags that you passinto your application, when you run, if you'regoing to do that you can't use a constantto store that value. Now constants can bemade up of any of the primitive types thatwe talked about in the last video. So if wehave this example, here, we've got an integerconstant, we've got a string constant, a floatingpoint constant and a Boolean constant. Andif we run this, we see that all of those printoutexactly the way that we expect. We've gotthe integer the string, the floating pointvalue and the boolean value. Now in an upcomingvideo, we're going to talk about the collectiontypes and the collection types. are inherentlymutable. So for example, you couldn't createan array and declare that to be a constanttype. Arrays are always going to be variabletypes. Now another characteristic that constantshave in common with variables is they canbe shadowed. So if we create a constant atthe package level, and let's just make thisan integer 16, and set it equal to the value27, then we'll delete these guys. And alsothese guys. Now we've got a constant calleda declared at the package level, that's aninteger 16. And then we got a constant inthe main function, that's also called a andit's an integer type. So if we update Thisprintf statement to print the type of variable,we see that the looks like I need to printmy variable twice, we see that the inner declarationof the constant wins. So not only can we changethe value of the constant, but we can alsochange the type because the inner constantshadows that outer constant. And we can provethat by commenting this line out, runningagain. And we see that the package level constantwins. So you want to be a little careful here,because if you're going to reuse constant,it's going to feel like those values are changing.So I wouldn't recommend that you take advantageof this. But if you do get into a situationwhere constants aren't evaluating the waythat you expect them to, this is one possiblereason. Now when we're working with constants,they work very similar to variables when we'reusing them in operations. So if we bring thisline back, and set it equal to 42, and thenwhat I want to do is declare a variable. Sowe'll declare a variable b as an integer,and set that equal to the value 27. And thenwe can do a plus b. And let's see what happenswhen we do that. So if we run that, we infact, get the ability to add a constant toa variable, and the result is going to bea variable. And so since the constant andthe variable are of the same type, we canperform the addition operation on there. Now,our constant is of a different type. For example,if we made variable b in 16, and run this,then we get exactly the same failure thatwe get when we try and add two variables ofdifferent types together. Now, so far, allwe've been talking about are these type constants,we're after the constant name, we list thetype. But we don't have to do that, we canuse the compilers ability to infer the typefor us. So let's just go ahead and do thatwith this example here. When we run this,we see that the constant a is inferred tobe an integer with the value 42. Now giventhat, given that the compiler is inferringthe value, what do you think is going tohappen? If we do something like this, if werestore that previous example, where we'regoing to add this constant to an integer 16?Well, in fact, in this case, the operationsucceeds, which might be a little bit confusing.But the reason that works is because whatthe compiler is actually doing, when it seesthis constant is it's basically replacingevery instance. So the way the compiler seesthis program is it sees it like this. So sincewe're taking a literal 42, and adding an int16 to it, that 42 is interpreted as beingan integer 16. So the compiler doesn't say,oh, constant, a equals 42, that's an integerand always an integer. Instead, the compileris going to look for every time that we usethe symbol a, and it's going to replace thatwith the value of the constant. And so wecan do these implicit conversions when we'reworking with constants, which is somethingthat we can't really do when we're workingwith variables. The next thing that I wantto talk about are what are called enumeratedconstants. So let me go ahead and start thatconversation out by wiping out what we havehere, clean up our code just a little bit.And then I'm going to do this at the packagelevel. Because this is where I've seen thesemost commonly applied, you could do thesein a function level, if that made sense inyour application. So I'm going to declarea constant a, and I'm going to have that asan untyped constant. And I'm going to setit equal to this special symbol called Iota.So when I run this, you see that a is evaluatedto have the value zero, and it's inferredto have the type integer. So what is Iota?Well, Iota is a counter that we can use whenwe're creating what are called enumeratedconstants. So in this example, having an enumeratedconstant isn't terribly valuable. But oneof the things that I can do with constantsis I can actually work with them in a constantblock like this. So when I'm doing this, Ican create another constant set that equalto Iota and another constant and set thatequal to it, let's go ahead and clean up thisbecause we already know what the type is goingto be. So we don't need to be printing thatout. And then let's print this command outtwo more times, switching to B, and C. Sonow we're using Iota three times and whenwe get the result we actually see Iota ischanging its value as the constants are beingevaluated. So the first constant that's assignedhas the value of zero than one and then toknow another special feature that we can takeadvantage of with Iota is that if we don'tassign the value of a constant after the firstone, then the Pilar is going to try and inferthe pattern of assignments. So in this example,we would expect to have an error because Band C don't have a value assigned. But sincewe've established a pattern for how to namethe constants in this block, when we run,we actually get the same result. And that'sbecause the compiler is going to apply thesame formula. So it's going to apply b equalsIota and C equals Iota for us. Now that valueof Iota is scoped to that constant block.So we create another constant block. And inthis case, we create a constant called a twoand set that equal to Iota, copy this line,bring it down, and print out the value ofa two, then what we're gonna find is Iotaresets to zero. So Iota is scoped to a constantblock. And what that lets you do is you canactually create related constants together,ensure that they have different values. Andthen if you have another set of related constants,you can start another constant block and ensurethat they have unique values, but allow duplicationbetween the values in one constant block inanother. So what's an example where you mightuse this? Well, let me just drop in this simpleapplication. And what we're doing here iswe're setting up a constant block, where maybewe're trying to store the specialty of veterinariansin a veterinarian clinic so that our narinecould be a cat specialist or a dog specialist,or maybe we can take his neck specialist to.Now as you can see, inside the cost box, I'msetting the cat specialist equal to Iota.And then in the main block, I'm creating avariable and setting its value equal to catspecialist. So if I check to see if the specialisttype is a cat specialist, then I in fact,get the value true. Now, that works just fine.And this also works if I, for example, usea dog specialist, assign that specialist typeto be a dog specialist run that, then that'sgoing to work out just fine. So everythinglooks really good here, right? And this isa very common use for enumerated constants.However, one thingthat I would warn you about is what happensif I declare this variable and don't initializeit to a type? Well, if I check to see if it'sdog specialist, I get false, which makes sense.But remember, what is the initial value ofIota? Well, the initial value of Iota equalsthe zero value for an integer. And so in fact,even though we haven't specified a specialisttype, it does show up as the value can't specialist.So what do we do about this, so there's acouple of approaches that we can take here.The first is to use the zero value of theconstant as an error value. So we can setthis equal to error, then we don't need thisstatement anymore. And now when we check tosee if the specialist type is a cat specialist,we get the value false because cat specialistsis equivalent to the integer value one, whichis no longer the zero value of the integer.This is a very valuable approach, if you wantto check to see if a value hasn't been assignedto a constant yet, so you can specify an errorspecialist. And then you can check to seeif that value is set equal to the zero valueof that constant. And if it is, you can handlethat error,becausepresumably, you expect that to be initializedin some way. Now, if your application guardsagainst that, and there's no reasonable wayfor this to happen, then you can take advantageof this underscore symbol, which is goes oneand only write only variable. Now what's thevalue of a write only variable? Well, withIota, we have to have a zero value, we alwayshave to start with zero. But if we don't careabout zero, then we don't have any reasonto assign the memory to it. So we can usethis underscore symbol. And we'll see thisin quite a few places in our go applications.And basically, what that tells the compileris yes, I know you're going to generate avalue here, but I don't care what it is goahead and throw that away. So if we run ourapplication, again, everything works justfine. But in this case, we can't actuallyget at the zero value of this constant block.Now the ability to create it lists of enumeratedconstants with a Oda is very valuable. Butthings don't actually stop there. And thereason is, remember, the value of a constanthas to be able to be determined at compiletime, but there are some operations that gois going to allow us to do for example, wecan do addition. So if we do this and run,then we get false again. But what happensif we print out the value of kept specialistif we do that, and we're going to have toremove this line, then in fact, that expressiongot evaluated. So the first line line eightis evaluated to Iota plus five, which is zeroplus five, the next line cat specialist, Iotaincrements, and the formula repeats. So catspecialist is equal to the value six, dogspecialist is seven, and snake specialistis eight. So this can be valuable if you needsome kind of a fixed offset. Now a commonuse case for this is to use the bit shiftingoperators because anything that we can applyto our primitive type, we can apply here aslong as it's not a function expression. Sowe can do addition, subtraction, multiplicationand division, we can do remainder operations,we can do the bitwise operations. And we cando bit shifting, which is one of the moreinteresting use cases that we can take advantageof. And the reason is because we don't havethe ability to raise two powers because raisingtwo powers and go is a function in the mathpackage. So we can't do that in our constantevaluations. But by bit shifting, we can raisethings to the power of two, because everytime you shift the number one level, you'reactually multiplying it by two. So we havethis example here. And I actually stole thisfrom the effect of go article on golang.org.So what we have here is we have an exampleof a constant block that's giving you constantsthat are equivalent to kilobyte, megabyte,gigabyte, terabyte, petabyte, and so on. Sodown here, in our main program, what I'vedone is initialize the file size to some arbitraryvalue. And then I've got this printf statement.And this is basically going to format a resultto print two decimal places, and then theliteral string GB afterward. So this stringhere is basically saying I'm expecting toformat a floating point number, and I'm goingto give it two decimal places, this GB isa literal GB, that's going to be printed inthe result. And then we've got the value that'sgoing to be used to fill this in. And that'sgoing to be file size divided by the GB constant.Now you notice this constant block is setequal to one, and then we're going to bitshift that value 10 times Iota. So the firsttime we're going to bit shift 10 times one,so we're basically going to multiply thisby two to the 10th. And then we're going tomultiply by two to the hundreds for the megabyte,and then two to the 1004, gigabyte, and soon. So when we run this, we get a really convenientway to format an arbitrary file size intoa human readable format. And in the effectivego article, it actually shows you how to puta switch block, which we haven't talked about.So you can make a decision about which constantyou're going to use based on the size of theincoming value. So here, we get this niceway to format this relatively difficult numberto read to be the very easily read 3.73 gigabytes.Now another thing that can be very valuableto do is using bit shifting in order to setBoolean flags inside of a single byte. Soif I paste this example in, we can see anexample of that. So let's just say that we'vegot an application and that application hasusers and those users have certain roles.So inside of this constant block, here, I'mdefining various roles that we can have. Sofor example, you might be an admin, you mightbe at the headquarters or out in the fieldsomewhere, you might be able to see the financialsor see the monetary values. And then theremay be some regional roles. So can you seeproperties in Africa, can you see propertiesin Asia, Europe, North America, or South America.So in order to define these constants, whatI'm doing is I'm setting the value to onebit shifted biota. So the first constant isadmin is one bit shifted zero places, so it'sa literal one, the second one is one bit shiftedone place, that's two, and then four, andthen eight, and then 16, and so on.So what I have is each one of these constantsis going to occupy one location in a byte.So down here in the main program, I'm definingthe roles in a single byte. And I'm oaringtogether is admin can see financials and cansee Europe now if you remember, oaring, isgoing to be set to true if one of the valuesis true, or the other one. So his admin hasthe binary representation of 0000001. I thinkthat's enough zeros, seven zeros, followedby one can see financials is going to endup with 100, can see Europe is going to endup with the value 100000. And so when we orderthose all together, we're going to get thisbyte that has these three flags set to true.So when we run this, we see that we've encodedeight access roles for user into a singlebyte of data. So we're able to store thisinformation extremely efficiently. So if Iwant to see for example, if this user is anadmin, I can go ahead and print his admin,and then print out the value. And then inorder to determine if that's valid or not,then I can do a little bit of bitwise mathematicshere. So I can take the constant is admin,and that with the roles, and what that's goingto do is that's going to apply what's calleda bit mask. So only the bits that are setin the is admin constant, and our roles aregoing to be left as true, which means if we'rean admin, we're going to have the value oneset at that first bit. And then I can comparethat to the is admin constant. So when I runthis, if we have the admin role, then we'regoing to get the value true. Now, if I checksomething that I don't have the role, so letme go ahead and copy this down. And then let'sjust see if they're at the headquarters, sowe'll put that in here. And it's exactly thesame bitwise operations, we're just changingour mask. If we run this, we see that is headquartersequals false Actually, let me put in my Linereturn here, and then run this again. Andyou see that is headquarters equals false.So we can very quickly and very efficientlystore a lot of different information aboutwhat roles and access rights a user mighthave, and a simple byte. And having this constantdefined with a numeration expression makesit really fast and really efficient and reallyclear in our application. Okay, so let's gointo a summary and review what we've talkedabout in this video. constants are anotherone of those foundational elements, that isgoing to be a part of almost every applicationyou're going to write. Now, the first thingthat we learned about with constants is thatthey're immutable, but they can be shadowed.So we can create a constant, we cannot assigna new value to it. But if we create a constanton an inner scope from an existing constant,then not only can we change the value, butwe can even change the type, because thatinner scope is going to shadow the outer scopeconstant, they have to be replaceable by thecompiler at compile time, so the value mustbe calculable. So we're not gonna be ableto access functions or command line argumentsin order to determine the value of the constantsin our application. But we are going to beable to do simple expressions like we talkedabout in the enumeration section. They'renamed like variables. So if you want to exportthe value of the constant outside of yourpackage, then you're going to use Pascal casing.And if you want to leave it as an internalvalue to the package, then you're going touse camel casing to name that constant. Typeconstants work just like immutable variables.So you can use them in arithmetic operations,you can pass them into functions, but theycan only interoperate with the same type untypedconstants have a little bit more flexibility.So they work just like the literals. So ifyou replace that constant throughout yourapplication with the literal value of thatconstant, that's how it's going to work. Sothat's going to allow us to interoperate withsimilar types. So we had the value 42 definedas an untyped constant. And we could add thatto an integer 16, we could add that to aninteger, we can add that to a un 16. Any ofthose would work, because the literal 42 willwork in all of those cases. Then we talk aboutthe enumeration types that we can work with.And we learned about the special symbol Iota.That allows us to start with as values zeroand increments one time every time we useit inside the same const block. Now the onething that we have to watch out for is thatconstant values that match the zero valuesof variables can cause subtle bugs in yourapplication, because you might have logicthat you expect it to initialize the valueof the constant. And if something happensand then initialization doesn't occur, thenyou're going to be working with zero value,which might give you a false match to a constantthat you're evaluatingagainst. Using that Iota operator, we canactually create what are called enumerationexpressions. So we can define the value ofthe constant dynamically by combining Iotawith any arithmetic bitwise operation, orbit shifting operation that's allowable withthe primitive type that the constant and representing,I want to talk about the first two collectiontypes that we have available and go arraysand slices. Now, arrays form the basis ofslices. So I want to start a discussion withthose. And when we talk about arrays, we'regoing to talk about how to create them thebuilt in functions that go offers us to understandwhat's going on with our arrays. And thenwe'll do some exercises working with arraysand see how we can use those in our applications,then we're going to follow the same pattern,but we're going to switch over to slices.So we'll learn the various ways that we cancreate slices, we'll learn the built in functionsthat we can use to understand what's goingon with our slices. And then we'll do someexercises working with those. Okay, so let'sgo ahead and get started by learning how tocreate an array. So the first thing that Iwant to discuss about arrays is the use casefor them. Why do we need them and what arethey used for? So let me just drop in an example,let's just say that we're building an applicationthat's going to work with the grades of studentsin a class. So with that arrays, we're goingto end up with an application something likethis, we're going to have maybe grade one,grade two, grade three, and then we can printout those grades. So we can go ahead and runthis. And we see that we get the scores 9785and 93 printed out. Now this works sort of.But we got a lot of problems here, becauseour application needs to know exactly howmany grades we have to work with, at the timethat we're designing the application. Andworking with these grades as a collectionbecomes very cumbersome. So enter the array,and that's going to solve all of our problems.So in order to see what an array looks like,let's go ahead and delete this code here.And then we'll create an array, that's goingto hold the grades for us. Now the way wedeclare an array is we're going to start withthe size of the array. So we're going to usesquare brackets and then we're going to havean index that's going to be the number ofelements that are array can hold, and thenthe type of data that the array is going tobe designed to store. So an array can onlystore one type of data. So in this case, we'redeclaring an array of integers that can holdup to three elements. If we wanted to holda different type, say we want them to havean array of strings, then we would type thisto a string, and so on. So you have to specifyat the time that you're declaring the array,what type of data you're going to store. Andthen we can use this initializer syntax Toput in the values for our array, so we canput in the same scores that we had before9785, and 93. And then, if we come into ourprint statement here, and add our grades aswhat we're going to print, then we see thatwe have all of the grades printed out togetherin this collection called an array. Rightnow, that's a convenient collector. As westart getting into looping constructs andthings like that, we're going to really findthat having things grouped into arrays andslices and the other collection types is avery powerful way for us to work with ourdata. Now, another advantage that we havewith working with arrays is the way that they'relaid out in memory. So if you declare threedifferent variables and specify their values,it's impossible to know how they're goingto be laid out by the go runtime with arrays.However, we know by the design of the languagethat these elements are contiguous in memory,which means accessing the various elementsof the array is very, very fast. So by collectingour data together in arrays, not only is iteasier to work with, but it also makes ourapplications generally a little bit faster.Now one problem that we have in this examplehere is if you look at it, we're actuallydeclaring the size of the array twice, becausewe have this syntax here, where we're sayingthat we're creating a three element integerarray, but then we're adding three elementsto it. And that's not really required. Ifyou're going to be initializing an array literallike we're doing here, then you can actuallyreplace the size with these three dots here.Basically, what that says is create an arraythat's just large enough to hold the datathat I'm going to pass to you in the literalsyntax. So in this case, we're going to getan array that has three elements in it. Andthat's implied by the fact that in this literalsyntax, we've passed three integers to it,we can also declare an array that has a certainsize, but has its values zeroed out, by doingsomething like this, if we declare an arraycalled students, and let's make that a threeelement array that's going to hold strings.And then let's print out what we have in thatarray. So if we print out, students, makesure I'm spelling everything correctly andrun this, then we see that we have an arraythat's empty. So we have declared a threeelement array that can hold strings. But obviously,there's no elements in there right now. Soin order to specify a value in the array,we're going to use this syntax, so we're goingto call upon the array, and then we're goingto tell it which index we want to work withwithin the array. So in this case, we're workingwith the zeroeth index of the students array.And then let's just assign the name Lisa toit,then we can go ahead and print out our arrayagain, and run it. And now we see that wehave, I always forget to add this line returnhere, then we see initially we have an arrayof students that's full of empty strings.In the second instance, we've actually specifiedthat first element. Now you may be wonderingwhy we're starting with the value zero. Andthe reason is related to how arrays are madeup of contiguous blocks of memory. So whenwe talk about students as the name of thearray, what go is going to do is it's goingto have a pointer or it's going to rememberthe location of the beginning of that array.And then the index that we pass in this casezero, is going to tell it how many stringsto walk forward. So it knows that when ithas a string, a string has a certain length.And so it's going to walk that many strings.So when we pass zero, it's going to be thehead of the students array moved forward zerostring elements. And so that's going to bethe first element of our array. So we canfinish this example out, if I drop some codein here, we can see what it would take tofill out this array. So in this case, we gotthe zeroeth element to Lisa, the first elementis Ahmed. And the second element is Arnold.So if we print that out, we see the expectedresult, where we have Lisa, Ahmed and Arnold.And it doesn't matter what order we work withthese, if we flip these around, then we findthat they do flip around in the array, wecan assign them in any order that we want.Now if we want to get add a specific elementin the array, then we can use this squarebracket syntax again, and dereference theelement from the array. So if we do this,and then change our label again. So we'regoing to get the second element of the array,which is index one, then we can go ahead andrun this. And then we see that the secondelement has the value of Arnold. So we canuse this square bracket syntax in order toassign values to the array, as well as topull out the values that have been assigned.Now another thing that we can do is we candetermine how big the array is. Now, obviously,we created the array up on line eight. Sowe remember at design time that we createdthis, but there may be a situation where youneed to go back and review the size of thearray that you're working with. And the waythat we can do that is using the built inlength function. So if I drop in another printstatement here, and format that, you see thatwe can get the number of students in arrayusing this built in alien function and passingin the array. So if we run this, then we seethat we get the number of students equalsthree and that's going to print out the sizeof the array. So if we change the size ofthe array to say five then the results ofprinting The array isn't going to change,but the size of the array does. Now one thingthat's important to remember is that an arraycan be made up of any type, it just alwayshas to be the same type for a given array.So we've been working with arrays of integersand arrays of strings. So we've been workingwith primitives. But this example here showsthat we can actually make up arrays have anythingelse. So in this case, we've got an arrayof arrays. So let's just say that we're workingwith some linear algebra. And we need theidentity matrix, which is a concept that'sused pretty often in linear algebra. So thisarray here stores a three by three identitymatrix. So the first row is going to holdthe values 100, the second row is going tohold 010. And the third row is going to hold001. So if we go ahead and print this out,then we see that we do in fact, get thosevalues. Another way to look at this, and maybea little bit easier to see is using this wayhere. So we're just going to declare the arrayof arrays, and then we're going to initializeeach one of those rows individually. So thisreads a little bit cleaner, and might be alittle bit easier for you to understand what'sgoing on. And if we run this, we get the exactsame result. Now the last thing that I wantto talk about with arrays is something that'sa little bit different with arrays and gothan in other languages. And that is thatarrays are actually considered values. Soin a lot of languages, when you create anarray, it's actually pointing to the valuesin that array. So if you pass things around,you're actually passing around the same underlyingdata. But in go, that's not true. When youcopy an array, as we're doing on line nine,here, you're actually creating a literal copy.So it's not pointing to the same underlyingdata is pointing to a different set of data,which means it's got to reassign that entirelength of the array. So if I run this, you'llsee what I'm talking about here. So on lineeight, I assigned an array on line nine, Icreated another variable b and assign thatto a, and then on line 10, I changed the secondelement of the array to the value five. Andwhat you see is that when I print out thearray, it has the original values 123. ButB has the new values of 153. So when you'reworking with these, you have to be a littlecareful, because copying arrays, especiallywhen we get into functions, if you're passingarrays into a function, go is going to copythat entire array over. So if you're dealingwith a three element array, that's not a bigdeal, if you've got a million elements inyour array that could slow your program downa little bit. So what do you do if you don'twant to have this behavior? Well, we haven'ttalked about it yet. But I want to give youa hint right now in order to cover this completely.And that is this idea of pointers. So theway that our program is working right nowis that the value B is assigned to a copyof the array. But if we do the address ofoperation, which is this character here, thenwhat we're saying is B is going to point tothe same data that he has. Now we'll get intomore detail about what this means later. Butthe long and the short of it is if I run this,now, A and B are pointing to the same data.So A is the array itself, and B is pointingto a. So when we change the value in line10, we're actually changing the same underlyingdata for both. So when we print them out,we see that the array has changed, as wellas the array that B is pointing to, becausethey happen to be exactly the same array.Now arrays are very powerful. And there'sa lot of use cases where you can use arraysvery efficiently. However, the fact that theyhave a fixed size that has to be known atcompile time definitely limits their usefulness.So in go, the most common use case for usingarrays is to back something called a slice.So let's take a look at a slice. So the firstthing that I want to do is comment out a couplemore items in this example here. So we'llcomment on this one, this one and this one.And then we'll change this over to slice syntax.So the way we're going to do that is simplyby eliminating these three dots here. So aslice is initialized as a literal by justusing the square brackets, the type of datawe want to store. And then in the curly braces,we can pass in the initialized data. So wego ahead and run this, we see that we getthe values 123, it looks exactly like an array.And as a matter of fact, everything we cando with an array we can do with a slice aswell, with one or two exceptions. So in orderto illustrate that, we have the length functionthat we talked about with an array, well,we have the length function with a slice aswell. So if I run this, we see that we doget the length of three. So we initializethe slice with values 123. So with three elements,so the length function gives us a value ofthree. Now there's an additional functionthat we have available with slices, and thatis the capacity. And that's because the numberof elements in the slice doesn't necessarilymatch the size of the backing array, becausethe slice is really a projection of that underlyingarray. So we can have a very large array andonly be looking at a small piece of it. Nowif we run this example, we see that the capacityfunction returns the value three. So the underlyingarray is exactly the same size as the slice.But as we go along here, we'll see how wecan get into situations where the length andcapacity are different, and why that's a verygood thing.Now unlike Likearrays, where we have this syntax here, andwe have to use this address of operation inorder to point to the same data slices arenaturally what are called reference types.So they refer to the same underlying data.So if we run this example, again, rememberwhen we ran this with an array, we saw thatB store different data than a when we weredone. So if we run this, we see that A andB are actually pointing to the same underlyingarray. And so when we change the value inB, we get a change in the value in a. So thisis one thing that you're going to have tokeep in mind when you're working with slices.If you got multiple slices pointing to thesame underlying data, you have to keep inmind, if one of those slices changes the underlyingdata, it could have an impact somewhere elsein your application. Now, so far, we've onlylooked at the literal syntax for creatinga slice. And that's what we're seeing hereon line eight, there's actually several otherways that we can create slices. And that isillustrated with this example here. So yousee on line eight, we're creating a slicethat has the values one through 10. And thenon line nine, we're creating a slice B, andthat's using this bracket with the colon inbetween. And what that's going to do is, it'sbasically going to create a slice of all theelements of what it's referring to. So it'sgoing to create a slice of all of the elementsof a online 10, we're creating a slight C,and that's going to start with a third elementof the parent, and copy all the values afterthat. So that's going to start with the elementwith index three, which is, of course, thefourth element and every element after that,so this is going to copy four through 10,into the slice at sea, then on line 11, we'regoing to do the other syntax, and that's goingto copy everything up tothe sixthelement. And that's the literal sixth element,that's not element number seven, that's actuallythe sixth element, which is going to havethe index five. And then on line 12, we actuallysee an inner slice. So we're going to copythe fourth element through the sixth elementinto our new slice. So let's go ahead andrun this and see what prints out. So we seethe first line printed out is the originalslice that we have. The second line is thecopy of that slice that's copying all of theelements. The third line is going to copythe fourth element on so we see the numberfour through 10. The fourth line copies everythingup to the sixth element, so we get the valuesone through six. And then the last line printedout is a slice from three to six. And so we'regonna get the elements four, five, and sixprinted out. So that can be a little bit confusing,because the first number has a slightly differentmeaning than the second number. So basically,the first number is inclusive, and the secondnumber is XClusive. So for example, if welook at line 12, again, we're going to copyfrom index three, up to but not includingindex six. So that's another way that youcan look at it. Now one thing to keep in mind,remember what I said that all of these operationspoint at the same underlying data. So if wetake element five, and change that value inthe a slice and run this again, notice allof them teams value, because they're all pointingto the same underlying array. So each oneof these operations includes the fifth indexin their results. And each one of those getsupdated to the value of 42. Now another thingto know about these slicing operations isthey can work with slices like we're doinghere, but they can also work with arrays.So if you remember, if I put these three dotsin here, it's actually going to turn a intoan array. And if I run this, we get the sameresult. And that's because slicing operationscan have as their source, an array or a slice.So whatever type of data you're working with,as long as it's one of those two, you canuse these slicing operations. Now the lastway that we have available to us to createa slice is using what's called the make function.And that's a built in function that we haveto work with. So if I delete all this, anddrop this guy out, we're going to use thebuilt in make function. And this takes twoor three arguments. So let's start with twoarguments. So the first thing we're goingto say is the type of object that we wantto create. So you can use make for severaldifferent operations. In this case, we'regoing to be talking about making slices. Soin this example, here, we're going to makea slice of integers. The second argument isgoing to be the length of the slice. So inthis case, I want to start with three elements.So now let's just get some information aboutthe slice that we created here. And we'lldo that by dropping in a couple of print statements.So we'll print out the values of the slice,the length of the slice and the capacity ofthe slice. So they run this, no big surpriseit zeroed out. So when I create a slice, everythinggets set to the zero value, which is whatwe always expect in go to happen every timewe initialize a variable, we expect it tobe initialized to zero values. And that'strue for slices just like it's true for primitives.When we asked for the length, we get a lengththree, when we ask for the capacity, that'salso set to three. Now we can also pass athird argument to the make function, and that'sgoing to set the capacity. So keep in mind,the slice has an underlying array, and theydon't have to be equivalent. So if we runthis, we see that we've created a slice oflength three, it's got three elements in it,but the underlying array has 100 elementsin it. So why would we do that? Well, thereason is because unlike arrays, slices don'thave to have a fixed size over there and Life,we can actually add elements and remove elementsfrom them. So in order to show you an exampleof that, let me drop in another example here.And this is going to start with a slice ofintegers that starts with no elements in it.So if we go ahead and run this, we see whatwe expect, we see an empty slice length ofzero capacity of zero. Now, if I want to addan element to this slice, I can use the builtin append function. So this takes two or morearguments, the first is going to be the sourceslice that we're going to be working with.So I'm going to start with a and I'm goingto add an element to it. And in this case,all I want to do is add the number one toit. And then let's go ahead and print outthe value of the slice the length of the sliceand the capacity of the slice again. So ifI go ahead and run this, we see that in thesecond operation, we have a value one storedin there, we have the length of one, and noticethe capacity is two. So that's kind of interesting.What happened here is when we initialize aslice to the value a go assigned a memorylocation for the slice. And since it didn'thave to store anything, it basically createdan underlying array of zero elements for us.As soon as we added an element, obviously,it couldn't fit in a zero element array. Soit had to assign an array for us. So whatgo does is it copies all of the existing elements,in this case, nothing to a new array that'sgot a larger size. So when we reassigned,it actually did create a new array, this onehas a capacity oftwo, and then it put the value one into thatarray. Now, when we're dealing with smallslices like this, things are pretty cheapand pretty easy, even if you're resizing thearray quite a few times. However, as thingsget very large, these copy operations becomevery expensive. And that's why we have thatthree parameter make function. That way, ifwe know the capacity is going to be somewherearound 100 elements, you can go ahead andstart there. And that way, as you're appendingelements and building up the slicer, you'renot constantly copying the underlying arrayaround. Now, if you remember, when I saidthe append function can take two or more arguments.The reason for that is this is what's calleda variadic function. So everything after thefirst argument is going to be interpretedas a value to append to the slice passed inthe first argument. So if we have this example,here, we're actually going to append the values234 and five to the slice returned by A. Soif we run this guy, we see that we get theelements one through five created there, andthe length is five, like we might expect.But now the capacity is eight knots not fixedin stone, how go resize of the arrays. Butgenerally, what I've seen it does is onceit fills up the underlying array with a slice,when you add the next element, it's goingto create a new array, and it's actually goingto double the size from the previous array.So if we start with an empty slice, we seethat the array would initially be of sizezero, then we'll go to 248 1632, and 64 elements.So that's something else to be aware of, ifyou're just over one of those powers of two,you can actually end up with a lot of memoryconsumed that you're never going to be using.So again, if you have the ability to comeup with a decent first estimate, then that'sgoing to be beneficial to you. Now one commonsituation that you're going to run into isif you have a slice of elements, and anotherslice of elements, and you want to concatenatethem together, so you want to have anotherslice created that has all of the elementsof the first slice, and all of the elementsof the second slice. So you might want todo something like this, if I convert thisover to a literal slice, you might want torun a command something like this. Well, thisunfortunately, is not going to work. If Igo ahead and run this, you'll see that we'regoing to get go complaining to us. And that'sbecause the second argument to the appendfunction has to have the type of the slice.So go can only accept integers here. It can'taccept a slice of integers. But we do havea way around this. So I don't know what it'scalled an NGO in JavaScript, they would callthis the spread operator, where if you havethree dots after the slice, it's actuallygoing to spread that slice out into individualarguments. So if we run this, this is goingto work. And it works exactly the same asif this weren't a slice at all, it's basicallygoing to take this slice and decompose itinto looking something like this. So thesework exactly the same way. But it's a convenientfeature to know about if you have slices andyou want to concatenate them together. Now,some other common operations that you mightdo with slices are stack operations. So let'sjust say that we're treating our slices astack, and we want to be able to push elementsonto the stack and pop elements off of thestack. And things like that, with the appendfunction is going to allow us to push elementsonto the stack. But how do we pop elementsoff? Well, we have a couple of different waysthat we want to do this. If we want to dowhat's called a shift operation, which meanswe want to remove the first element from theslice, then we can do this operation here.And what this is going to do is it's goingto create a new slice that starts at indexone, which has the value two in this example,and takes everything else from that. So ifwe go ahead and print out the value of b,we'll see that that has the elements two throughfive. Now if you want to trim an element offof the end, then you're going to have to usea different syntax here. So we want all ofthe initial elements. So we're going to startwith a colon, and then we'll use that lengthoperation, figure out the length of the slice.But remember, that's going to return the numberthat's too large, we actually want to removean element off. So let's go ahead and do lengthminus one. And this will have the values onethrough four in it. So it's pretty easy toremove an element from the beginning of theslice or the end of the slice. But what happensif you want to remove an element from themiddle?Well, here, things get a little bit hairy.Because what we have to do is we actuallyhave to concatenate two slices together, thefirst slice is going to be all the elementsup to where we want to remove the element.So in this case, we can take the first twoelements, by doing a slice of a, and passingin colon two for this slice that we want tocreate, then we have to concatenate to thatall of the element after the index we wantto remove. So in this case, we're removingthat middle element. So we'll take from threeon and then we have to use this spread operationin order to spread things out so that theappend function is happy. So let's go aheadand run this and then get my syntax correcthere. And now we see that we've got the elements124, and five. So we've successfully removedan element from the slice, they have to bea little bit careful here. because keep inmind, we're working with references to thesame underlying array. Because these are allslicing operations, there's one underlyingarray and everything is being done on thatunderlying array. So just to show you thehavoc that we've created here, let me go aheadand print the value of a out here, and thenprint the value of a out afterwards and goahead and run that. And you see we start outwith the elements 12345. Like we might expect,we do our slicing operation, removing theelements from the middle, we get 1245, whichwas what we might expect. But then when weprint a out, things look a little weird, becausewe have that slice that V represents whichis 1245, our three is completely gone, andfive is duplicated. So this is something tobe very sensitive about. If you're going tobe removing elements from the inside of aslice using a command like we have on line10, make sure that you don't have any otherreferences to that underlying array. Otherwise,you're going to get some unexpected behavior.So what do you do if you have a situationlike this, and you really, really need a tostay the same, and you really, really needB to change? Well, unfortunately, we don'thave the tools to work with this right now.Because what we're going to need to do isto use a loop to create a copy of that slicethat doesn't point to the same underlyingarray, and then you can make the manipulation.So when we get to the looping section, we'llshow you how to do loops. And then you'llhave all the tools that you need in orderto handle that situation. For now, the onlything I can say is be aware of this behavior.And if you get into a situation like this,understand that you're going to have to doa little bit of research in order to makesure that your application responds correctly.Okay, so that covers what I want to talk aboutwith the raising of slices. Let's head intoa summary and review what we've talked about.arrays and slices are very common in NGO applications.And I think you're going to see them all overthe place as you start to work more and morewith the language. So I hope that this conversationhas really helped you understand what arraysand slices are and some basics about how youcan work with them. We started our discussionby talking about arrays. And we learned howthere are collections of items with the sametype. So you can't create an array that hasstrings and integers and things like thatall of the types have to be the same, butdoes collect them all together. And as westart to get into conversations about loopingand things like that, we're going to findhow we get some very powerful tools that wecan work with, when we're basing our dataon arrays and slices. They are fixed size.So when you create an array, you have to specifyat compile time how big that array is. Andit can never change. So you can't make themsmaller, and you can't make them larger. Thereare three different ways that we can declarean array, we can do the literal style, likeyou see on this first line, where we're goingto specify the number of elements and thenwe use a literal that's going to initializethose elements. However, we do have a shorthandnotation that uses three dots instead of thatfirst size. And that's going to be a littlebit more robust in your application design.Because if you run into a situation whereyou need to add another literal, you don'thave to remember to update the size of thearray, it's going to update automatically.We also have this third syntax where we candeclare a zero data array. In this case, we'regoing to declare an array of three integers,each one of those integers is going to startwith the value zero. We also talked abouthow arrays are zero based. So we're goingto be counting from the head of the array.So the first element in the array has theindex zero, the second element has the indexone, and so on. So in this example, if weasked for the index one of array, we're goingto get the value three out, we have the Lenfunction that's going to return the size ofthe array in case you need to know that someother place in your application. And you wantto have a way to be able to handle that robustly.That's not relying on the fact that you know,at design time how big that array is, youcan use the Le n function to interrogate thesize of the array. Anytime you're moving thearray around, it's going to copy the underlyingdata. So if you have a three element array,and you assign that to another variable, allthree elements are going to be copied to anew location in memory and you're going tobe working with an independent copy. So thatcan cause some unexpected behaviors becauseif you change that copied array unexpectedchanges to be reflected back in the initialarray, that won't happen. And it can be veryexpensive because all that memory has to beallocated a second time.Then we moved on to discuss slices and howthey're very similar to arrays. As a matterof fact, they're backed by an array. So everyslice that you see, under the covers, go hasan array that's holding all of that data foryou. There are several creation styles, wesaw that we can create a slice by slicingan existing array or another slice, we havethe literal style that we can use, which isvery similar to the array literal, exceptfor we just leave those three dots out, becausethe size of the slice is dynamic. And so itcan be determined at runtime, we also sawhow we can use the make function. So if wepass two arguments to the make function, thefirst is going to be the type of slice thatwe want to create. So in this example, we'regoing to create a slice of integers. And thesecond parameter is going to determine thelength and the initial capacity of the slice.If we need a capacity that's different thanthe initial length, then we can pass a thirdparameter into the make function. And that'sgoing to allow us to specify a capacity independentof that initial length, the Len function returnsthe length of the slice itself, whereas thecapacity function returns the length of theunderlying array. So if for some example,you want to control the resizing of the slice,you can go ahead and do that. And use thatcapacity to function to understand when you'restarting to get to your limits, you can alsouse the append function to add an elementto the slice. And what that's going to dois it's going to take in a parent slice, andit's going to take in one or more elementsto add to that slice. Now through the courseof that append operation, you can add elementsthat exceeds the capacity of the underlyingarray. If that happens, then you are goingto trigger a copy operation. And all of thoseelements are going to have to be copied toa new location. So be aware of that. If you'redealing with large slices of data, and youend up resizing, a lot of times your applicationperformance can suffer, you might want tothink about using that three parameter makefunction, in order to set the capacity ofthe slice close to where you need it to be.When you're passing slices around in yourapplication. Keep in mind that assigning anew value to an existing slice actually pointsto the same underlying array. So if you manipulatethe slice in one location, it's actually goingto affect all the other slices that are pointingto that same underlying data. So we talkedabout an example where we created a sliceand removed the middle element out of it.And we saw how that actually affected theinitial slice, and could cause some behaviorthat we weren't expecting. I want to completethe discussion, I started in the last videoby talking about the two remaining collectiontypes that are available and go. And thosetypes are mapped in structs. So we'll startour discussion by talking about maps, we'lltalk about what they are, how we can createthem, and then how we can manipulate the datawithin the maps, then we'll move on to thestruct data type, we'll talk about what theyare, how we can create them, then we'll moveon to the naming conventions that we're goingto have to be aware of as we're working withstructs, then we'll talk about a concept calledembedding, and how we can use that to havea struct that we're creating inherit a lotof functionality from a base struct, thenwe'll finish our discussion of structs bytalking about a concept called tags, and howwe can use tags to provide some additionalinformation about the fields within our structsto help the consumers of objects created fromthe struct get additional information abouthow that field is intended to be used. Okay,so let's begin our discussion by talking aboutwhat maps are and how we can use them. Sothe first thing that we're going to need toget our heads around when we're talking aboutmaps is what exactly a map is. And I thinkthe easiest way to show you what a map is,is by showing you a map. So we have an examplehere of a variable called state populations.And this represents a map of US state namesover to the population within those states.So what we see here is a map is going to takesome kind of a key, in this case, the statenames, and it's going to map that over tosome kind of a value, in this case, the populationof that state. So what this provides us isa very flexible data type. When we're tryingto map one key type over to one value type.Now there's a couple of constraints we'regoing to have to keep in mind. As you cansee up in the Declaration on line eight, we'regoing to map one key type to one value type.So all of the keys in this map have to beof type string, and all of the values haveto be of type integer. Now we can use a lotof different types for the keys. And we canuse any type for the value. But when we declarea map, that type has to be consistent forevery key value pair within the map. So ifwe run this, we can see an example of whata map looks like when we print it. And it'snot the best output in the world, but we canget an idea of what's going on. So we seethat we get the key value pairs printed out.So we get California, Texas, Florida, NewYork, Pennsylvania, Illinois, and Ohio. Soone thing that I just alluded to is that wehave a lot of options with the key type, butwe don't have an infinite number of options.So the basic constraint on the keys when you'recreating a map is they have to be able tobe tested for equality. Now most of the typesthat we're working with can do that. So Booleancan be tested for equality, all of the numerictypes, strings, pointers, interfaces, structsarrays, and this thing we haven't talked aboutcalled channels. All of those can be testedto see if one instance of for example Fora string variable is equivalent to anotherone. However, there are some data types thatcannot be used for equivalency checking. Andthose are slices, maps and other functions.So for example, if we create a map calledm, and we'll make that a map of, say we wantto map slices of integers over two strings.And we'll use the literal syntax for that,and then we print that out, then you wouldexpect a map to print out. But in fact, weget an error because we've got an invalidkey type because a slice cannot be a key toa map. However, if we had an index here, thenit turns it into an array. And now we do geta successful printout, of course, the mapis empty. So we don't see anything, we justsee this empty map right here. But we weresuccessfully able to create the map, becausean array is a valid key type, but a sliceis not. So what are the different ways thatwe can create a map? Well, you see, the firstway here, this is the literal syntax, andthis is going to probably be the most commonway you're going to see maps created. So wejust need to declare the type of map. Andwe're going to do that using this syntax thatwe see here on line eight, where we're goingto start with the map keyword in square brackets,we're going to list the type of the key, andthen after the square brackets, we're goingto list the type of the value. Another waythat we can do this is we can use the builtin make function again. Now we first saw themake function when we were talking about slices.But we can use that once again here. So ifI copy this variable up here, and I set thisequal to the result of calling the make function,and we're going to tell it what type of mapwe want to make. So we're going to use themap keyword again, the key is type string,the value is type integer. And now we cango ahead and remove this colon. And if werun this, we have to remove our m again fromour print statement. Now if we run it, weget the same value printed out. So this isa common way that you can work with maps,if you don't have at the time that you'redeclaring the variable, the entries that you'regoing to want to put in it. So for example,if you're populating the map within a loop,you might use this syntax. Now another optionthat you have with the make is it will takea second parameter. So if I run that likethis, then that works. However, I wasn't ableto find the intention for this. So this isavailable. But it doesn't seem to affect thelength of the map that's created, the mapis always going to be the length of the numberof elements in it, but it might have someeffect under the covers. If you find thatwhat that's for, please leave a comment downbelow. So that I can learn along with youknow, let me go ahead and remove this becauseI don't know what it's there for. And I haven'tseen that very commonly used and run thisagain. And we can see that we're back to whereour map used to be. Now how are we going tomanipulate the values in our map? Well, thefirst thing that we can do is we can pullout one single value from the map by usingthe square brackets and typing in the valueof the key. So the key can be provided eitheras a variable or as I'm doing here as a literal.So if I go ahead and run this, we see thatwe get the population of Ohio is 11 point6 million people. So we can interrogate thevalues of our map. Using this syntax here.We can also add a value to our map using avery similar syntax. So we call on the statepopulations variable. And I add a key. Inthis case, let me add the next largest state,which is Georgia. And it as of 2016, had apopulation of 10,310,371. Now I can pull thatvalue back out, run this. And we see thatwe get that value printed out of our map.If we print the entire map out, we see righthere, Georgia gets added in here. Now, somethingyou might have noticed, let me do this, I'mgoing to copy this print line here, put itabove where we added the Georgia key. Andnotice the ordering is different. And thisis going to be a very important thing to keepin mind as you're iterating through maps lateron,the return order of a map is not guaranteed.So even though we declared California, Texas,Florida, New York, Pennsylvania, Illinois,Ohio, and then added Georgia, this is notlike a slice, a slice or an array would returnthose elements in exactly the same order weprovided them. In a map, everything is storedin a way that cannot guarantee the order uponreturning. So even though we just added oneentry here, it completely changed how everythingwas organized within the map, and we get ita little bit different output. Now anotherthing that I want to show you is that we candelete entries from the map. So we can dothat using the built in delete function. Thefirst is the map that we want to delete from,and then we need to provide the key that wewant to delete. So if we go ahead and deleteGeorgia back out, it wasn't there very long.But if we run it looks like I need to addmy s here. Now if we run it, we see that gais no longer part of our map. So we can addby just calling on the map providing a newkey and value. We can delete using the builtin delete function, and we can interrogatea value from the map using those square brackets.Now, an interesting thing about deleting isif I asked for the value of that key again,what do you think I'm going to get? So wesee here in the output, Georgia is no longerpart of the map. So you might expect somesort of error to be raised. Well, in fact,if I run this We get the value zero out. Nowthat should cause you some concern, becausedoes Georgia have a population of zero? Oris it missing from our map? Did I just misspellthe key? What's going on? So for example,if I fat finger, Ohio, and I forget the Iand run this, I get a value zero. Well, dideverybody move out of Ohio? What happened?What with what we know right now, there'sno way for us to really now. So there's anotherway that we can interrogate our map. And thatis using what's called the comma. Okay, syntax.So with doing what we know, right now, we'rebasically doing this, we're calling statepopulations. And we're asking for the key,Ohio, and I'll continue that misspelling.And then we're printing out the value of thatpopulation variable, and we get the valuezero. Well, we can also add an optional comma,okay, here. And when we do this, let's goahead and add that okay, variable to the outputand see what that's going to do. So noticethat this prints out the value false. So theokay is false if the key was not found withinour map, but if we add the eye back in andcorrect our spelling, we see that we get thevalue true out. So if you're in a situationwhere you're not sure if the key is in themap or not, then you can use this comma, okay,syntax. As a matter of fact, if you just wantto check for presence, then we can use thatright on the operator again, in order to throwaway the actual value. And then we just getthe okay variable printed back out. Now, there'snothing magic about the variable name, okay.But it is conventional to use, okay, and yougo programs when you're using it for thiskind of a test. The next thing that I wantto show you about maps is we can in fact,find out how many elements are in them. Sowe can do that using the built in Le n function.So if I go ahead and run that, we see thatwe get the value seven, and we have let'ssee 12345 yet we have got seven states declaredin our map right now. So if we added anotherstate back in, then the length function wouldreturn eight. The other thing that's interestingto know is that just like slices, when youhave multiple assignments to a map, whichis especially important when you're passingmaps into functions, the underlying data ispassed by reference, which means manipulatingone variable that points to a map is goingto have an impact on the other one, so I candemonstrate that by creating another variablecalled SP, and set that equal to state populations.And then let's go ahead and delete poor Ohioout of SP, it always seems to get left out.And then we can print out SP and print outstate populations. So if I run this, we seethat in our first result, we don't have theentry Ohio anymore. And then our secondprint statement, which is the original map,Ohio has been removed from there as well.So if you start passing maps around and youstart manipulating the data within that map,keep in mind, you can have side effects onthe calling functions or any other place wherethat map is referred to. Because manipulatingthe map in one place is going to have impactson every other place to use. So the finalcollection type that I want to talk abouttoday is called a struct. Now at first, youmight not think of a struct as a collectiontype. But go with me on this, if I drop inan example, we see here an example of a struct.Now I'm in kind of a doctor who frame of mindtoday. So we're going to run with an examplethat's talking about some information aboutone of the doctors from Doctor Who. So ifI go ahead and run this, we can see that we'regetting information on the third doctor, theactor's name is john Pertwee, we see someof his companions listed out here. So thereason I call this a collection type is lookat the declaration or the struck on line seventhrough 11. What we have here is a list offield names. So we've got number, actor nameand companions as field names, and then atype associated with that field. So the doctornumber is an integer, the actor name is astring, and the companions is a slice of strings.So what the struct type does is it gathersinformation together that are related to oneconcept, in this case, a doctor. And it doesit in a very flexible way. Because we don'thave to have any constraints on the typesof data that's contained within our struct,we can mix any type of data together. Andthat is the true power of a struct. Becauseall of the other collection types we've talkedabout have had to have consistent types. Soarrays always have to store the same typeof data slices have the same constraint. Andwe just talked about maps and how their keysalways have to have the same type. And theirvalues always have to have the same type withinthe same map. When a struct we can have ourfields describe any type of data that we want.So we can have structs that contain otherstructs, anything that we want. So while ina lot of ways, structs are the simplest typeof collection that we're going to be talkingabout. They're also one of the most powerful.So we see down here in the main function,how we can create a struct and I'm using thedeclaration syntax, where I'm using namedfields. So we're going to create a variablecalled a doctor. And we're going to use thisliteral syntax. So we list the type of thestruct doctor, we use the curly braces likewe do with every other literal definition.And then I have a list of the field namescolon and the value. So number colon threeafter name colon, john Pertwee, and then campaignTo set equal to this slice of strings nownotice that when I'm setting something equalto a slice, I do actually have to tell go,what kind of collection type, I'm initializingfor that, and then down here on line 23, Igo ahead and print the entire value of thestruct out. And you see that I get the valuesprinted out, I see three, john Pertwee, andthen the three companions that john Pertweehad, in his time as the doctor. Now if wewant to interrogate one value from a struct,then we're going to use what's called thedot syntax. So if we ask for the actor nameof this struct, then we're going to put adot after the variable name, put in the fieldname. And when we run this, we see that weget john Pertwee out. So we can work withthe struct as a whole, or we can start drillingdown. As a matter of fact, we can drill downthrough the structure. So if we ask for thecompanions, we can get that out, certainly.But this is a slice like any other slice.So if I just want the second item in the collection,I can get Joe grant out by interrogating theslice that gets returned by asking for thecompanions field. Now another way that wecan instantiate our struct is using what'scalled a positional syntax. So here, I'm listingthe field names, but it don't have to. Soif I go ahead and take these out, and takethis out, so I print the entire struct out,then I get exactly the same result as I gotthe first time. Now, this is valid go syntax,I would encourage you not to use it, though.And the reason for that is it can become amaintenance problem. So for example, let'sjust say that this is our struct, and thisis checked into source control, and everybody'shappy with it. But then let's say we havea change request Comm. And we're going toadd a list of the episodes that each doctorappeared in. So that's going to be anotherslice of strings. And that gets added righthere.Well, if we use the positional syntax andtry and run this, we've got a problem. Becausego doesn't know how to map the fields thatwe provided into the initializer. Becausethere's three values provided in the initializer.And there's four values in the struct. Sowe have to find every place that we have oneof these declared with the positional syntax.And we have to add, for example, a placeholderor populated or something. So we go aheadand run this and everything's working again.But now, what happens if another change comesalong, and somebody does this? Now they'vechanged the order of the fields. When we runthis, everything looks fine. But when I askedfor the doctor's companions, again, I getan empty slice, because the positional syntaxrequires the fields to be declared in thecorrect order, much better for us to go aheadand add in the field names. So let me dropthose in real fast here. Now when I run this,I get the expected results out. And noticewhat the field names syntax, I don't evenhave to have it in the same order as they'redeclared in the struct go is going to figureout how to map the data from the initializerover into the object that's created by usingthose field names. The other advantage thatI have is, if I don't have any informationabout the episodes at this point in my program,I actually can ignore the fact that that fieldexists. And what this means is I changed theunderlying struct without changing the usageat all, which makes my application a littlebit more robust and change proof. So whileit is possible for you to use the positionalsyntax, I would strongly recommend you donot use it unless you've got a very shortlived struct. We'll talk about anonymous structshere in a second. And in those situations,positional declaration might make sense becausethat struct is not going to live for verylong. However, I would strongly recommendanytime you're taking the time to declarea type, like we're doing here on line seven,then you're generally going to be better servedby using field names explicitly instead ofthe positional syntax. Okay, the next thingI want to talk about are naming conventions.So as you can see, here, I've got my typedeclared as Doctor with a capital D, and thefields declared as lowercase. So the rulesin this situation, follow the rules for anyother variable and go, if we start with acapital letter, it's going to be exportedfrom the package. If we start with a lowercaseletter, it's going to be internal to the package.So in this case, I can work with all of thefields, because I'm declaring the doctor typein my main package. And so my main function,which is also in the main package has accessto it. However, nothing in any other packagewould have access to this. So it would beable to see that there is a doctor struct,but it wouldn't see any field names. So ifI did want to publish these out, so anythingcan use them, I would have to go ahead andcapitalize these field names, of course, orhave to capitalize them in my declarationas well. And then when I run I get the exactsame result that I got before. So I forgotto change this variable as well go ahead andrun that, and I get the expected result out.So generally, we're going to follow the sameconvention as we do with any other variable.uppercase is going to export lowercase isgoing to import, you should use Pascal casingand camel casing, you shouldn't have underscoresand things like that in your field names orin your struct names. Now if you notice hereon line seven, I have explicitly created atype called a struct. And that's going tobe a very common way that you're going tosee struct to use. You see you're going todefine a type and then everywhere you needto use that struct, you can just refer toit by its type. But we don't have to do that.And there are some situations where you mightsee a program like this. So on line eight,we're declaring what's called an anonymousstruct. So instead of setting up a type, andsaying, doctor, and that's going to be a struct,and that's going to have a single field calledname, that's going to take a string. We'recondensing all of that into this single declarationright here. Now, I can't use this anywhereelse, because it's anonymous. And so it doesn'thave an independent name that I can referto it. But I can certainly use this just fine.So let me go ahead and delete this. And thennotice I'm using the initializer syntax righthere. So we got two sets of curly braces,it's very important to remember what eachone is doing. So the first set of curly bracesis paired to the struct keyword. And it'sdefining the structure of the struct. Thesecond is the initializer. And it's what'sgoing to provide data into the struct. Soif we run this application, we see that wedo get the struct with the value john Pertweeprinted out and everything's gonna work justfine. Now, when are you going to use this,this is going to be used in relatively fewsituations, basically, you're going to usethis in situations where you need to structuresome data in a way that you don't have ina formal type. But it's normally only goingto be very short lived. So you can think aboutif you have a data model that's coming backin a web application, and you need to senda projection or a subset of that data downto the client, you could create an anonymousstruct in order to organize that information.So you don't have to create a formal typethat's going to be available throughout yourpackage for something that might be used onlyone time.Now, unlike maps, structs are value types.So if I take this example here, and let'sjust use the same basic manipulation thatwe've been doing with all of these collections,so I'm going to create another struct, andI'm going to assign it to the value of mycurrent struct, and then I'm going to manipulatethe value of that name field. So I'm goingto create another doctor called Tom Baker.And then let's see what happens when we printboth of these doctors out. So as you can see,even though I copied another doctor from adoctor, and change the value, the values remainindependent. So a doctor still has the namejohn Pertwee, another doctor has the nameTom Baker. So unlike maps, and slices, theseare referring to independent datasets. Sowhen you pass a struct around in your application,you're actually passing copies of the samedata around. So if you've got structs, thatare very, very large, keep in mind, you'recreating a copy every time you're manipulatingthis. Now, just like with arrays, if we dowant to point to the same underlying data,we can use that address of operator. And whenwe run this, we have in fact, both variablespointing to the same underlying data. A doctoris the struct itself. Another doctor is apointer to the struct. So when we manipulateits name field, we're actually manipulatingthe Name field of the a doctor struct. Thenext thing that I want to talk to you aboutin go is a concept called embedding. Now,you may have heard in your research on thego language that go doesn't support traditionalobject oriented principles. So for example,we don't have inheritance, and oh, my goodness,how am I going to create my program if I don'thave inheritance available? Well, let me showyou what go has, instead of an inheritancemodel. It uses a model that's similar to inheritancecalled composition. So where inheritance istrying to establish the is a relationship.So if we take this example here, if we werein a traditional object oriented language,we wouldn't want to say that a bird is ananimal, and therefore a bird has a name abird has an origin, a bird has also bird thingslike its speed, and if it can fly or not,go doesn't support that model. But instead,it supports composition through what's calledembedding. So right now we see that animaland bird are definitely independent structs,there's no relationship between them. However,I can say that a bird has animal like characteristicsby embedding an animal struct in here likethis. So I've just embedded the struct itself,I haven't provided a name. Now, if I did somethinglike this, then how to have a named fieldin the bird called animal. But I'm not doingthat I'm doing this. So I'm just saying embedthe animal struct right into the bird struct.Now how can I use this? Well, let me dropin a new main function here. And then youcan see, so I'm creating an instance of abird. And then I'm initializing the variables.So name is going to be E mu origin is goingto be Australia, the speed is 48 kilometersan hour, and it cannot fly. So if I printthis out, I see that other than a little bitof a strange syntax with that inner type,I see that I have all of my fields defined,it's a matter of fact, I can come in hereand dig into any one of those animal fields.And everything worked exactly like I wouldexpect them to so it kind of looks like birdis inheriting the properties of animal. Butreally what's happening here is there's somesyntactic sugar go is handling the delegationof the request for the Name field automaticallyto the embedded animal type for you. So ifyou try and pass these around and look tosee if the bird is a type of animal, it isnot bird is still an issue. Independent structthat has no relationship to an animal otherthan the fact that it embeds it. So it's nota traditional inheritance relationship wherea bird is an animal. Instead, it's a compositionrelationship, which is answering the questionhas a, so a bird has an animal, or has animallike characteristics is how we would say itin this example. But it is not the same thingas an animal, they cannot be used interchangeably.In order to use data interchangeably, we'regoing to have to use something called interfaces,which we'll talk about a little bit later.Now we can see in this example, that if wedeclare the type, and we initialize the variablesafterward, everything's pretty clear, we canjust treat all of the fields as if they'reowned by the bird struct. And we don't haveto worry about the internal structure of thestruct. And that's very intentional. However,if we're going to use the literal syntax,we do have to know a little bit about thebirds internal structure. So let me drop inthis code here, clean up our formatting alittle bit, and then remove these lines here.So this is declaring exactly the same object,we're going to have an E mu from Australia,the speed is actually going to be 48 kilometersan hour, and can fly is going to be false.But notice what I have to do here, I haveto explicitly talk about the internal animalstruct. So when I'm working with the literalsyntax, I have to be aware that I'm usingembedding. But if I just declare the objectand manipulate it from the outside, I don'thave to be aware of it at all. Now, when shouldyou use embedding? Well, I will say Generally,if you're talking about modeling behavior,embedding is not the right choice for youto use. When we get into methods, we willsee that embedding will allow behaviors ormethods to carry through into the type thathas the embedding. However, the fact thatwe can't use them interchangeably, is a verysevere limitation. Generally, it's much betterto use interfaces when you want to describecommon behavior. So when is embedding a goodidea? Well, if you're offering a library,for example, and let's just say you're makinga web framework, and you've got a very sophisticatedbase controller, in that case, you might wantto use consumers of your library to embedthat base controller into their custom controllers,so that they can get useful functionalityout of it. In that case, you're not talkingabout polymorphism. And the ability to interchangeablyuse objects, you're just trying to get somebase behavior into a custom type. And in thatcase, embedding makes sense. The last thingthat I want to talk about with structs isa concept called tags. So let me just dropin another example program here that we canwork with. This is a simpler version of whatwe had before, because we don't need to structsfor this conversation. So we're just goingto have a simple animal struct with the nameof the origin fields. And then what I wantto do is I want to add what's called a tagin order to describe some specific informationabout this name field. So let's say for example,that I'm working with some validation framework.So let's just say that I'm working withina web application, and the user is fillingout a form and two of the fields are providingthe name and the origin. And I want to makesure that the name is required and doesn'texceed a maximum length. So I can do thatwith a tag that looks something like this.So the format of a tag is to have backticksas the delimiters of the tag, and then wehave these space delimited key value pairs.So my first field is going to be required.So I'm going to say that the Name field isrequired. And then I've got a max length argument,which in this case, is set to 100 characters.So the value of the tag is arbitrary, youcould actually put any string that you wantin here, but this is the conventional usewithin go, we're going to use space delimitedsub tags. And then if you do need a key valuerelationship, then you're going to use a colonto separate the key and the value. And thenthe value is typically put in quotation marks.So now that we have this, how do we get atit? Well, the way that we're going to getat it is using gos reflection package. Sothere's no straightforward way from an objectto get at the tags of a field, you have touse reflection in order to get that, unfortunately,go makes this pretty easy. So the first thingthat I need to do is I need to get the typeof an object that I'm working with. So I getthat using the reflect packages type of function.And I have to pass in an object to this. SoI will just initialize an empty animal toget them that and then I can grab a fieldfrom that type, using the types field by namemethod, and then passing in the name. In thiscase, I want the name name. And now I canget at the tag by asking for the tag propertyof the field. So if I run this, we see thatI do in fact, get that tag out. Now, whatdo I do with that? Well, that's really upto my validation framework to figure out alltags do is provide a string of text and somethingelse has to figure out what to do with that.So this tags value is meaningless to go itself,we're then going to have to have some sortof a validation library that's going to haveto parse this figure out that yes, requiredis part of the tag. And then it's going tohave to decide well, if required is thereand then I'm going to apply this logic makingsure the string is non empty or whatever makessense in our use case. Okay, so that finishesup what I have to talk about with maps andstructs. Let's go into a summary and reviewwhat we've talked about. In this video wetalked about the two remaining collectionstypes that are available and go, we talkedabout maps, and we talked about structs. Andwhen we talked about maps, we learned thatthey are collections of value types that canbe accessed by keys, they can be created asliterals, or via the make function, and membersare accessed via the square bracket key syntax,we can check for the presence of an elementwithin a map using this comma, okay syntax.So we can ask for the value at a comma, okay,and what the map is going to return is thevalue type as well as a Boolean, that's goingto indicate if the value was found or not.Now, if the value wasn't found, your valuevariable is going to be the zero value ofthe value type in that map. So for example,if you've got a map of strings to strings,and the element isn't found, you're goingto get an empty string returned out, if you'vegot a map of string to integers, for example,you're going to get the value zero. If youhave multiple assignments to the same map,they all point to the same underlying data.So they're what are called reference types,which means that if you manipulate the datain a map in one location, any other variablesthat are pointing to that same map are goingto see that change as well. We then went onto talk about the struct type, and how they'rea collection of disparate data types. So whereasarrays and slices and maps are all collectingthe same data type together, a struct is uniquein that it collects any type of data together.So you've got a common field name. But thosefields can point to any valid data structurewithin go. They're keyed by name fields, whichgenerally follow the syntax of any valid variable,including whether they export it or not. Soif you capitalize the name of the field, thenthat's going to be exported from the package.If you leave it lowercase, then that's goingto be kept internal to the package. They'renormally created as types. But we can createanonymous structs if we need to. And again,common use cases for this are very short livedstructs, such as generating a JSON responseto a web service call, that's only used onetime. structs are value types. So if I assigna variable to an existing struct, then allof the struct values are going to be copiedover to create a brand new struct. So if Imanipulate a struct in one location, it'snot going to affect any other variables inyour application. We don't have an inheritancesystem within go. But we can use compositionusing what's called embedding. So when wewant to embed one struct within another, wejust list the type of the struct, and we don'tgive it a field name. Go is then going toautomatically interpret that for us and delegateany calls for fields or methods in the containingstruct down to the embedded struct. If thattop level struct doesn't contain a memberwith that name, wealso learned a little bit about tags and howthey can be added to struct fields to describethat field in some way. So there's a lot ofdifferent use cases. For this. The JSON packageuses this to provide ways to map field namesfrom NGO field names, which are typicallyuppercase to follow JSON conventions, whichare typically lowercase. We also saw thatwe can use this, for example, to pass informationinto a validation framework. And that frameworkcould generate errors. If we need fields tobe required, or have rules about their lengthor things like that, I want to start a discussionabout the tools that go makes available tocontrol the flow of execution in our applications.In today's video, we're going to focus ontwo of these tools, the if statement and theswitch statement. We'll start a discussionby talking about if statements. And we'lltalk about all of the different ways thatwe can use them in our applications. And we'regoing to specifically focus on the variousoperators that are often associated with workingwith if statements. And we'll go through whatthose are and how you can use them. And thenwe'll talk about the relatives of the if statement.And those are the if else statement. And theif else if statement. We'll then move on totalk about switch statements. And we'll startthat conversation with some simple use casesfor switch statements. We'll then talk aboutcases with multiple tests, how we can followthrough from one case to another in our switchstatements. And then we'll talk about thisspecial version of a switch called a typeswitch. So let's go ahead and jump in andstart learning about if statements. In orderto start our discussion about if statements,I want to start as simply as we can. So ifyou look at this application here, it's gotjust about the simplest if statement thatI could come up with. So we're going to startwith the if keyword as you see here on lineeight. And then we're going to have an expressionthat generates some kind of a Boolean result.And that's actually where we're gonna spenda lot of time when we're talking about ifstatements is how to generate these Booleanresults. In this case, I'm just using theliteral true, which is the simplest Booleanresult that we can come up with. And theninside of these curly braces here, I've gotthe code that's going to execute if that Booleantest returns true. So in this case, sincewe're using a literal true, then the testis always going to pass. And when I run thisapplication, I see that I get this test istrue printed out in the results here, if Ichange this to a literal false, and we seethat we don't get any output from the applicationbecause the code inside of the curly bracesis not executed. Now, one thing to keep inmind if you're coming to go from another language,is you may be expecting this to be valid syntax.Well, if we try and run this, we actuallyare going to get a syntax error because oneof the design decisions that was made withgo is that You're never allowed to have asingle line block evaluated as the resultof an if statement. So you always have touse curly braces, even if you only have oneline to execute. Now, this style of this statementhas the Boolean test right here. And that'sthe only thing after the if clause. There'sactually another style that's very commonlyused in the go language. And that's what Iwould call the initializer syntax. So as yousee here on line 17, we've got a call intoa map that's pulling out the value from amap, and it's pulling out the okay variable.And then notice I've got this semi colon,and then the Boolean result listed here. Sothis part here, this first part of the ifstatement, is the initializer. So the initializerallows us to run a statement and generatesome information that's going to set us upto work within the F block. So in this case,I'm generating a Boolean result by interrogatingthe map. And then I'm using that as the testto see if the code inside the if block shouldexecute or not, I also have access to thepop variable that I'm creating right hereinside of the F test, and that variable isgoing to be scoped to the block. So if I executethis, you see that I get the population ofFlorida printed out, no problem there. IfI try and work with that variable outsideof here, and printed out again, I'm goingto get an undefined error, because the popof variables is only defined within the scopeof that if statement. The next thing thatI want to talk about with if statements arethe comparison operators that we have. I mean,obviously, as long as we can generate pureBoolean values, things are going to be prettysimple. But that's a pretty limited benefit,because in a lot of applications, we needto do some kind of a comparison in order todetermine if the branch should execute ornot. So let me drop in this example here.And let's just say that we're building somekind of a number guessing game. Now to keepthings as simple as possible. This is goingto be a pretty simple number guessing game,because we're going to hard code the valuethat we're going to guess. And we're goingto hard code our guests. So we're not goingto have a user interface involved here atall. And we're not going to randomly generatethe number that way, it's very clear whatthe application is doing. So right now whatwe have is we've got the number that we'regoing to try and guess we've got our guestshere on line nine. And then we've got a coupleof tests. So in line 10, we're checking tosee if the guess is less than the numbers.So this is the first comparison operator thatwe're looking at. This is the less than operator,we also have the greater than operator andthe equality operator. So if we run this withthe guests of 30, and a number of 50, we'regoing to get the value to low printed outbecause this first test evaluates to true.And the second and third test evaluate tofalse. If I change my guest to 70, and run,then the second test evaluates to true andthe first and third evaluate to false. Andas you might expect, if I put in a guess of50, and run this, then I finally got it. Sothis is the basics of a number guessing game,all you need to do is wrap this in loops,which we'll talk about in a future video andadd a little bit of a user interface and you'vegot your first game written in the go language.Now there's some other operators that we haveavailable. And it's a little hard to justifygetting them into this example. So I'm justgoing to go ahead and dump them here. Andthen we can take a look at them. So this firsttest is called the less than or equal operator.So it's going to check to see if number isless than or equal to guess the second isthe greater than or equal to operator. Andthe third is the not equal operator. So ifI go ahead and run this, you see that we areless than or equal to because we're exactlyon the number we are also greater than orequal to, and we fail the non equal t test.So we get true true false printed here, ifwe go with 30. Again, we're going to get falsetrue true. And if we go with 70, we're goingto get true false true. So these are the sixdifferent comparison operators that you'retypically going to use in your applications.And these work with all numeric types. Theydon't however, work with things like stringtypes. So if you're going to work with stringtypes, typically what you're going to workwith is the equality operator or the non equalityoperator. And that goes for any kind of referencetype as well. Now the next thing that I wantto do in this example is add in some kindof simple validation. So if you imagine thatwe've got some kind of a simple user interfacewhere the user is asked to enter a numberon the keyboard, and then we're going to usethat as our guests, then we're going to needsome way to validate that number to make surethat for example, they don't enter negativefive. So to do that, we can add another logicaltest. But we can also combine multiple teststogether using what are called logical operators.So let me drop this code in here. And youcan see our first logical operator. So thiscode here is conducting two tests, it's checkingto see if the guest is less than one. Or ifthe guest is greater than 100. And if it is,then we're going to print out a message here,we can also put a guard around our actualgame code, checking to see if the guess isgreater than or equal to one. And this isthe AND operator less than or equal to 100.So this first test case is going to executeif the guess is out of range. So if we enternegative five, for example, then we're goingto run this and we see that the guests mustbe within one and 100. If we enter a numberthat's within range, then everything executeslike we expect it to and this code on line11,000 execute. So this first operator hereis called the OR operator. And this is checkingto see if the test on the left is true, orthe test on the right is true. So obviously,we can't have the guest less than one andgreater than 100 at the same time, but oneor the other could be true. And if one ofthose is true, then we're going to have aninvalid guess. And it makes sense to printout a message to the user saying that thisother operator that we see here is calledthe AND operator. And it evaluates to trueif both the test on the left and the teston the right evaluate to true. So if we hada guest, for example of 105, then this codeis gonna evaluate to true because it is greaterthan or equal to one. But this code is notbecause 105 is not less than or equal to 100.So with an and test, both cases have to betrue, unlike an or test, we're only one ofthe two has to be true. So a guess of 105is going to print out our error message. Andit is not going to execute the code withinthis if statement here. The other logicaloperator that we have is called the NOT operator.And what that's going to do is it's goingto take a Boolean, and it's going to flipit to the other side. So if I take and justas a simple example, if I take and print thevalue true out, then we're going to get thevalue true printed out here at the end ofour program. If I put the NOT operator, whichis just an exclamation point, and run that,then the true becomes false. Similarly, ifI put this in front of a false, the falsebecomes true. And I can just prove that falseis really falseby taking that away, and false is false. Sothose are the three logical operators thatwe have. We've got the OR operator, whichis the double pipe that you see here on line10. We've got the and operation which is thedouble ampersand, which you see here on line13. And then we have the knot operation, whichreverses a Boolean result. And that's donesimply using the exclamation point. The otherinteresting thing to know about working withlogical operators, is a concept called shortcircuiting. So in order to show you that,I'm actually going to have to jump ahead andadd a custom function here. And we haven'ttalked about this. So let me just give youa simple of an explanation as possible. SoI've got a function down here between line27 and 30, called return true, that's gonnareturn a Boolean result. Now in order to makesure that we know that it executed, I'm printingout returning true here, and then I am returningthe true value. So what happens when we callthis function is it's just going to be replacedwith the Boolean true in our application code.Now, up here in line 10, I've changed ouror test to include this return true. And thisdoesn't make sense in our demo example. ButI'm doing this just to show you how this works.So in this case, we're ordering three teststogether. So what's going to happen is gois going to evaluate these guys, it's goingto generate a result. And it's going to takethat result, and it's going to order it againstthis guy. So basically, it's going to evaluateall of them. And only one of those has tobe true in order to generate a validationmessage. So as you might expect, if we runthis like this, we're going to get returningtrue because the guest is not less than one.So we're going to check this value, this returnstrue. And so we're going to evaluate if it'sgreater than 100. None of those are truthsor or statement fails. And then we're goingto drop down and we're going to execute thiscode down here. Like we might expect, if wegenerate an invalid value. So if we put anegative five, then well, let's just run itand we'll see what happens. So if we run this,we get the validation message. The guestsmust be within one and 100. Well, that's okay.But what happened to our return true? If youremember, this function is supposed to printreturning true out, and it didn't. So whathappened? Well, what happened is a conceptcalled short circuiting. So as soon as onepart of an or test returns true, then go doesn'tneed to execute any more code, it alreadyknows that the old test is going to pass.So it does what's called short circuiting,which basically means it's not going to evaluateany other part of the or test, it's just goingto move on and say, well, the or test passed,and therefore everything works. So in thiscase, since guess is less than one, there'sno reason for go to evaluate these two testshere. So since this test is a function call,it doesn't even execute that function. Now,if we go the other way, and we go out of rangehigh, then this is going to return false.This is actually going to return true becauseit's hard coded. And actually go isn't evengoing to evaluate this because this is goingto return true. However, this value is goingto fail this test here. So if we run this,we see that now we get returning true printedout. So this is a concept called short circuiting.Go is going to lazily evaluate the logicaltests that we put in here. So when we giveit negative five, since it doesn't need toevaluate anything after this first test, itdoesn't. So any function executions are notgoing to be evaluated. The same thing happensfor an and test. If one of the parametersreturns false, then we're going to get a shortcircuiting here. So if we get into a situationwhere this is false, go will not even evaluatethis test here. So for example, with the negativefive, when Google evaluates this, it seesthat the guest is not greater than or equalto one. So the ant test has to fail becauseboth sides of an ant test have to return truein order to work and so go is going to exitearly It's not going to execute this code.So for the next thing that I want to talkabout, I want to actually drop back into ourbaseline for our test here. And I want totalk a little bit about these two if tests.Now this code obviously works, but it's alittle bit ugly, right? Because we have thislogic here, guess is less than one or guessis greater than 100. And then we have thisexpression here. And this is exactly the opposite.So this is basically saying, Well, if it'snot less than one or greater than 100, thendo this other thing. So while this code isperfectly fine, it becomes a little bit ofa maintenance nightmare, because really, ourintent is that we want to execute either thiscode or this code. So the way we can do thata little bit more elegantly and go is by takingout all of this and just putting in the keywordelse. So what's going to happen in this situationis it's going to evaluate the logical tests.If these tests return a Boolean true, thenwe're going to print this. Otherwise, we'regoing to execute this block here. So in thiscase, if we run this, we see that we get thevalue too low. And our logical tests downhere in line 21. Execute. If we put in thevalue negative five, however, then we getthe same behavior that we had before. Butour code is a lot cleaner, and a lot moreclear as to our intent. Now related to thatis the situation where we have multipletests that we want to chain together. Andso let me paste in another permutation ofour little game. And in this case, I've actuallysplit out the validation logic. So I wantto check if it's less than one, I want toprint out one message, if it's greater than100, I want to print out another message.Otherwise, I've got a valid value. And todo that, I'm using this else if clause. Sobasically, what this is doing is it's takingan IF test, and then it's chaining on anotherif test if this fails. And so what goes isgoing to do is the first logical test thatpasses is going to be executed, otherwise,it's going to drop down to the else clause.So with the guests of 30, we would expectour else clause to fire. So if we run, wesee that in fact it does. If we put in a valueof negative five, then we would expect thisfirst statement to run and it does. And ifwe put in a value that's too high, then wesee that we get the second validation messageprinted out. And those are the three differentways that we have to create if statements.So we've got the simple if statement, that'sjust if and if that evaluates to true, thenthe statements inside of the curly bracesare going to execute. We've got if else. Sowe're either going to execute the first setof code or the second set of code. And thenwe have the if elsif, which we can chain togetherwith else in order to have very complicatedbranching, so that we can have multiple codepaths execute, depending on how our logicaltests execute. Now one thing to keep in mindis through these examples, we've never beenable to have more than one code path execute.So even if we did something like this, ifwe did elsif guess is less than one or thenotice both the first test and the secondtest will pass with a value of negative five,however, goes on and go to execute the firstbranch that has a succeeding test. So we getthe result, the guests must be greater thanone because this test passed. And so thistest wasn't even evaluated, go immediatelywent in and executed this branch of code andignored all of the rest of our if statements.Now another thing to keep in mind when you'reworking with equality operators, and thisisn't specific to if checks, but it is somethingthat's easy to demonstrate with an F check.So if I take a look at this example, here,I've got a floating point number of 0.1. AndI'm going to check to see if that number isequal to the number squared, and then I'mgoing to take the square root of it. So ifyou get out of pencil and paper, you're goingto take point one squared, which is goingto be point one, and then you're going totake the square root of that, which is goingto be back to point one, right? So this shouldevaluate to true. If we run this, we see thatin fact they are however, if I add in a coupleof decimal places here and run this, now gosays the results are different. Well, thefact is that if you square a number and takethe square root of it, it is the same number.So what's going on here? Well, the thing isgo is working with floating point numbersand floating point numbers are approximationsof decimal values. They're not exact representationsof decimal values. And so we have to be verycareful with them. When we're doing comparisonoperations with decimal values, this is generallynot a good idea, the better approach is togenerate some kind of an error value and thencheck to see if that error value is less thana certain threshold. So in this case, sincewe're taking a floating point number and comparingit to a floating point number, what we canactually do is divided two numbers and subtractone. And now what we have is a value that'sclose to zero. So if we take the absolutevalue of that, so we'll use the ABS function,which does that from the math package. Andthen check that to see if it's less than,for example, point 001. What this is doingis saying divide these two numbers, and ifthey're within a 10th of a percentage of eachother, then we're going to consider them tobe the same. So if we run this, then you seethat the results are the same and we We canadd as many decimal places as we want rightnow. And the errors that are associated withfloating point numbers aren't going to getin our way. Now, this isn't a perfect solution,because you could get two numbers that aren'ttruly the same, and get them to pass thistest. So the key to that is tuning this errorparameter here, making sure that it's sufficientlysmall to catch those cases, but sufficientlylarge, so that the errors introduced withfloating point operations don't affect yourresults. Okay, so the next thing that I wantto talk about are switch statements. So letme start that conversation by dropping inan example. And we'll see a basic switch statement.So a switch statement is a kind of specialpurpose if statement. So where if statementhas a logical test and runs a block of codeif that test is true, and then you can addan elsif to run a second test and anotherelsif. And as many else ifs as you want. Alot of times in application development, werun into a set of comparison operations thatare very similar to one another. So a lotof times it's more efficient to use a switchinstead of an if statement. So we have a simpleexample here, we're going to use this switchkeyword as you might expect. And in this example,we're going to start with this value here.And that's called a tag when we're workingwith switch statements. So this is what we'regoing to compare everything against, thenwe're going to have a series of cases. Andyou can see here we got case one, and we gotcase two. So what's goingto happen is the value here is going to becompared to the tag. And if it's true, thenthe statements after the case are going toexecute. If not, then they're not going toexecute. And just like with if statements,the first case that passes is going to bethe one that executes, we also have this defaultcase down here. And that's going to executeif none of our other test cases passed. Soif we go ahead and run this, we see that weget to print it out. If we add the value onehere, we get the value one printed out. Andif we have three, then we get the defaultcase printed out. So it's a very simple wayto compare one variable to multiple possiblevalues for that variable. Now in a lot oflanguages, if you want to compare againsta range, then you'd have to do something likethis, you'd have case two, you'd have casethree. And then we use what's called fallingthrough in order to compare multiple casestogether, well, we don't actually have fallingthrough as a default behavior and go, butwhat we do have to make up for that is theability to have multiple tests in a singlecase. So if I extend this example out here,and I say this is going to test one, five,and 10. And this is going to test two, four,and six, then I'm going to say this is goingto print out one, five, or 10. And this willprint two, four, or six, and then I put inthe value five, and let's just change thismessage to this is going to say another number.Okay, so if I run this, then I get the firstcase is going to pass because I matched thissecond test. So this is going to execute ifI have the value one, five, or 10. So I canalso get into that same case by passing inthe value of one, if I pass in the value offour, then like you might expect, I'm gonnahave the second case, evaluate. And naturally,if I pass in an unknown number, then I'm goingto have the default case execute, just likeI had before with a single test. Now one thingwe have to be aware of is the test cases dohave to be unique. So if I try and run this,where I'm going to have five in the firstcase, and in the second case, that's actuallya syntax error, because you can't have overlappingcases when you're using this syntax. Now,just like with if statements, we don't haveto have a simple tag here, in our switch,we can use an initializer, just like you'reseeing here. Now, in this case, the initializerdoesn't have to generate a Boolean result,because we're not doing a Boolean test inour switch statement up here, the Booleantest is comparing the second parameter inthe switch statement to our cases. So in thiscase, we're just going to initialize thatvalue. So I'm doing some simple math here,I have set equal to two plus three. So obviously,that's going to be five, then I've got thesemi colon and the tag that we're going tobe testing against. So as you might expect,when I run this, the first case executes becausetwo plus three is five, five is matched bythe first case. And so we get this statementprinted out. Now another syntax that we haveavailable to us in switch statements is atankless syntax. So both of the styles thatwe've been talking about, we've always hada value that we're comparing to our test cases,where there's actually another syntax, thistag list syntax. And this is arguably morepowerful than the tag syntax, although itis a little bit more verbose. So in this case,on line eight, I'm establishing a variablewhich is going to come from some other logicin my application. And then I've got the switchstatement that standing all alone and immediatelyopening a curly brace. So notice, I don'thave a tag here. However, my case statementsnow I've got full comparison operations. AndI can use the comparison operators, I canuse the logical operators. So in this case,the cases are standing in for the logicaltests that we have in our if statements, they'redoing exactly the same job. So in our firstcase, I'm checking if i is less than or equalto 10. And if it is, I'm going to print thatout. And then in our second case, I'm goingto check if i is less than or equal to 20.And then of course, we've got the defaultcase. If The value falls through. So if Irun this, I see that I do get the code executing,which is an interesting thing. And it executesthe first case. Now, why is this interesting?Well, if you notice the first case, in thesecond case overlap, because 10 is less thanor equal to 10. It's also less than or equalto 20. So unlike the tag syntax, we've gotmultiple test cases. And I said they cannotoverlap. When we're using this tag list syntax,they are allowed to overlap. And if they do,then the first case that evaluates to trueis going to execute. Now another thing thatI should have pointed out earlier is noticethat we don't have curly braces around this,any statements that after this case, and beforethe next case is going to be part of the blockthat's going to execute, so I can have asmany statements as I want here. The delimiters,in this case, are the actual case keywords,the default keyword, or the closing brace,so any of those delimit the end of a blockin a case statement. Now again, if you'recoming from another language, then this syntaxmight be looking a little weird, because youmight be looking for this word coming in here.And we'll have to add this word here. Andwe'll have to have this word here.So you might be wondering where the heck aremy break keywords, one go, the break keywordis actually implied, because so many use casesfor switches have breaks in there. And forgettinga break is the cause of innumerable errorswhen working with switch statements, the designdecision was made to have an implicit breakbetween our cases, instead of an implicitfall through which most other c based languageshave. So what happens if you do in fact wantyour case to fall through. So in this example,let's just say that we do want to print outthat we're less than or equal to 10, and thatwe're less than or equal to 20. And we knowthat if we pass this first case, we're goingto pass the second case to, in that case,we can actually use the keyword fall through.So if I run this, you see that both messagesprint out. So I'm going to get less than orequal to 10 and less than or equal to 20.Now one thing to keep in mind here is fallthrough is logic less. So when we say fallthrough to the next case, it will do that.So if I, for example, change this to be greaterthan or equal to, then we know that I failsthe second case, but if I run, it still executes,because fall through means that you are takingthe responsibility with the control flow.And you intentionally want the statementsin the next case to execute. Now and go youdon't use follow through very often, becausewe have that tag syntax that can take multipletests per case. And so a lot of the use casesfor falling through are taking care of that.However, if you do have a use case where youneed follow through, then it is availableto you. The next thing that I want to talkabout was switch statements are a specialuse of a switch statement called a type switch.So I'm going to drop in this example here.And this should be pretty familiar exceptfor what we're switching on. So notice thatwe are switching on a tag. But the tag variableis typed to type interface, which can takeany type of data that we have in a go application.So the interface type can be assigned to aninteger like we're seeing here, we can assignit to a string to a Boolean, to a functionto a struct to a collection, anything we wantto assign to an interface is going to be permissible.So then in go, we can use this syntax here.So we can put in the dot operator. And afterthat, put in params with the word type inthe middle. And what that's going to do istell go to pull the actual underlying typeof that interface and use that for whateverwe're doing next. Now, you don't just usethat for type switching. But this is a commonuse case for it. So in this case, I'm goingto assign the variable i the value one, whichas we know is going to type I to be an integer.And then I can actually use go types as thecases, because this is going to return a type,then I can use those type keywords here asmy cases. So if I run this, knowing that oneis an integer, I should not be surprised whenI see the first case passes, and the othertwo are ignored. If I had a decimal pointzero here, then that's going to turn thisinto a floating point 64. And we see thatgo recognizes that. Similarly, if I put onein a string here, then we're going to seethat go understand that. And I can even make,for example, an array. And we don't need toinitialize any values here. But if we seethat, then go recognizes that as another type.And if I want to have a case to catch that,then I can actually have a case for a raiseof three integers. And then I can print thatout. Now if I run, we see that it recognizethat and this is different than two. So ifI use a two element array, please be awarethat those are different types. Arrays arenot equivalent to every other array, you haveto have the same data type and the same arraysize in order for them to be evaluated asthe same type. Okay, the last thing that Iwant to talk about I can illustrate with thisexample here. Now in this example we're extendingon the previous one was type switches. Andnotice that I've got these two print statementshere on line 11 and line 12. So if I run this,I see that I is an integer and this will printto it. In some situations, you might needto leave a case early. So maybe inside ofthe case, you're executing some kind of alogical test, and you find out that thereis some logic in the case that you don't wantto execute in certain situations. Well, ifyou want to do that, you can actually exitout of the switch statement early by usingthe break keyword. So if I add that and run,then I have a way to break out of the switchearly. And I'm not going to execute any codebelow that break statement. So you can wrapthis in a logical test to determine if you'vegot a validation error. For example, on someincoming data, you might not want to savethat data to the database. And then you canuse this break keyword in order to short circuitthat save operation. Okay, so that prettymuch wraps up what I have to talk about withif and switch statements. Let's go into asummary and review what we've talked about.In this video, we started our discussion aboutthe different control flow tools that we haveavailableto us in our NGO applications. And we startedout by talking about the very basic if statementsand how we can use a boolean variable to determineif a block of statements will execute or not.We then talked about the initializer syntax,and how that allows us to execute a statement,that's going to set up variables that we canuse to run our test against as well as usewithin the statements of our if tests. Wethen talked about the comparison operators,and we talked about how there are six of them,we've got less than, greater than, less thanor equal to and greater than or equal to,to work with primarily the numeric types,as well as the double equals operator andthe non equals operator that are availableto be used more generally, we talked aboutthe logical operators, the AND operator, theOR operator and the non operator that allowsto chain together Boolean operations, as wellas reverse the result of a Boolean operation.In the case of that knot operator, we talkedabout short circuiting and how when you'rechaining together tests with an OR operation,for example, if any one of those logical testsevaluates to true, then the remaining testsare not executed. So any side effects or anyfunctional operations that might occur withthose are not going to be executed. Similarly,with an an test, the first part of an ad testto return a false result is going to haltthe execution of that and test. So any sideeffects you might be relying on afterwardare not going to occur. We then talked aboutthe two different variants of if tests, wetalked about the FL statement, and how wecan use that to execute one of two branchesof code. So if the if test succeeds, thenwe're going to execute the first block ofstatements. Otherwise, we're going to executethe second block of statements. Related tothat is the if else if statement. And in thiscase, instead of determining between one oftwo paths, and forcing your code to executeone of those, you can add additional logicaltests. So at most one block is going to execute.But if all of your F tests fail, then youmight not get any blocks executing. And finally,I took a minute to talk about equality operations,especially when working with floating pointnumbers. And how equality and floats don'treally get along that well. So if you do needto do some kind of inequality operation withfloating point numbers, then you're goingto want to turn that into some kind of a comparisonoperation against an error function. So forexample, we were checking to see if two floatswere equal, instead of checking to see ifthey're actually equal, and we divided themsubtracted one from that result, and checkthat against an error parameter. So we decided,if they were within a 10th of a percent, theywere going to be considered the same, youcan use a similar type of error function withyour operations. But in general, you shouldnot test to see if two floating point numbersare equivalent to one another. We then movedon to talk about switch statements, and thevarious ways that we can use those in ourapplications. We started by talking abouthow to switch on a tag or tag is just anothername for a variable. But that variable hasa special significance in a switch statement,because all of your test cases are going tobe compared back to that tag. We talked abouthow with go we can have multiple tests withour case statements. And by doing that, wecan actually eliminate a lot of situationsin other languages where we need to fall throughand chain multiple cases together. Well ingo, we can just add those multiple tests inthe same case, and eliminate the need to dothat following through. We also do have initializersavailable with switch statements. But insteadof the initializer, generally generating aBoolean as we do with an if statement, theinitializer is going to generate the tag that'sgoing to be used for the comparisons. In ourcases, we talked about switches with no tagsand how it adds a lot of flexibility to theswitch statement. But it is a little bit moreverbose syntax. So instead of the cases beingcompared for equivalency with the tag, we'regoing to actually put in the comparison operationourselves. So we can use more than just equivalencytesting we can check less than or equal to,or any other comparison function, includingchaining comparisons together by using logicaloperators. We talked about how we have thefall through keyword available in switches,and how that replaces the break keyword inmany languages. So where many languages haveimplicit fall through an explicit breakingin go we flip it around so we have implicitbreaks. But if you do need one case to fallthrough to the next Then you can use thisfall through keyword. Now one thing to keepin mind when you're doing that is when you'refalling through, any additional case logicis not executed, the fall through will overrideany case logic that you have. And so the nextstatement will execute regardless of if thatcase would normally pass or not. We also talkedabout type switches, and how we can use thatspecial syntax by adding a dot perenne typeperenne on an empty interface in order toget the underlying type of that empty interface.So for example, if we've got an empty interfacethat's holding an integer, then we can usethat syntax in order to get that underlyinginteger value. Then in our switches, we canswitch against those types. And then we canmake a decision based on the data that wegot in. And finally, we talked about breakingout early, when we've passed into a case inour switch statement. There are times when,in the middle of the case, we need to exitout early, because we've done some additionalanalysis. And we've decided that we shouldnot proceed executing this case. So for example,we've got a switch statement. And inside ofthat case, we're doing some validation onthe data before we save it to the database,we might decide that data is invalid, andwe should not proceed to save it to the database.So in that case, you can use the break keywordbreak out of the case and continue executionafter the switch statement, I want to continueour discussion of the control flow structuresthat we have available in the go languageby talking about looping. Now in the lastvideo, we talked about the two ways that wehave to introduce branching logic into ourapplication if statements and switch statements.Well, when we talk about looping and go, thingsare actually going to be a little bit simpler,because we only have one type of statementthat we're going to have to learn. And thatstatement is the for statement. Now we'rebasically going to break this conversationdown into three different parts, we're goingto talk about simple loops, we're going totalk about how we can exit a loop early. Andthen we're going to talk about how we canloop through collections. Okay, so let's goahead and get started by learning how to dosome simple loops in go.So in order to start our discussion aboutlooping and go, I want to start with thisperhaps the most basic for loop that we cancreate. So we're going to start with the forkeyword as you might expect. And then we'regoing to follow the for keyword with threestatements, the first statement is going tobe an initializer. Now this is the same asan initializer. And if statements and switchstatements. So we can add any kind of statementthat we want here. And normally, we're goingto use that in order to set up a counter,when we're using a for loop like this, thenext statement that we have here is goingto be some kind of a statement that generatesa Boolean result. And that's going to be usedby the for loop to determine if it's donelooping or not. And then the last statementis going to be the incrementer. And normally,we're going to use that in a situation likethis to increment a counter variable. So ifwe run this, we see that we get the numberszero through four printed out, we could alsoincrement i by two. And there, you see thatwe only get three iterations of the loop,because I has the value zero, then it hasthe value two, and then it has the value four,when it comes to the next time it has thevalue of six, well, six is not less than five.Therefore the comparison operation resultsto false and we exit out of the loop. So thisis certainly possible to do and you will occasionallysee this. But the most standard way that yousee these loops created is like this usingan increment statement. Now if you're comingfrom another language, you might be expectingto be able to do something like this. So maybewe want to variables, increment in our forloop. And so we're going to initialize j there.And then we're going to increment j with eachloop. So if we go ahead and try and run this,we see that that's actually not allowed, becausewe have no comma operator in the go language.So you can't separate two statements likethis using the comma. So this is an error,and this is an error. So if we want to dothat, though, we can use gos ability to initializemultiple values at the same time by addinga comma here getting rid of this part here,adding the comma there. And then we can'ttreat these as individual statements, becauseyou can only have one statement here. Andin go the increment operation is a statement,it's not an expression. So we need to figureout a way to do both of these at one time.So we will simply come in and said I n j equalto i plus one and j plus one. And go aheadand run that. And now we have I printing outif we have j here we see that j printout aswell, we can actually increment j by two here.And we see that i and j are incrementing independently.And we have no problems here. Now just tocome back and revisit that increment commentthat I made earlier, you might be temptedto do something like this, if you just wantto increment them by one. If we run that,we're actually going to get an error becauseagain, in go unlike a lot of languages, theincrement operation is not an expression.So we can't use that as part of a statement.It is a statement on its own. So when you'rejust incrementing one variable, you can certainlydo this because i plus plus is a statement,but we can't use that in combination withanything else. We can't use it as an expression.Something else that's interesting. Let mego back to our initial example here. So wecan play around with this a little bit. Soat first blush, if you just come into programming,this AI variable might seem like it's somethingspecial because it's used up in the for loop.And the fact is, it's a variable just likeanything else. So we can do whatever we wantwith it. As a matter of fact, just to showyou that, let me drop in this cool littlebit of code. As you can see, what we're goingto do here is we're going to take the modulusof i n two, and we're going to see if thatzero, so basically, we're checking to seeif it has an even number or an odd number.If I is an odd number, then we're going todivide it by two. If I is an even number,then we're going to multiply it by two andadd one to it, it's just a couple things toplay around with the incrementer. So let'srun this and see if this actually goes tocompletion. And in fact, it does. And yousee that the variables going all over theplace. So we start with zero zeros on so dividethat by two, which stays zero, the next iterationis going to be one because we're incrementingby one, so it's actually going to be multipliedby two and one added to it. So we print theone here. But then by the time we're donehere, two times one plus one is three, andso is three, that gets incremented to four,so we're printing for four out here, fouris even to divide that by two, two gets incrementedagain, so we print out the value three, threeis odd. So three times two is six plus oneis seven, seven is greater than five. Andso we leave the comparison operation, so wecan do whatever we want with the counter.Now, just because you can play with the counterdoesn't mean it's a good idea. As a matterof fact, normally, it's very bad practiceto manipulate the counter within the for loop.So maybe the best reason to show you thatyou can do this is to make sure that you avoiddoing this because there can be times whenyou're inadvertently changing the countervariable within a loop. And your loops mightstart to act really strange, like we're seeingin this example here. Okay, so let's go aheadand restore our original example, make surethat that's running again, and it is we'reprinting out the values zero through fouragain. Now the next thing that I want to showyou is that we don't actually need all threestatements here. So one thing that we coulddo, let's just say that I is initialized somewhereelse in our application, so we don't needit to be set up here. And we can run withthis first statement being empty. Now youcan't leave this for a semi colon out, becauseif you leave that out, then everything's inthe wrong place. And it thinks that this isgoing to be the initializer. And this is goingto be the comparison operator. So if we tryand run this way, we're going to get an error.But if we leave the semicolon in, then gorecognizes that we simply don't have an initializerin this for loop. And it executes everythingthe way that we expect it to. And of courseI've variable ri is taken in just like wewould expect. Now the difference between thisformat and the previous one is in this i isscoped to the main function. But when we havethis syntax here, I is scoped to the for loop.So if I tried to print the value of i outhere, we're not going to get a valid valuebecause I is not defined. But in this otherform, when we're putting it out here, thenI is scoped to the main function. And we areactually going to get the final value of Iprinted out which is the value five, becausefive is the first value that fails this test.So this is actually coming from this statementright here. Okay, the next thing that we cando, let me go ahead and wipe this out, wealso don't need the incrementer value, sowe can eliminate that. Now if I run this right,now, we're actually going to get an error,because what this is going to do is it's goingto generate an infinite loop. And in the goplayground, they don't let us do infiniteloops. And so after it runs for a little bitof time, it's going to shut the process downon us. If we were actually running this ina local environment, all you'd see is thevalue zero printed out over and over and overand over again, until eventually you got boredand shut the application down. Now we canof course, since I is a variable just likeany other, we can put the incrementer in here,and everything works just fine. So once again,we have to remember to put the semicolon here,if we remove the semi colon, then go doesn'tknow what the heck we're asking it to do,because this is invalid syntax. So if youhave the first semicolon, you need the secondsemi colon. Now this case does happen actuallyquite a bit where we need a for loop and justa comparison operation. And this is how godoes do while loops. So in a lot of languages,you have two more keywords, you've got thedo keyword and the wild keyword. And normallythose work with simply a logical test. Andthen you have some other increment operations.So you either have an increment or insideof the loop, or you're pulling the next valueoff the stack. And comparing that on eachiteration, or something like that, we'll gohas the same problems to solve, but didn'tmake sense for the designers of the languageto introduce a new keyword just for what isbasically a special case of a for loop. Sowhat they allow us to do is we of course canuse this double semi colon syntax here. Butyou can also leave them both off. So if Irun this way, we see that this works. Andthis is going to work exactly the same wayas if we have both of these semicolons here.So this is a little bit of syntactic sugar.It's exactly the same construct. It's justyou don't need to have the additional semi-colonsand it reads a little bit cleaner. So in thiscase, our for loop is just doing a comparisonoperation. Go is going to assume that we'retaking care of where those variables for thecomparison are coming from somewhere else.Now the third form of a for loop that we haveavailable when we're working with counterslike this is the infinite for loop. Becausewe also have a situation with do while loopsin other languages, where we need the applicationto run through the loop and undetermined numberof times. And by undetermined means, we don'tactually know when to stop by an obvious logicaltest, we need some complex logic within theloop in order to determine when to leave.So in go, the way that we do that is we simplyleave off the logical test, and we run likethis. So if I run this, again, as you mightexpect, the playground is going to crash outon us because there's no way for the programto exit. And so it's just going to run forever.So we need some way to tell our infinite loopwhen it's done processing and when to leave.And the way we're going to do that is usingthe break keyword. And then Normally, theway that you do this is you'll put some kindof logical tests inside of the loop. So wecan say, if I equals five, then we're goingto use that break keyword. And we saw thebreak keyword, the first time when we weretalking about switch statements, or the breakkeyword is actually more commonly used insideof for loops, especially this kind of infinitefor loop here. So what we're doing here isif i is equal to five, so we're going to printout the values, zero through four, then whenI was fine, we're gonna break out. So if wego ahead and run this, we get exactly thesame values that we had before. Now when wedo this, we actually leave the entire forloop. So execution of the loop stops, andwe're done processing. Now another thing thatwe can do is use what's called a continuestatement. So let me drop in an example thatshows you that. So in this case, we're loopingfrom zero to nine, because we're going tokeep incrementing as long as i is less than10. And then we're checking to see if thevalue is even if it is even, then we're goingto use this continuous statement here. Andwhat this does is it basically says, exitthis iteration of the loop and start backover. So what's going to happen when we have00 modulates to is zero, so we're going tocontinue, which means we're not going to hitthis print statement, when we have one, oneminus two is one, so we're not going to hitthis continue, and we're going to print outone, and then two is going to hit the continueand three is not. So what we should see iswe'll only print the odd numbers out. If werun this, we see that that is in fact whathappens. So continuous statements aren't usedvery often. But they can be very, very useful.If you're looping through a large set of numbers,and you need to determine within the loopwhether you want to process a record or not.Now, to show you the next thing, I'm actuallygoing to do this, this is actually going toprint out a nested loop. So what we're doinghere is a pretty simple example, we're startingwith the variable, I initializing it to one,and we're going to run as long as i is lessthan or equal to three, and then we're doingthe same thing with J on the inside. So basicallyis going to be one, then it's going to loopthrough days from one to three, then is goingto be two, it's going to loop through j 4123.Again, and then inside this inner loop, we'rejust multiplying the two numbers together.So if we run this, we see that we get allof the permutations of i times j, where iand j go from the values 123. So we get allof these values printed out, and everythingworks the way that we expect. Now what happensif we want to leave the loop, as soon as weget the first value that's greater than three.So what we want to do is something like this,so we're going to multiply it times j, we'regoing to repeat our logic here. And we'regoing to see if that's greater than or equalto three, if it is, then we're going to breakout of the loop. Now if I run this, we don'texactly get the expected result. And the reasonfor that is as soon as i times j is greaterthan three, it breaks out of the loop, butthe loop that is going to break out of isthe closest loop that it can find. So it'sactually breaking out of this loop here. Andthis loop just restarts because there's nothingto tell it to stop. So you might ask the question,Well, how do we break out in the outer loop?Do we have to have more logic here and checkto see if i times j is greater than threeagain, and then break out? Well, the answeris, of course, now, we do have a concept calleda label that we can add. So a label is puttogether something like this, we're goingto start with a word and follow it with acolon. And we're going to left justify thatin our code blocks. So once I have a labeldefined, I can actually add the label afterthe break keyword. And it basically describeswhere we want to break out to. So since thislabels just before this for loop, we're goingto break out of the outer for loop. So ifI run now, I get to the value three, threeis greater than or equal to three. And soI break out of both the inner and the outerloop. The last thing that I want to talk aboutin this video is how we can work with collectionswith for loops. So we've seen this beforewe have a slice of integers. In this case,we're containing the values one, two, andthree. And we've seen how we can work withthis as a slice so we can print this out.We run this we get no big surprise, we getthe slice printed out with the values 123.But what happens if I wanted to work witheach individual value? Well, we've seen howwe can do this, we can pull out one valuefrom the slice, and we get the value two.But how do we work with an arbitrarily sizedslice. So I don't know how big this sliceis going to be at runtime. I want to be ableto loop through all the items within the slice.So the way that I'm going to loop througha collection is using a special format Thefor loop called a four range loop. So I'mgoing to start that with the four keywordas you might expect. And then what I'm goingto get is both the key and the value for thecurrent item in the collection. So I'm goingto have two variables that I can set there.And then I'm going to set that equal to thisspecial range keyword and then provide thecollection that I'm going to range over. Sowhat this is going to do is it's going tolook at the collection here. And it's goingto take each value one at a time, give usthe key and the value, and then we're goingto be able to work with those values independently.So inside of this, let's go ahead and justprint out the value of the key and the valueand run that. And you see that we get theindexes for the items in the slice, and weget their values. So the indexes are comingfirst, those are the keys. And then the valuesare the actual values in the slice. And thisis the only syntax, you have to remember whenusing the range keyword, you're always goingto get this key comma value result comingout. So this syntax is going to work for slicesand for arrays. So if we make this an arrayby just adding a size, here, we see that weget the exact same output, we can loop overmaps. So if I pull back the state populationsmap that we use for a couple of videos now,and loop over that, once again, pulling upthe key and the value and ranging over themap this time, then if I print the key andthe value, I'm going to be able to split thatout and then get the state in their populationprinted out separately. And I can even usea string as a source for a four range loop.Because what we're going to be able to dohere is pull each letter out of this stringas a character and look at that. And we seethat we do get each position. And now we getthis numeric value, which if you've been payingattention over the last few videos, you'regoing to remember that this is actually theUnicode representation for that digit. Sowe can cast it back to a string by just usinga simple conversion operation. And we seethat we get each letter printed back out aswe would expect. The other type of data thatwe can arrange over in go is what's calleda channel. Now channels are used for concurrentprogramming and go and that's a topic fora future video. So I'm going to leave thattopic right now, I promise when we talk aboutparallelism and concurrency and go, I'll revisitthe for loop and show you how you can usefor loops with channels, because there aresome special behaviors that you have to beaware of when you're ranging over channels.Now, if I come back to this example, and printthis out, you see that we have access to boththe key and the value every time. Now, thisis not always true, you don't always needaccess to the value. And this can lead tosome problems. Because if I only want to printthe keys out and run this, this is actuallyan error, because in go, you have to use everyvariable that's declared. And so we got thissituation where we've got this variable v,but we're not using it. And so that's goingto cause us a problem with our application.Now and go what you can do, if you only wantto get the keys out, you can actually skippedthe comma value. And when you run that, it'sgoing to ignore the value. But what happensif you get into a situation where you onlywant to print the value out. So if I restorethis, and I want to want to print the value,of course, I get the same error. But I can'tjust say this, because go who's going to assignthe keys to that. So what do I do? Well, inthis situation, when you only want the values,then you can use that underscore operatorthat write only variable that we've seen acouple of times. And basically, that's goingto give a hint to the compiler that we don'tactually care about the key. But we need thisin this first position in order to get thevalue. So when we run this, we do see thatwe get those populations printed out. Okay,so that covers what I want to talk about withfour statements and go, let's go into summaryand review what we've talked about. In thisvideo, we talked about the second major controlflow construct that we have in the go language,the looping construct, and we talked abouthow we're going to use the for statement forall of the loops that we need to do and go.So we don't have to remember I do keywordand while keyword and a for keyword, whenwe use the for keyword that's going to allowus to loop over every collection that we have.We started by talking about simple loops andhow there are three basic forms for the simpleloop, we've got this first syntax, which isthe most basic, that's the initializer testincrementer syntax, and we're going to usethat initializerto set us up in our loop. Normally, by initializinga counter variable, then we're going to followthat with our test, our test is normally goingto look at the incrementer to see if it'sat a certain value that's out of range forour for loop. And then we've got an incrementerand that incrementer. His job is to move theincrementer along at every iteration of theloop in order to move to the next case thatwe want to loop through. Now we also learnedin this first syntax, we actually can leavethe first and last statements on that out.But if we're going to use any of this syntax,we have to leave the semi colons in place.So leaving a semi colon out confuses the compiler,it starts to assign things to the wrong places.And so you're going to get an error in yourapplication. So if you need an initializeror an incrementer, then you're going to haveto use this full syntax. Now the second syntaxeliminates the need for the initializing theincrementer. And it assumes that the testconditions are being managed somewhere else.So in this construct, you only have the fortest. And as soon as that test is false, you'regoing to exit the loop. The last constructthat we have is the for loop on its own andthat will run indefinitely. Until somewherewithin your four loop, the break statementis called. That leads us to how we can exitearly from a for loop. And we have three conceptsthat we talked about with that, we've gotthe break keyword that will break out of theimmediate loop that's executing and continuethe application execution after that loop.The continue statement is similar to the breakstatement, except for eight doesn't breakout of the loop, it just breaks out of thecurrent iteration goes back to the incrementer,and continues execution of the for loop atthe next increment. Now we can also use labelsin our application and combine those withbreak statements in order to break out ofan inner loop. And that's typically how they'reused in go. So for example, if you have anested loop where you're iterating over twocollections, and you need to break out ofthe outer loop, then you need to set up alabel and then follow the break keyword withthe name of that label so that Google knowswhere to break out to. And finally, we talkedabout how to loop over collections, and howthe collections that we have available, there'squite a few we've got arrays, slices, maps,strings and panels that we can loop over.But they all have this similar syntax, we'regoing to use that for keyword, we're goingto get a key and a value. And then we're goingto set that equal to the range keyword followedby the collection that we're looping over.So the keys when working with arrays, slicesand strings are going to be the index withinthat collection. So you're going to have thatzero based index. With maps, we're going toget the key from that map. And the valuesare what you expect them to be. They're thevalue for that current index. Now when we'retalking about channels, it's going to followvery similar syntax, but channels have a littlebit of a special interpretation for these.And we'll talk about those in a future module,I'd like to finish our discussion of controlflow constructs that we haven't go by talkingabout defer panic and recovery. We'll startthat discussion by talking about deferredfunctions, and how we can actually invokea function, but delay its execution to somefuture point in time. Then we'll talk abouthow an application can panic. So in this conversation,we'll talk about how go application can entera state where it can no longer continue torun and how the go runtime can trigger thatas well as how we can trigger that on ourown. And then related to panicking, we'lltalk about recovery. Now when your applicationstarts to panic, ideally, you'd have someway to save the program so that it doesn'tbail out completely. So we'll talk about ifyour application can be saved, how you canuse the recover function in order to signalthat to the rest of your application. Okay,so let's go ahead and get started. In a normalgo, application control flows from the topto the bottom of any function that we call.Now, of course, we can alter that a littlebit by introducing branching logic so thatwe can skip some statements. And we can uselooping that we talked about in the last videoto repeat certain blocks of statements multipletimes. But generally, a function is goingto start at the first line and execute throughuntil it gets to the last one. So if we runthis program right here, we see that it'sno big surprise that we print start, thenwe print middle, and then we print end. Andwhat we can do if we want to defer the executionone of these statements is proceeded withthe defer keyword. So if I put that in frontof this print line here, then run that, younotice that middle is actually printed afterend. So the way the defer keyword works ingo, is that it actually executes any functionsthat are passed into it after the functionfinishes its final statement, but before itactually returns. So the way this main functionis executing is its calling line eight andit's printing out start, then it recognizesthat it has a deferred function to call andthen it prints out end. And then the mainfunction exits now when go recognizes thatthat function exits, it looks to see if thereare any deferred functions to call. And sincewe have one, and then goes ahead and callsthat, so deferring doesn't move it to theend of the main function, and actually movesit after the main function. But before themain function returned. Now, if we put thedeferred keyword in front of all of thesestatements, then we'll actually see an interestingbehavior. Because the deferred functions areactually executed in what's called lifepoorder or last in first out. So the last functionthat gets deferred is actually going to bethe first one that gets called. And this makessense, because often we're going to use thedeferred keyword to close out resources. Andit makes sense that we close resources outin the opposite order that we open them, becauseone resource might actually be dependent onanother one. So if we run this like this,we actually see that we've reversed the orderof printing this. So we're now printing andmiddle and start. And just to remind you,these aren't executing in the context of themain function they're executing after themain function is done, but before it returnsany results to the calling function. Now,these are good theoretical examples. But inthis situation, I felt it makes sense to geta little bit more practical example for youto look at. So for this, we're actually goingto need to go into Visual Studio code, becausewe're going to need to run a program that'sgoing to make some resource requests throughthe HTTP package. And you can't do that ina playground. So if we look at this program,here, we're importing a couple of packages.Then we're using the get function from theHTTP package in order to request the robotsdot txt file from google.com. Now this exampleyou can actually find in gos documentation.I basically stole this as an example for howyou can use the deferred function. Now, asyou can see, with this request, we're goingto get a response and an optional error. Andwe're going to check to see if that erroris nil. And if it's not, then we're goingto log that out and exit our application.If it is not nailed, and we got a good response.So then we're going to use the read all functionfrom the IO util package, what that'll dois that'll take in a stream, and that'll parsethat out to a string of bytes for you to workwith. So if we look at the robots variable,here, we see that that is a string of bytes.And then we close the body of the responseto let the web request know that we're doneworking with it, so it can free up those resources.Of course, the read operation can fail, sowe're checking for that error. And then we'refinally going to print out the value of therobots variable here. So if we run this application,by invoking it with the go run command, wesee in fact that we do get the robots printedout, and everything worked out just fine.So we got this entire response printed outto the console here. Now, one thing that we'redoing here, and that the defer keyword canhelp with is handling this body close. Now,in this application, we only have one statementthat's really being worked with between therequest being made, and the body being closed.However, in many applications, you might havequite a bit of logic that's involved, thatneeds that body to be open and continue towork with it, maybe you're reading this streamone character at a time, and you're doingsome pattern matching or something like that.So you can actually end up with quite a fewstatements in here. So what can happen hereis that you might make this request and openup this resource up here. And then it mightbe dozens of lines later that you actuallyget around to closing in. Well, that introducesthe possibility that you forget to close it.Now, it's going to be hard to find that it'sgoing to be hard to remember to close theresource. And so you're introducing the possibilityof bugs coming into your application. So whatwe can do is add the defer keyword here. Andthen we can go ahead and move this up a line.So if we run this, then it looks like we'reclosing the resource, and then we're tryingto read it. But if we run the application,we see that it works just fine. Everythingcomes out the same way it did before. Nowif we leave the defer keyword off,then we do get an error because we closedthe response body before we were done readingit. So what this allows you to do, and thisis the most common use case that I've seenfor using defer is it allows you to associatethe opening of a resource and the closingof the resource right next to each other.So you see this pattern a lot where we'regoing to open resource, we're going to checkfor an error. And then we're going to closethe resource. And you want to check for theerror first. Because if there's error generatedhere, then you actually never got this resource.And so trying to close, it will cause yourapplication to fail. But as long as you knowthat you have the resource there, then itmakes sense to do a deferred call to closeit. Now, one thing I would warn you aboutis this is a pretty common pattern. And you'regoing to see this all the time. But if you'remaking a lot of requests and opening a lotof resources within a loop, then using thedefer keyword to close, it might not be yourbest choice. Because remember, the deferredstatements don't execute until the functionitself executes. So if you've got a loop that'sgoing to loop over a million resources, you'regonna have a million resources open beforeall of those get closed at the same time whenthe function finally executes. So if you'reworking with resources in the loop, then youmight not actually want to use the defer keyword,you might want to explicitly close those resourceswhen you're done with them. Or another optionwould be to delegate the processing of thoseresources out to another function and havethat function, close the resource. That way,you're not keeping a bunch of resources openat one time and wasting memory. Now the nextthing that I want to show you we can do backin the playground. So I want to do that, becauseI think that's the most accessible environmentthat we have is this program here. So whenI run this program here, what do you thinkis going to print out when I run this, nowthere's two lines of thought that you mighthave, the first line of thought is, well,I'm defining a is the string start. And soI'm going to print start out. But you mightalso realize the defer statement is goingto cause this statement to print after themain function is done. And before main isdone, we've already changed the value to end.So you could make an argument that this isgoing to bring start or end with the factis that we're going to get the values startprinted out. And the reason for that is whenyou defer a function like this, it actuallytakes the argument at the time, the deferis called not at the time the called functionis executed. So even though the value of ais changed, before we leave the main function,we are going to actually eagerly evaluatethis variable. And so the value start is goingto be put in here, we're not going to payattention to this value as it changes. Nowthe next thing that I want to talk to youabout in this video, is what's called panicking.Now in go, we don't have exceptions, likea lot of applications have, because a lotof cases that are considered exceptional anda lot of languages are considered normal ina go application. For example, if you tryand open a file, and that file doesn't exist,that's actually a pretty normal response.It is reasonable to assume that you mighttry and open a file that doesn't exist. Andso we return error values. We don't throwexceptions because that's not considered exceptionaland go. However, there are some things thatget a go application into a situation whereit cannot continue and that is consideredexceptional. But instead of using the wordexception, which you has a lot of connotationsbecause of its use and other languages, we'regoing to use another word and that word isgoing to be panic. And that's because ourapplication can't continue to function. Andso it's really starting to panic because itcan't figure out what to do. So if we takethis example, here, I'm declaring two variablesa and b, and setting a equals one and B equalto zero. Now, obviously, the answer of onedivided by zero is invalid, we can't do thatin a go application. So if I run this, we'reactually going to see that the runtime itselfwill generate a panic for us. And the runtimeerrors printed out integer divide by zero.And then we get a stack trace letting us knowwhere that error occurred. Now, if you'regoing along and writing your own program,and you get to a situation where your programcannot continue to execute, because of a certainstate that gets thrown, then it makes sensefor you to panic as well. So to do that, you'regoing to use the built in panic function likewe see here. So we're printing out start,then we're using the built in panic functionpassing in a string, and then we're goingto print out. And what's going to happen whenwe run this is very similar behavior to whenwe had that divide by zero error a few secondsago, but the error message that's printedout is actually going to be the string thatwe passed into the panic function. And thennotice we get the same stack trace out, wedo get start printing out. But of course,we don't get the string end printed out.Now more practical example. Now this willnot run in the playground. But that's okay,because it probably wouldn't fail on the playgroundanyway, is this one here. So this is a verysimple web application where you're goingto use the handle function. So we're registeringa function listener, that's going to listenon every URL in our application. And thenthis is a callback that gets called everytime a URL comes in that matches this route.And all this is going to do is print out thestring hello, go. As a matter of fact, whydon't I go ahead and do this over in VisualStudio code, that way, you can see what thisprogram is going to do. So we'll go aheadand save this and we'll get it up and running.Now there's no errors and went ahead and started,okay. So if I come in, and hit that URL, whichis localhost 8080, that you see that we getthe string hello, go printed out. There yougo an entire web development course in onevideo. So we've got a very basic web handlerthat's printing out the string, hello, go.And that's coming from this line right here,where we're writing to this response writer,this response writer is basically giving usaccess to the response to this web request.And we're printing out the string hello, go.Now, what's interesting here is this errorhandler right here. So the listening servefunction returns an optional error object.So when might that happen? Well, one situationthat can happen all the time, when you'refiring up a web server is the port can beblocked. So if I open up another terminalhere, and go ahead and try and run this, again,you see that we get our application panicking.And the reason for that is we're trying toaccess a TCP port that's already been blocked.And it's been blocked by the fact that we'rerunning the application over here. So thisis a situation that happens all the time.Now, the listening serve function doesn'thave an opinion on if that's a panicking situationor not, because he just tried to execute something.And it's going to return an error value saying,Well, that didn't work, it's reasonable toassume that listening serve can fail. Andso it's not going to panic is going to returnan error. Now, we're the ones writing theweb application. And we know that if thatdoesn't work, the entire application getsbrought down, and nothing happens. So in thatsituation, we decide that we're going to panicpassing out that error that comes from thelistening serve method, letting whoever'strying to start this up, know that somethingvery bad just happen. And so this is a commonpattern that you're going to see and go gois rarely going to set an opinion about whetheran error is something that should be panickedover or not. all it's going to do is tellyou, hey, this didn't work the way that youexpected it to work. It's up to you, as adeveloper to decide whether that's a problemor not. So in this case, we say, yeah, thatis a problem, we fail to start our application.And so we do want to generate a panic. Sowhat are we going to do in the situation thatour application is panicking. And we can getourselves to a situation where we can recoverthe application. So panics don't have to befatal. They just are, if we panic all theway up to the go runtime, and the go runtimerealizes wait doesn't know what to do witha panicking application. And so it's goingto kill it. So we come back to this applicationhere, and run, we see that once again, we'regetting this panic. But something I want toshow you related to our deferred discussionis this modification here. So I've added thisline, a deferred print statement that saysthis was deferred. So if we run this application,something interesting is going to happen.We get stopped printed out. That's not a bigsurprise. But then we get this was deferredprinting out and then the panic happens. Andthis is really important, because panics happenafter deferred statements are executed. Sothe order of execution is we're going to executeour main function, then we're going to executeany deferred statements, then we're goingto handle any panics that occur. And thenwe're going to handle the return value. Sothere's actually quite a bit that happensin a function after it exits this closingcurly brace here. So why is this important?Well, the first thing that's important isthat the first statements that are going toclose resources are going to succeed evenif the application panics. So if somewhereup the call stack, you recover from the panic,you don't have to worry aboutresources being left out there and left open.So any deferred calls to close resources arestill going to work even if a function panics.But the other thing that's really importantis if I change my deferred function here tosomething like this, now, this is a customfunction. And we're not going to talk aboutthis for a couple of videos yet. But thisis important in order to talk about this.So I need to jump this ahead in the queuea little bit.So what we're creating here is what's calledan anonymous function. And an anonymous functionis simply a function that doesn't have a name.So nothing else can call this, this is definedat one point, and we can call it exactly onetime, these parenthesis here are making thatfunction execute. And this is an importantthing to know about the defer statement, thefirst statement doesn't take a function itself,it actually takes a function call. So youwant to invoke that function, otherwise, thingsaren't going to work properly. Now, insideof this custom function, notice that we'reusing this recover function here. So whatthe recover function is going to do is itwill return nil if the application isn't panicking.But if it isn't nil, then it's going to returnthe error that actually is causing the applicationto panic. So in this logical test, we're checkingto see if it's not nil. So if it's not nil,that means our application is panicking. andin this situation, we're simply going to logthat error out. Now, what happens in thissituation is the execution still stops atthe panic. And let me import this log packagebefore we run this. And we see that the applicationstill execute. So we got the string startprinted out, then we printed out the errorusing the long package. But we didn't getthis end function printed out. So it lookslike our application is still dead. But recovereddoes have some important impacts if we havea deeper call stack than what we're dealingwith right here. So let me drop in this examplein order to show you. So in this case, we'vegot the main function, that's going to beour application entry point, of course. Andthen we've got this other function calledpanikkar. And all this thing does is it'sgoing to print on the line that says it'sabout the panic, and then it's going to panic.And it's going to go ahead and recover fromthat panic using that deferred function thatwe saw just a moment ago, then we're gonnahave this line here, done panicking. And thenup in our main function, we're going to printstart, we're going to call the panacur. Andthen we're going to print and so let's seewhat happens when we run this. So as you see,we get the start line printed out, like youwould expect, we see the about the panic stringprint out, then we panic, something bad happened,we go into our recover loop, because we'renot going to execute this because our applicationpanic. And so our panikkar function is goingto stop execution right there and executeany deferred functions. And inside of thatdeferred function, we call recover. So inthat recover, we're going to log out the factthat we have that error, and we're going tolog that error message out that we see here.But then in the main function execution continues.So in the event that you're recovering froma panic, the function that actually recovers,still stops execution, because it's in a statewhere it can no longer reliably continue tofunction. And so it makes sense for it tostop doing whatever it was trying to do. However,functions higher up the call stack, thosefunctions that call the function that recoveredfrom the panic, they are still presumablyin a situation where they can continue, becauseyou recover function said that your applicationis in a state working to continue to execute.Now, this is a little bit limiting as well,because in order to determine what that erroris, you actually have to call the recoverfunction, which basically means that you'resaying that you're going to deal with it.So what happens if you get that error, andyou realize that this isn't something thatyou can deal with? Well, in that case, whatyou're going to do is repainting the application.So all we need to do in order to do that isjust read through the error, or you can comeup with another error string, whatever makessense for you. But in this case, inside ofthe handler, we're throwing a panic again,then we actually see that we don't get thatend statement printed out our main functionpanics because we read through that panic.So we get the full stack trace of when thepanic actually happened. And we see that we'reinside a func one function, one is actuallythis anonymous function right here. And wesee that we do get that stack trace printedout and we don't get the string and printedout. So if you're in a situation where you'retrying to recover from a panic, and you realizeyou can't handle it, you can feel free tore throw that panic, and the further managementof that panic, higher up the call stack. Okay,so let's go into a summary and review whatwe've talked about in this video. In thisvideo, we talked about the final two controlflow concepts that we have to be aware ofin go programming. Now, one could argue thatdiffers and panics aren't really control flowconstructs, because they're not the traditionalbranching or looping logic that we consider.But they definitely do alter the flow of executionof our program. And so I'm going to lump themtogether in this category. The first thingthat we talked about was the use of the deferfunction in order to delay the execution ofa statement until the end of a function. Now,these are useful to group open and close functionstogether. So if your open function is goingto open up a resource, and you need to makesure that you close that, then you can deferthe call to close that resource to make surethat you're freeing up the memory and notholding on to that resource any longer thanyou need to. The one thing I would warn youthough, is be careful loops. If you're openingand closing a bunch of resources inside ofa loop, then you probably want to explicitlyhandle that close without the deferred keyword.Because when you're using deferred keyword,all of those resources are going to stay openuntil the function finishes execution. Andthat can cause you some memory issues, especiallyif you're dealing with a very large numberof resources. If you've got multiple differentstatements inside of a function, then theyrun in life order or lastin. First out, sothe last deferred statement that you callis going to be the first one that actuallyexecutes. And again, this makes sense, becauseif you're opening a bunch of resources, andthose resources are dependent upon one another,then you're going to close them in the reverseorder that you open them, which is typicallythe right way to go. arguments in a deferredcall are evaluated at the time the defer isexecuted, not at the time the called functionis executed. And that's important to keepin mind because you could pass in variablesinto a deferred function call. And it canbe a little bit confusing if you change thevalue of those variables further on. And thosearen't reflected in your deferred statement.So just keep in mind, when you call defer,the value of those variables are going tobe captured at the time that that defer isexecuted, not at the time the function actuallyexecutes. Now, something unplanned happensin our application, then we've got two waysthat a go application can go it can returnan error value, which is the normal case,or it can panic. Now, what you generally wantto do is you want to return an error value,because in go programming, we typically don'tconsider a lot of things that go wrong inan application to be exceptional events, orto be events that should cause the applicationto shut down. For example, a classic exampleof that is making an HTTP request, and youmake the request and you don't get a responsefrom that server. Well, that's something thathappens all the time, no response is a validresponse, it just happens to be an error,because you're looking for that resource,and you got a 404 return back. So in thatcase, you're going to return an error value,you're not going to panic, the applicationpanics are used when an application gets intoa state that it cannot recover from, for example,you hand the runtime a divide by zero problem,there's no way for you to figure out how todivide a number by zero, and so has no wayto manage that. And that's a situation wherethe application is going to panic. So justto summarize, then, they occur when an applicationcan't continue at all, don't use them whena file can't be opened. Unless it's a criticalfile. For example, if you're opening a templatethat's used in a web application to generateyour view layer, then that might be somethingthat's worth panicking over. But if you'retrying to open up a log file, and you can'tget there, well, there's no reason to panicfor that necessarily, you just need to returnan error value and then inform somebody thatthe logs aren't available.Now a situation where you might want to panicis unrecoverable events. So if you're startingup a web server, and it can't get ahold ofthe TCP port that it's supposed to be listeningon, then that's probably a situation wherepanic makes sense, the function will stopexecuting immediately at the point of thepanic, but the third functions will stillfire. If nothing handles that panic, theneventually the panic is going to go up thecall stack, it's going to hit the go runtime,the go runtime has no built in handling forpanicking situations. And so the program willthen exit. If you do have a panicking situationthat you feel that you can recover from, thenyou can recover using the built in recoverfunction. Now that function is used to recoverfrom panics, it's only useful inside of deferredfunctions. And the reason for that is becauseof the behavior of panic. Remember, when anapplication starts to panic, it no longerexecutes the rest of that function, but itwill execute deferred functions. So the properplace to use the recover keyword is insideof a deferred function, that's going to lookfor a panicking situation. And if the applicationis panicking, then it can go ahead and decidewhat to do with it, the current function willnot attempt to continue. But if you do recover,then higher functions in the call stack willcontinue as if nothing went wrong. Now, ifthat's not the behavior that you want, ifyou call that recover function, you look atthe error that's returned from the recoverfunction and you can't handle it. Rememberthat you can go ahead and rethrow that panicby calling the panic function again. And thenthe panic will continue to propagate up thecall stack, I want to talk about pointersand how we can use them in the go language.We'll start that discussion off by learninghow to create pointers. And then we'll talkabout something called dereferencing. a pointer,which is basically using a pointer to getat some underlying data. Then we'll talk aboutthe new function. And then we'll talk abouta special type in NGO called nil. And thenwe'll wrap up our discussion by talking aboutbuilt in types and go that use internal pointers.And so their behaviors a little bit differentthan other types that you'll work with inyour applications. Okay, so let's dive rightin and learn how to create some pointers.To start our discussion, I want to start assimply as I can. So I'm going to use thisexample here. As you can see him declaringa variable a and assigning it the value 42.And then I'm going to go ahead and print thatout. So it should be no big surprise thatwhen I run this, the value 42 gets printedout. Now, if I extend this application a littlebit, and I create a variable b and assignit to the value of a and print both of thoseout, then again, it's no big surprise that42 prints out two times. Now since a and bare value types. When I put in this line here,what go is going to do is it's actually goingto copy whatever data is stored in a and assignit to be. So we're not actually pointing tothe same memory. And we can prove that bychanging the value of a to, for example, 27,and then printing out everything again. Andif we run this, we see that the value of achanges to 27, but the value of b stays at42. Now we can change this behavior a littlebit by having B point to a using somethingcalled a pointer. Now in order to demonstratethat, I'm going to actually change our declarationsyntax a little bit and go to more of a longform syntax. And the reason for that is tomake things a little bit more clear abouthow the pointer notation works. So I hopeyou'll agree with me that this line eightis exactly the same as the previous one thatwe had, it's just a little bit more verbose.Now, if I want to change B into a pointer,and use that same long form syntax, what Ican do is declare the variable b. And thenI'm going to declare it as a pointer to aninteger. And the way I declare it as a pointeris by preceding the type that I'm pointingto with this asterisk here. So then if I wantB to point to a, then I'm going to use a specialoperator called the address of operator thatyou see here. So at line nine is now sayingis that B is a pointer to an integer. AndI want to point it to anow what is a pointer exactly? Well, let meremove these lines here and run this and seewhat we get as output. So as you see here,a is still holding the value 42, like we expect,but B is holding this strange data here. Sowhat is that? Well, this is actually the numericalrepresentation for the memory address thatis holding the location of a. So at this locationin memory, we actually have assigned the integer42. So be isn't holding the value 42, it'sholding the memory location that's holdinghis data. And we can prove that by using theaddress of operator down here. And when werun this, again, we should see, in fact, wedo see that the values are exactly the same.So the address of A in memory is this valuehere, and B is holding that exact value. Andas a matter of fact, we can even go the otherway. Because while the address of operatoris going to give us the address of a variablein memory, we can use a different operatorin order to figure out what value is actuallybeing stored at a memory location. And that'scalled the dereferencing operator. So if Igo ahead and remove this, and then I'm goingto put a dereferencing operator right herein front of this pointer. Now you notice we'reusing the same asterisk. But these have alittle bit different meaning here. So up hereon line nine, this asterisk before type isdeclaring a pointer to data of that type.So this is a pointer to an integer. Now whenI put the asterisk in front of a pointer,then that's going to dereference, which basicallymeans it's going to ask the go runtime, todrill through the pointer, find the memorylocation that that pointer is pointing to,and then pull the value back out. So if werun this, we see that we get the value 42printed out both times once again. Now what'sthe point of all of this, what the point is,since B is pointing at the same value of a,Now both of these variables are actually tiedtogether. So if we change the value of a onceagain, and then print out their data, nowwe see that both A and dereferencing, b bothgive the value of 27. Because they're bothactually pointing at the same underlying data.As a matter of fact, we can even dereferencethe pointer and use that to change the value.So if I use the dereference, B, and assignthe value 42 to it, and then print it outagain, then we see that once again, both valuesare changing a and the value that B is pointingto our both teams, because it's in fact thesame data. Now, if you come from a backgroundin languages that allow you to work with pointersas variables, then you might be expectingto do something called pointer arithmetic.So if I drop this example in here, we're goingto start to play around with something that'sgoing to lead us to see how go treats pointerarithmetic. So I'm going to start with a variablea, and that's going to hold an array of threevalues, one, two, and three. And then I'mdeclared B as a pointer to the first elementin the array to this value right here. Andthen C is pointing to this second elementright here. Now if I use this print statement,here, it's going to print the value of thearray and then this percent p syntax is actuallygoing to print the value of the pointer forBNC. So if we run this, we see that we doin fact, get the array and then we get thesetwo memory locations printed out that B andC are holding. Now notice that C is holdinga value that's for higher than B. And thereason for that is how go lays out arraysin memory. Since this is an array of integersand integers in this version of the runtimeare four bytes long each element of an arrayare four bytes apart. So this memory addressending with 124 is holding the first elementin the array, and then four bytes later, we'regoing to hold the next element of the arrayc you might be tempted to do something likethis if you come from another language, ifI take the address of C and subtract four,then that actually should give me the addressof B and then I can dereference that and Bothof these should be pointing to the head ofthe array. Well, if I run this, I in fact,see that I get an error. And the reason forthat is go does not allow math like this tobe done on pointers. Now, once again, if you'vecome from C or c++, you're probably awareof the tremendous performance advantages thatyou can get if you're allowed to do pointerarithmetic, because you can jump around mappedmemory very, very quickly, and gain some prettysubstantial benefits in the performance ofcertain applications. However, whenever youget into pointer arithmetic, you're typicallygetting into some fairly complicated code.And since go has as one of its core designconcerns simplicity, the decision was madeto leave pointer arithmetic out of the golanguage. Now, if you absolutely need to havesomething like this in your application, thenwhat I would suggest is you can come intothe go packages and come down here to theunsafe package. And this is going to giveyou operations that the go runtime is notgoing to check for you. So if you really needto do pointer arithmetic and things like that,then the unsafe package, which has a veryappropriate name is available for you forthose very advanced scenarios. Now, thosescenarios are advanced enough that what I'mgoing to suggest is if you need to know it,you're going to learn it. But generally, you'renot going to need to know how this stuff works.Now the next thing that I want to show youis how we can actually create pointer types.So we've seen this address of operator andthat's allowing us to instantiate a pointerto a certain variable. So if we look at thetype of B, it's actually a pointer to an integerbecause we're pointing to one element in thearray. But so far, we've had to declare theunderlying type first. Well, that's actuallynot necessary and go because often, you onlywant to work with the pointers, and you don'treally care where the underlying data is stored,you just need the ability to point to it whereverit's at. So in this example, we see how wecan do that. Now we've seen almost exactlythe same syntax before, because when we weretalking about structs, we had an example thatlooks something like this. So we were declaringmy struct object, we were instantiating itusing the object initialization syntax. Andwhen we print everything out, we print thevalue 42 out. Now if I make this MSX variable,a pointer to a my struct, and then use theaddress of operator in this object initializer,then I actually get almost the same behavior,except for notice that when I print out thevalue of MS, I end up with this ampersandhere, which is basically saying that ns isholding the address of an object that hasa field with a value 42 in it. Now the advantageof being able to do this, it's going to cometo light in a future video. But for now, justbe aware that you can do this. Now this isn'tthe only way that we have available to usto initialize a variable to a pointer to anobject, we can also use the built in new function.Now unfortunately, with the new function,we can't use the object initialization syntax,we're just going to be able to initializean empty object. So if I go ahead and runthis, we see that we do get an initializedobject. But all of its fields are initializedto their zero values, we can't initializethem using the object initialization syntax.Now, since I mentioned zero values, it's importantto understand the zero value for a pointer.Because as we talked about, in a very earlyvideo, every variable that you declare ingo has an initialization value. So right hereafter line eight, ns is holding something.So the question is, what is that thing? Soif I go ahead and copy this print statementup here, we will be able to answer that question.So let me format this and run it. And thenwe see that we get the special value nil out.So a pointer that you don't initialize isactually going to be an initialized for you.And it's going to hold this value nil. Sothis is very important to check in your applications.Because if you're accepting pointers as arguments,it is best practice to see if that pointeris a nil pointer. Because if it is, then you'regoing to have to handle that in a differentway. So for example, if we try and drill throughand get to this foo field, but and this isactually nil, then we're going to get a runtimeexception, and our program is going to crashon us. Now that actually leads us to an interestingpoint, how do we actually get at this underlyingfield and work with it? Well, the obviousway that we're going to need to do that iswe're going to have to dereference the nspointer in order to get it that struct, andthen we can get it that field. So we're goingto have to use something like this. Now, youmight be asking why the prints are there?Well, it turns out that the dereferencingoperator actually has a lower precedence thanthe dot operator. So we need to print in orderto make sure that we're dereferencing, theMS variable instead of dereferencing, Ms dotfoo. So now we can go ahead and set this tothe value 42. Now in order to get at the valueof foo, to print it out, then we're goingto have to repeat the same exercise. So we'regoing to add some brands here. I'm going towrap this ms variable, and then we'll printout the value of foods. So if I go ahead andrun this, we in fact, do set and get the value42, which is the value of that field. NowI hope you'll agree with me at this pointthat this syntax is really ugly, because weuse this syntax every time we dereferencea pointer, we're going to have to use matchthat a params and have this dereference operator.Well it turns out because of the limitationsthat are put on point Here's the compileris actually able to help us out a little bit.So in fact, we don't need this syntax at all.If we go ahead and remove this and removethis from the print statement as well, andrun this, we actually get exactly the samebehavior. Now again, if you're coming froma language, which makes extensive use of pointers,this is probably freaking you out. Becausethe pointer and sdoesn't actually have a field foo on it. Thepointer MS is pointing to a structure thathas a field foo. So how is this working? Well,again, this is just syntactic sugar. Thisis just the compiler helping us out becauseit understands or not actually trying to accessthe Foo field on the pointer, we're implyingthat we want the underlying object. And sogo is going to go ahead and interpret thatproperly for us, and dereference, the pointer.So the compiler really sees this statementthe same as this statement, they're exactlythe same to the compiler, it's just one readsa little bit more cleanly. Now, the last thingthat I want to talk about today is how gohandles variables when they're assigned oneto another. So let me go ahead and paste thisexample in here. As you can see, on line eight,we're initializing an array. On line nine,we're initializing another variable and pointingit in the same array as a, then I'm goingto print out both A and B. And then I'm goingto change index one of the A array to 42,and print them out again. Now, we've alreadydone this example in the past, and hopefullyyou remember that a is going to change butB is not because b is a copy of the arraythat we stored in a and so they update independentlyof each other. However, if I remove this index,and turn this into a slice, the behavior changesa little bit. If we run this, now we see thatboth A and B are changed together. So whathappened there? Well, the slice is copiedjust like the array, but the effect of thecopying is a little bit different. Becausein the version with an array, the values ofthe array are actually considered intrinsicto the variable. So A is holding the valuesof the array, as well as the size of the arrayand that size is held that way we can do boundschecking. So for example, if we asked forindex three, which is beyond the bounds ofthis array, and run, we can do bounds checkingin the go language. And that's a very goodthing. However, with slices, remember, a sliceis actually a projection of an underlyingarray. And so the slice doesn't contain thedata itself, the slice contains a pointerto the first element that the slice is pointingto on the underlying array. So what that meansis that when we work with slices, the internalrepresentation of a slice actually has a pointerto an array. So while line nine is still copyingthe slice a into the slice B, part of thedata that gets copied is a pointer, not theunderlying data itself. What that basicallymeans is, when you're sharing slices in yourapplication, you're actually always goingto be pointing at that same underlying data.Now, the other built in type that has thisbehavior is a map. Because maps, once again,have a pointer to the underlying data, theydon't actually contain the underlying datain themselves. So if I take this example,where I'm initializing a map of strings tostrings and assigning that to a, and thenB is assigned to a, then I print them bothout, then I change one of the values in aand print them out again, if I run this, wesee that both maps start out the same. Andthen when I change the key foo in the map,a we actually change that in the map B aswell. So what does that mean? Well, what itmeans is, when you're working with slicesand maps in a go application, you have tobe very, very careful to keep in mind at alltimes who's got access to that underlyingdata. Because passing slices and maps aroundin your application can get you into situationswhere data is changing in unexpected ways.However, if you're working with the otherdata types, specifically primitives, arrays,or structs, then this generally isn't goingto happen to you because when you copy a struct,it's actually going to copy the entire structureunless you're using pointers. Okay, so thatwraps up what I have to talk about with pointers.Let's head into a summary and review whatwe've talked about in this video. In thisvideo, we talked about pointers and how touse them in the go language. Now, we haven'tseen a lot of practical application for pointersyet. But we need to understand what pointersare. Before we get into that. So over thenext couple of videos, we'll get into whyyou would want to use pointers and the benefitsof them. But for now, we're just trying tointroduce the basic subject. So we startedby learning how to create pointers. And welearned that if we prefix a type with an asterisk,that's actually going to declare that typeto be a pointer to that underlying data type.So for example, we have this asterisk int,which is going to be a pointer to an integer.We also learned how we can create pointersusing the address of operator to get the addressof an existing variable in memory. Then welearned about dereferencing pointers and howwe can use that asterisk operator again, butthis time in front of a pointer instead ofin front of a type and use that to drill throughthe pointer to get at the value that the pointeris pointing to. We also learned that whenyou're working with complex types, such aspointers to structs those pointers are automaticallygoing to be dereferenced for us. So our syntaxdoesn't get cluttered up by a whole bunchof dereference, operations and parenthesis,then we moved on to learn how to create pointersto objects. And we learned that there's acouple of different ways. So the first thingthat we can do is use that address of operatorto get access to a pointer to an object thatwe've already created. So in this example,we've got an instance of a my struct objectcalled Ms. And then we can use that addressof operator to create a pointer p to thatstruct. But we can also do that directly.So if we proceed an object initializer, withthat address of operator, then we can actuallydirectly create a pointer. And we don't haveto have an intermediate variable that's holdingthat value, we can also use the new keywordto initialize an object, but we can't initializethe fields at the same time. So the behavioris a little bit different, because we're goingto have to use the new keyword that's goingto zero out all the fields in a struct, forexample. And then we're going to have to comein later and initialize all the values. Thelast thing that we talked about was typeswith internal pointers. And we saw how whilethey're treated exactly as any other variabletype, when we do an assignment, their behavioris a little bit different. So all assignmentoperations and go are copy operations. Sowhenever you have a variable a and you createa variable b and set it equal to a, all thedata in a is going to be copied in order tocreate B. However, slices and maps containinternal pointers. And so even though they'recopying, they're copying pointers, and sothey're pointing to the same underlying data,we're going to take a deep dive into functionsand how you can use them in the go language.Now, we've been talking about functions throughoutthis entire video series. But we've neverreally taken the time to focus on them, becausethere's a lot of groundwork that we've hadto go through building up to the point wherewe can understand what a function is, andhow we can use those in our applications.So in today's video, like all of our videosin this series, I'm going to break the discussiondown into multiple parts. We'll start by talkingabout the basic syntax of a function. Thenwe'll talk about the parameters that you canpass into a function to influence how it works.Then we'll talk about the return values thatyou can get back out of the function. We'lltalk about something called an anonymous function.We'll talk about how functions in the go languageare first class citizens and can be passedaround in your application like any othervariable, and then we'll wrap up our discussionby talking about a special kind of functioncalled a method. Okay, so let's get startedby learning the basic syntax of a function.To start our discussion of functions, we don'thave to go any farther than the basic applicationthat the NGO playground gives us. So as soonas you come into the playground, you're presentedwith this very simple application. And righthere, we have our first function. Now, theway that a go application is structured, isyou always have to have an entry point inthe application. And the entry point of goapplication is always in package main. Andwithin that main package, you have to havea function called main that takes no parametersand returns no values. So we can see thatright here. And so when we run the application,the application actually starts right here.And so we print out the string, hello playground.And that's all that our application has todo. So every NGO application starts this way.And we see here the most basic function thatwe can create in the go language. So thereare several major parts that we need to understandabout a function. First of all, they startwith the func keyword. So as you can see here,we're not going to start with function oranything else, we start with the func keyword,and that's going to describe a function thatwe're going to create. Then we have the nameof the function that we're going to create.And this follows the same naming conventionas any other variable and go see you're goingto use Pascal case or camel case for the namesof your functions. And depending on if youhave an uppercase or lowercase determinesthe visibility of that function. So just likewith variables, and uppercase first letteris going to be published from the package,so anything else can use it, and a lowercasefunction name is going to be kept internalto the package. Now after the name of thefunction, we have these match params herenow we'll see as we get into parameters, whatthese are for, but this is required syntaxafter the main function. So even if you don'ttake any parameters in your function, youhave to have these match params. And thenthe actual body of the function is containedwithin these curly braces here. Now thereare a lot of holy wars and a lot of languagesabout where these curly braces should go.So some languages put them here, some languagesput them down here, some languages like toindent them, there's all sorts of differentconventions about where to put these curlybraces. Well, in the go language, there aren'tany arguments because this is the conventionthat is enforced by the compiler itself. Soyou have to put the opening curly brace onthe same line as the func keyword. And thenthe closing curly brace generally has to beon its own. Now there are a couple of situationswhere you can have that closing curly bracecombined with a couple of other params. Andwe'll talk about that a little bit later inthis video. But generally speaking, when you'redefining a function, the closing curly bracehas to be on its own line. Now, with functionsdefined like this, the execution path is static,we can't actually influence what our functionsdoing from the outside, because we're notpassing any information into it. So if wedo want to pass information into it, thenwe're going to provide what are called parametersinto the function. So let me just drop anexample that uses parameters. And you seehere a main function is now calling into anotherfunction called say message. And that samemessage function takes in this parameter MSG,that's of type string. So when you're defininga function that takes parameters, the parametersgo between these two parenthesis here. Andthey're described like any other variabledeclaration, except for you don't need thevar keyword. So you're going to have the nameof the parameter, and you're going to havethe type of the parameter. So then when wewant to call that function, we have to passin the value for that parameter. And that'scalled an argument. So we're going to passin the argument Hello, go. And then insideof our function, we're printing out the messagethat gets passed in. And so when we run this,we see that Hello go prints out as a result.Now this MSG parameter isn't special in anyother way than the fact that it's passed intothe function, it's treated as a local variable,just like any other local variable that wemight create. So if we created another variablehere, and we said, Hello, go, that's treatedexactly the same way as our mystery variable.So the only difference between the two isthe MSG variable can be passed in from theoutside. And of course, that greeting variablewas created locally. Now, you aren't constrainedto just pass a single parameter in, you canactually pass multiple parameters in. So ifI drop in this example, we can see that inaction. So here, I've extended the same messagefunction to take two parameters. So I've stillgot the message parameter that's of type string.And then I've put this comma here, and I'veadded another parameter, and that's goingto be an index. And that's going to be oftype integer here. So as you can see, we canpass as many parameters as we want, we'rejust going to provide a comma delimited listof those parameter names and their types.So then when we call the function, we're goingto pass in one value for each one of thoseparameters. And they have to be in the sameorder that they're declared. So the firstargument that we're going to be passing isthe string hello, go, which is going to matchthis MSG variable. And then we're going topass in the I that's going to be the loopcounter here. And that's going to be passedinto this ID x parameter. So then, in thisfunction, we're just going to print out thevalue of the message and then we're goingto say what index we receive. So when we runthis, we see that the message prints out fivetimes. And we get the same message, but theindex variable changes because we're passingin a different value every time we call thefunction. Now, often, when you're defininga function, you're going to pass in multipleparameters of the same type. So you're goingto be tempted to have syntax like this. Soin this example, we've got a slightly differentfunction. So instead of a generic, say, Hello,we're going to have a say greeting function.And we're going to provide the greeting thatwe're going to have and the name of whoeverit is we're going to greet. So in this case,we're passing in the string hello, and thestring Stacy. So when we run this, we're gonnasay hello to Stacey. Now, since these typesare the same, the go compiler actually providesus a little bit more syntactic sugar, becausethis is a little bit more verbose than isstrictly necessary. So instead of specifyingthe type every time, we can actually justspecify a comma delimited list of variables,and then the type at the end. And what thecompiler is going to do is, it's going toinfer that every variable that's in that commadelimited, list has the same type. So whenwe run this, we actually get exactly the sameexecution. But we just have a little bit moreterse syntax. Now, so far, we've been passingparameters in as value types. If you rememberfrom our last discussion, we were talkingabout pointers. And notice that I don't haveany pointers in our function signatures rightnow. So let me drop in this example here.And then we can start playing around withthe difference between passing in values andpassing in pointers. So when I have this examplehere, and I run it, we get exactly the sameoutput that we had before. But I've got somevariables that I'm using to pass in as argumentsto this function. Now, what do you think isgoing to happen if I change the value of oneof these variables inside the function, sofor example, if I change the name variableto Ted, and let's go ahead and print thatout, just to verify that it printed. And thenwhat do you think is going to happen if Iprint the same variable out, again, righthere. So I'm passing in the name variableby value. So that means that the go runtimeis going to copy the data that's in this namevariable and provided to here. So what wewould expect is when we change the value ofthe name variable right here, it should havean effect. And we should print 10 out here.But since this is a copy of the name variable,we actually shouldn't have any effect outhere. So if we run this, we see that in fact,that is true. So this is a safe way to passdata into a function, you can be assured bypassing by value, that the data is not goingto be changed when you pass it in. Now, ifI change this to passing in pointers, by addinga pointer here, and then passing in the addressof these variables right here, and then dereferencing,the pointers right here, then let's see what'sgoing to happen. Actually, I need to add anotherdereference right here. And now we're passingpointers to our variables around in our application.So now, instead of working with a copy ofthe name variable, we're working with a pointerto the name variable. And so when we run this,we actually see looks like I missed a dereferenceoperation right here. And now we see thatwe have in fact, change the variable not onlyin the scope of the function, but in the callingscope as well. So by passing in a pointer,we have in fact manipulated that parameterthat we passed in. Now, why would you wantto do this? Well, there's a couple of reasons.First of all, a lot of times our functionsdo need to act on the parameters that arepassed into them. And so passing in pointersis really the only way to do that. The otherreason is passing in a pointer is often much,much more efficient than passing in a wholevalue. Because right now we're passing insimple strings, and strings aren't that largeand ghost, so passing in copies versus passingin pointers is pretty much going to be thesame in terms of performance. However, ifyou're passing in a large data structure,then passing in the value of that data structureis going to cause that entire data structureto be copied every single time. So in thatcase, you might decide to pass in a pointersimply for a performance benefit. Now, youdo have to be a little careful when you'repassing in pointers, because of course, youcan inadvertently change that value. And soyou can cause some issues for yourself. Now,just to remind you of something else thatwe talked about in the pointer discussion,if you're working with maps or slices, thenyou don't really have this option, becausesince those two types have internal pointersto their underlying data, then they're alwaysgoing to act like you're passing pointersin. So just be careful when you're using thosedata structures. Because you can inadvertentlychange the data in your calling function,when you're manipulating them within the callingfunction.The last thing that I want to talk about whenwe're working with parameters are what arecalled variadic parameters. So if I drop inthis example, here, we can see an exampleof a variadic parameter. So in this case,I've got a generic some function that I'mcreating here, and I'm passing in the numbersone through five. Now, I'm not receiving fivevariables here, instead, I've got one variablehere, and I've preceded its type with thesethree dots here. So what that's done is that'stold the go runtime to take in all of thelast arguments that are passed in, and wrapthem up into a slice that has the name ofthe variable that we have here. So then insideof the sum function, we're going to printout what that values object is just so wecan see that and then we're going to go aheadand add up all the values in there. So sinceit's going to act like a slice, we can usea for loop and range over those values. Andthen we're going to print out the result ofthat. So when we run this, we see that wedo in fact, have a slice is printed out, thesum is 15. So we got that result printed outproperly. And there's no problem at all. Now,when you're using a variadic parameter, youcan only have one and it has to be the lastone. So if I, for example, want to have acustom message string, I can pass that in,and then pass that in here and then replacethis. And this still works just fine. However,I couldn't, for example, put the message parameteras the last parameter, because the runtimedoesn't have the ability to understand wherethe variadic parameters and and where additionalparameters would begin. So if you're usingvariadic parameters, you can only have oneand a half to be at the end. Okay, now, it'snice to be able to pass data into our function,because now depending on the different datathat I pass in, I can change the behaviorof the function. But it's also very usefulto be able to use our functions to do somework, and then return a result back to thecalling function. So in order to do that,we're going to use what are called returnvalues. So if I dropped in this example, wesee it's basically the same as our last example.But instead of printing the message in thesum function, we're returning the result out.And then the main function is actually workingwith that. So there's a change we had to makein our function signature. So right here afterthe parameter list, and before the openingcurly brace, I've listed the return valuestype. So in this case, I'm expecting to returnan integer. So I just put it right here, insideof my function, I'm going to use the returnkeyword. And then I'm going to return thevalue of the variable that I've been buildingup throughout the course of the function.So in this case, I declare the result variablehere, I populated in this loop. And then Ireturn that result back now up here in themain function, I can catch that return valueby declaring a variable and setting it equalto the result of this function. So S is actuallygoing to be an integer type, because that'swhat was returned out of this function. Andthen I can work with that integer. So if Irun this, I get exactly the same behaviorthat I had before. But now the sum functionis more of a pure function, it doesn't carewhat I do with that result, it's just goingto generate the result and return it backto the caller. Now another feature that gohas that's actually pretty rare in a lot oflanguages, is the ability to return a localvariable as a pointer. So in our previousexample, when we return that result, go actuallycopied that result to another variable, andthat's what got assigned. But we can alsodo this. So if you look here, I'm returninga pointer to an integer now. And instead ofreturning the result, I'm returning the addressof the result. And so S is now a pointer.So I change to a dereference operation. Soif I run this, it works exactly the same way.Now again, if you're coming from another languagethat uses pointers a lot and doesn't abstractaway the differences between working on thestack and working on the heap, then this mightfreak you out a little bit, because when wedeclare the result variable, it's actuallydeclared on the execution stack of this function,which is just a special section of memorythat's set aside for all of the operationsthat this function is going to be workingwith. So in this funk Exit, then executionstack is destroyed, that memory is freed up.And so in a lot of languages, this is nota safe operation, because now you're returninga pointer to a location in memory that justgot freed. And so you've got no idea whatvalue is going to be there.Well, in the go language, when it recognizesthat you're returning a value that's generatedon the local stack, it's automatically goingto promote this variable for you to be onthe shared memory in the computer, what'salso called the heap memory. So you don'thave to worry about this value being cleared,the runtime is going to recognize that you'rereturning a pointer from the local stack,and everything is going to work for you justfine. And that makes a lot of things moreconvenient. Because within the function, wecan work with this as a true value. So wedon't have to worry about dereferencing pointers,and then just right at the end, we can returnthe address of the result. And the runtimemakes it all work for us. Another thing thatwe can do in the go language, and this isn'tdone very often, but there are cases whereit is valuable is using named return values.So if I drop in this example here, noticethat I've changed my return value. Now I'vegot a set of parenthesis here. And then I'vegot a name for the return value and a typefor it. So when you do this, this is basicallysyntactic sugar for declaring a result variable.So this variable is going to be availablein the scope of our sum function. And thenthat value is going to be implicitly returned.So we can work with that result variable righthere within our function. And then we don'thave to specify the name of the return variabledown here in line 17, we just have to tellit to return. So when we run this, we seethat once again, we get exactly the same behavior.But the body of our sum function is actuallyquite a bit cleaner, because we don't haveto do the maintenance of instantiating, thisresult variable. Now, this is actually notdone very often in the go language. And mysuspicion is because it can be a little bitconfusing to read, because your return variablesare declared way up here at the top of thefunction, and your actual return is down hereat the bottom. So if you're reading this code,and you're trying to figure out what thisfunction is actually going to return, youhave to come all the way back up to the functionsignature. So this can be a very valuabletechnique to use. But I would be very carefulwith it. Because if you've got long functions,the named result parameters can actually bemore confusing instead of less confusing.So you have the option there, pick whicheverone makes the most sense for your application.The last thing that I want to talk about withreturn values is the fact that we can do multiplereturn values from a function. So in orderto show you why this is valuable, let's takethis example here. So I've created a simpledivide function that takes in two parametersA and B, that are float 64, it's going todivide them and it's going to return thatresult back. So if I run this, I get 1.6 666.Like you might expect, and everything's fine.But what happens if I pass in a zero here.Now when I run this, I get an unknown result,I get a positive infinity result. And I can'twork with that in my application. So I'm goingto probably cause some sort of a failure downthe line. So in a lot of languages, the onlything we could do is throw an exception orpanic the application and go when we detectthat there's this invalid value for the parameterB. So I guess we could do that we could addsome kind of logic here. If b equals equals0.0, then we're going to panic and we're goingto say, cannot provide zero as second value.And that would work. But keep in mind whenwe talk about control flow and go, we don'twant to panic our application as a generalcourse of action, because panicking meansthe application cannot continue. Now, in fact,this application cannot continue if somebodyprovides the value of beat. But it's reasonableto assume that we might pass zero in for thisbe parameter occasionally. So instead of doingthis, what we actually want to do is returnan error back letting the calling functionknow something that they asked it to do wasn'table to be done properly. So instead of doingthis, we're actually going to add a secondreturn variable. So to do that, we're goingto add a print here. And we're going to returnan object of type error, and then close thatparenthesis off. So we can return as manyvalues as we want from a function. But thisis a very idiomatic way of using the go language.So we're going to return the intention ofthe function as the first return value. Andthen we're going to return an error objectin case something went wrong. So in that case,what we're going to do is, we're going toremove this panic, because we really don'twant to be doing that. And we're going toreturn the first value, we're just going tozero it out, because we can't do this operation,so we can't return anything meaningful. Andthen we're going to return an error object.Now you can generate one of those by usingthe air f function. And we can say cannotdivide by zero. So we're going to providea value for that error. And then that's allwe need to do here. So since we've returnedto this function in the error case, then ifwe get past it, we can continue as if ourparameters are okay. And so in that case,we can actually do our operation. And thenfor the error value, we're going to pass nilbecause no error was present. And again, thisis very idiomatic go, we're going to returnan error value if something went wrong, andthen we're going to explicitly return nilif nothing went wrong. And then if you'veread any amount of go code, you've seen thisquite a few times. We're going to check tosee if that error also Got our standard iferror is not equal to nil, and then we'regoing to put our error handling logic in here.So in this case, all we're going to do isprint out the error and return from our mainfunction. So we're going to exit our application.If we don't have that, then we're going tojust print out the result of our calculation.So again, this is a very common pattern andgo inside of your functions that can generateerrors, you're going to return the expectedvalue and then an error as the second parameter,then you're going to have a guard that's goingto check for those error conditions, you'regoing to return as soon as possible from yourfunction with the error value if an erroris present. And the reason for that is we'regoing to try and left justify our code asmuch as possible. So we don't end up withthese pyramids of doom, where we're goingto have else checks. And we do all of ourerror checking at the bottom, we're goingto do our error checking at the beginningand then return out as soon as possible. Soif we do get past that, then we're going tobe on our happy path. And we're going to returnout the result of a calculation and then anil error up in the calling function, we'regoing to have the standard test to see iferror is not equal to nil. If it isn't equalto nil, then we're going to process that error,because now we've got something we're goingto have to deal with. And then again, we don'thave an else block here, we just continuemoving on, we're going to make sure our errorhandling logic either recovers from the erroror exits onto the function. And that way,we can keep our main thread of execution leftjustified. So our main thread of executionhere is we're going to call this divide function,and then we're going to print out the result.So any error handling should not force themain line of execution to be indented. Sonow if we run this, we see that I forgot toinitialize my error parameter. So let's goahead and add that. And this is somethingelse I should talk about. When we're receivingmultiple values out of a function call, weactually have a common delimited list of thosereturn values. So this D parameter is goingto match up to this float 64. And this eerrparameter is going to match up to this errorparameter here. So now if I run, everythingshould work. And we see that we now get anerror cannot divide by zero. So our main functiondoesn't explode on us, we actually have somethingthat we can work with. But if we put in avalid value, then we're on that other pathof execution, and we get the return valueback out. Now, so far, we've been treatingfunctions as this special thing, because we'realways using this func keyword, we're declaringthese at the top level of our application,we're working with them. But functions andgo are actually more powerful than that. Becausefunctions themselves can be treated as types,they can be passed around as variables, theycan be passed as arguments into functions,you can get them as return values, prettymuch anything you can do with any other typeyou can do with functions. So let's take alook at that a little bit. So in this example,I'm actually declaring a function on the fly.And this is called an anonymous function.Now we're going to continue to explore thisover the next couple of minutes. But thisis the simplest example I can come up with.So notice that I'm starting with the funckeyword, I've got the params. For the parameters,I've got the opening and closing curly brace,but I don't have the function name here. Sowhen you're doing this, this is what's calledan anonymous function. And this is the basicstructure of a function when you're not workingwith functions in this traditional scope.But instead you're working with functionsas types. So inside of my function body, I'mprinting out the message Hello, go. And thenI've got accompanying my closing curly brace,these params here. Now these params here arebasically going to invoke this function. Sothis is an immediately invoked function, I'mdefining it and executing it at exactly thesame time. So when I run this, and actuallydoes execute that function, and we get thevalue Hello, go printed out. If I don't havethese friends, then the compiler is a littleconfused. It doesn't know what to do withthis function. It's just defined, but it'snever used anywhere. So fails a compilationcheck. But if I do invoke that function immediately,then I get this behavior here. Now why wouldyou use an anonymous function like this, Iactually have no idea why you would use ananonymous function like this. I mean, therecan be situations where you can declare variablesinside of here. So if I declare a messagevariable here, and I set that equal to thisstring, and then print that out, that canbe valuable because you're actually generatingan isolated scope. So this message variableis not going to be available in the main functionis only going to be inside of this anonymousfunction here. Now another place that youmight use this is if we've got a for loop.So if I start up a simple for loop, and I'lljust count up to five, and increment by one,let's see if I can do this on the fly here.And then I come in here and I actually printthe value of i out, I'll get rid of this messagehere and I'll print out I, you're going toget a little bit of strange behavior. If Irun this, this works, okay. But as we startgetting into asynchronous code, things aregoing to start behaving a little bit oddly.So we do have access to this AI variable,because we're in the scope and the main function.And so inner functions can actually take advantageof variables that are in the outer scope.But the problem is, if this function is actuallyexecuting asynchronously, then this countervariable is going to keep going. And we mayactually have odd behavior here. So the bestpractice is actually to provide a variableinside of here and actually pass that AI variable.And what that's going to do is we're not goingto be reading from the outer scope anymore.We're going to be passing that into the functionexecute And that way, even if this is runningasynchronously, we're going to print out thevalue correctly. Now, this works correctlyin the playground, the way that we have thisright now, because we actually aren't doinganything asynchronously. This is all synchronousexecution. And so we are safe to use thisouter counter. But it's not good practiceto do that. Instead, it is best practice topass in that kind of variable. If you needthat in your inner function. That way, changesin the outer scope aren't reflected on theinner scope. Now taking this a little bitfarther, we can work with functions as variables,like I said before, so in this case, I'vedeclared an anonymous function, and I've assignedit to this variable F. And then I can executef by just invoking it like any other function.So if I call that, we see that we do printhello, go out. So now that I've got this functiondefined as a variable, it's free to pass aroundmy application. Now, you might ask yourself,what is the signature for this function. Solet's go ahead and go through that. So ifI get rid of the short syntax and extend thisout a little bit, then I'm going to startwith the var keyword. And since this is avery simple function, the type is just likethis, we have the func keyword, and then anopen and close parenthesis. So the parameteris normally going here, I don't have any parametershere, I don't have any return types. So thetype signature for this variable is simplyfunc with two params. There. So if I run this,that works just fine. Now we can go a littlebit more complicated. And I'll drop an exampleof that in just to show you how that's goingto look. So in this case, I'm declaring afunction signature for a divide function,that's going to take in two floats, and it'sgoing to return a float and an error. Andyou see, this is the syntax for that. So wepass our parameter types in here. And thenwe have the return types in params. As well,if you have just a single return type, thenyou don't need these params, we could justput the type there like that. But we do havethat error type this coming back. So we doneed to include that. And then when I initializethat variable, I'm going to set it equal toan anonymous function that takes a and d.And this is exactly the same divide functionthat we had before. And I can call that exactlylike we had before. So when I run this, ithas exactly the same behavior as the lastexample we did with the divide function. Butnow we have the divide function declared asa variable. And we're working with it exactlythe same way as when we declared it as a function.Now, the difference between this and whenwe had the divide function declared globally,is if I try and call it up here and run theapplication, notice that I get an error becausein this case, the function divide hasn't beendeclared yet, because it's declared as a variable.And so I can't work with it yet. So that'sjust something to be aware of. If you're goingto be working with functions as variableslike this, make sure that they're definedbefore you actually try and execute them.Okay, the last thing that I want to talk aboutwith functions today is working with whatare called methods. And there's a couple ofthings to talk about with those. So let mejust drop in an example that shows that. Andwe can walk through this and then see whatit's going to do. So in this example, I'vegot a struct called greeter that greeter structhas two fields greeting and name. And thenI've got this method on it. And we'll comeback to this in a second. So in my main function,I'm declaring a greeter struct, and then I'mcalling this function preceding it with thestruct that I have here. And this is how we'regoing to do method invocation. So we callthe method just like we were accessing a field,except for we have the params here where wecan pass some arguments in. Now my methoddeclaration down here looks a lot like a functionexcept for it's got this odd bit right here.And this is what makes this function intoa method. So a method is basically just afunction that's executing in unknown context,and unknown context. And go is any type. Nowit's very common that we can use structs.But you can use any type. So we can make atype for an integer. So maybe we have a typefor an integer called counter. And then wecan add methods on to that counter type, andwork with those. So when we declare that method,we're actually going to get access to thattype right here in this part. So what's goingto happen when we call the greet method isthe greet method is going to get a copy ofthe greeter object. And that's going to begiven the name g in the context of this method.So then when we print out, we can access thefields on that greeter object. So we can printout the greeting and the name. So when wego ahead and run this, we see that we getHello go printed out. And that's the basicsof a method. So methods are basically thesame as functions, they just have this littlebit of syntactic sugar, that's providing acontext that that function is executing in.And when we do that, we call that a method.Now when we use this syntax right here, noticethat we're specifying greeter as a value type,we don't have a pointer here. So this is what'scalled a value receiver. The received objectin this greet method is the value greeter.So what that means is just like any othertime that we're working with values, we aregetting a copy of the struct, we're not actuallygoing to get the struct itself. So if I changethe value of the Name field here, and thenI print the Name field out of here, so I saythe new name is and then I print the Namefield out, then it's no big surprise thateven though I assigned an empty string tothe Name field here, up here in the main function,it didn't have any effect. Because down herein this method, we're operating on a copyof the greeter object. We're not operatingon the greeter object itself. So again, that'svery valuable if you want your methods tobe able to access the data of their parenttype without being able to manipulate Justkeep in mind there is a cost with that. Soif there's greeter object was a very largestruck, then we would be creating a copy ofthat struct every time we invoke this method.Now, as you might expect, there's anotheroption that we have here. And that is to passin what's called a pointer receiver. So ifwe make this a pointer, and run the application,again, now we're actually able to manipulatethat underlying data. So we're going to printout Hello ghosts. So the method operates inexactly the same way. And we don't have tochange the format here, because we do havethat implicit dereferencing of pointers that'sworking for us. But now when I change thevalue of the Name field, and print the Namefield out up here, we do in fact, see thatwe've been able to reassign the value of thatfield. Okay, so that covers working with functionsin the go language. Let's go into a summaryand review what we've talked about. In thisvideo, we talked about functions and how touse them in the go language. And we startedout by talking about the basic syntax of afunction, and we saw that this is about assimple of a function as we can get. So westart with the func keyword, we have a namefor that function. And again, if that firstletter is uppercase, then that function isgoing to be published and allowed to be executedfrom outside of the package. But with a lowercasefirst letter, it's going to be kept internalto the package, then we follow with a matchset of parenthesis, and then we have an openand closed curly brace. Now the open curlybrace has to be on the same line as the funckeyword. And the closed curly brace has tobe on its own line after the final statementof the function, then we moved on to talkabout parameters and parameters allow us topass data into the function to influence howthat function executes, basically providingsome variables for the function that are passedin from the outside. So we talked about howparameters are passed in as a common delimitedlist of the name of the parameter and thetype of the parameter. So we see here we'repassing into the Foo function, two parameters,the bar parameter that says type string, andthe bass parameter that's of type integerparameters of the same type can be comma delimited.And the type can be listed at the end there.So in this case, we're passing in bar andBabs as parameters. And both of those aregoing to be of type integer.When pointers are passed in the function containsthe value in the caller. So by default, we'regoing to be passing in the values themselves.And so the go runtime is going to be copyingthat data and passing it into the function.So any changes that are made inside of thefunction aren't going to be reflected in thecolor scope. However, if you pass in a pointer,then you are going to be able to manipulatethe value inside of your function. And thatwill have an effect in the calling scope.So the only exception to this rule is whenyou're working with slices and maps, sincethey work with internal pointers, any changesinside of the function to the underlying datais always going to be reflected inside ofthe calling scope. We also talked about howyou can use variadic parameters to send alist of the same types in, so it must be thelast parameter in the parameter list. It'sreceived inside of the function as a slice.And you see an example of the syntax righthere. So we've got the function foo. It hasone parameter bar that's of type string, andthen a parameter Baz, that's a very atticparameter of integers. So inside of this foodfunction, we're going to have a slice calledBaz. And that's going to contain all of theintegers that have been passed in. Once yourfunction finishes doing its work, a lot oftimes we want it to return a value back out.And in order to get that information backout, we're going to use return values. Soif you have a single return value, all youneed to do is list the type. So in this case,our foo function needs to return an integer,we can also specify multiple return values.So if we're going to do that, we need to putparentheses around the types that we're goingto be returning. So in this example, we'regoing to be returning an integer and an error.And this is a very common pattern that you'regoing to see in NGO applications where we'regoing to have our functions return the intendedvalue, and then an error value that's goingto let the caller know if anything went wrong.That way, the function itself doesn't haveto determine whether the application needsto panic, or execution can't continue. Itjust knows it wasn't able to do what it wasasked to do. And then it can delegate whatthat error means to the application to thecalling function, you can also use named returnvalues. So when you do that, instead of justproviding the types in the return last, you'regoing to provide the name of that return value.So when you do that, the runtime is goingto initialize that value for you to the zerovalue for that variable. And when you usethe return keyword, all you need to do isenter return on its own go is going to findthe current value of those returned variables.And that's what's going to be returned outof your function. Another special behaviorof go is you can actually return the addressesand local variables as return values, andthose are going to be treated properly. Sowhen you do that those variables are automaticallypromoted from local memory or stack memoryup into shared memory or heap memory. So youdon't have to worry about those values beingcleared out as the function stack memory isreclaimed. We then started talking about anonymousfunctions. And we talked about a couple ofdifferent uses for those. So we have thisimmediately invoked function, which reallyisn't used too often in the go language, butit's as simple of an anonymous function asI could get. The only potential advantagethat you have here is you can create an innerscope. So local variables that are createdinside of this anonymous function aren't goingto be available outside, but I haven't seenthat very often. It's not going to be veryoften that you're going need to use this kindof a function, then we also talked about howwe can take that anonymous function and actuallyassign that to a variable. So in this example,we've got the variable a assigned to the valueof that function. And then we can invoke thea function just like any other function. Theonly difference between this and the normaldeclaration of a function is that the a functioncan only be invoked after it's been declared.So when you declare a function using the traditionalsyntax, it's actually declared at the timethat the package is initialized. And so it'salways available to you. When you're usingthis syntax, you have to make sure that ais initialized before you can call it extendingon that discussion about the ability to assignfunctions to variables, functions, or types,just like any other type in go language, anytimeyou can use a primitive or a slice or an arrayor a map, you can use a function. So you canassign them to variables, you can use themas arguments, they can even be returned valuesfrom functions, then we also talked abouthow since a function is a type, we have tohave the ability to create a type signature.So if you're declaring anonymous functions,it's often most convenient just to use thatcolon equals syntax and declare your anonymousfunction and the type is going to be inferred.However, if you're using a function as a parameterto another function, or the return value froma function, then you're going to need to specifythat type signature. So we see an examplehere. In this case, we've got the definitionof a function f. And that function is goingto take three parameters, two strings, andan integer. And then it's going to returnan integer and an error type. So it's basicallythe same as when you're declaring a functionnormally, the only difference is we don'thave the names for those variables. Becausethose names will be provided when we actuallyimplement the function, we just need to knowthe types that are coming in, and the typesthat are coming out at this point. The lastthing we talked about were methods, and howmethod is a special type of function thatexecutes in the context of a type. Now a typedoesn't have to be a struct. Although thatdefinitely is a very common use case for methods,you can actually attach a method to any customtype. So you can create a type of an integer,and then you can add methods on to that integer.When we create a method, we're going to usea modified version of the basic function syntax.So before the name of the function, and afterthe func keyword, we're going to provide anotherset of parentheses, we're going to providea local name for the type that's going toreceive that method. And then we're goingto follow that with that method type. Now,that variable is what's called the receiverfor the method. So in this case, our G variableis what's called a value receiver, which meanswe're going to get a copy of that greeterobject. And that's going to be passed intothe greet method. However, we can also usewhat are called pointer receivers. So by addingan asterisk in front of that greeter type,the method is going to change. So insteadof passing a copy of the greeter, we're goingto get a pointer to the greeter object inthere. And then any manipulations we maketo the greeter object in the greet methodare going to be reflected throughout yourapplication. Now, that's very powerful ifyou need the method to be able to manipulatethe state of the object. It's also much moreefficient if you're dealing with large structures,because instead of copying the entire structure,it only has to copy a pointer. And that'snormally a much more efficient operation,I want to talk about one of the coolest featuresof the go language. And that is interfaces.Now I know that interfaces are normally consideredpretty humble features, and they sit in thebackground. It's much more fun to talk aboutgo routines and channels, especially whenyou're learning to go language. But I wouldargue that the way interfaces are implementedin the go language are potentially one ofthe reasons why go applications tend to beas maintainable and scalable as they haveproven to be. So we're going to start thisconversation like we start every conversationby introducing the basics. So we'll learnwhat an interface is and how to use them inthe language itself. Then we'll move on todiscuss how to compose interfaces together.Now, just like in other high level languages,such as Java, or C sharp, we can actuallymake interfaces of interfaces. And we'll talkabout how to do that, and why that's a verygood thing to do when you're writing yourapplications. Then we'll talk about type conversion.Now, we've touched on this a little bit beforein a previous video. But when we talk aboutinterfaces, things changed a little bit, andit's worth revisiting the topic. Along theway, we're going to talk about the empty interface,which is a very useful general construct thatwe're going to deal with in our programming.We'll also revisit type switches, which we'vetalked about before, and we'll revisit themin the context of our interface discussion.Then we'll talk about how to implement interfaces.And there's actually two different ways thatyou can do that. One is by implementing withvalue type, and one is by implementing witha pointer. And we'll talk about some of thesubtle differences that you're going to runinto as you implement interfaces with thesetwo different types. And then finally, we'regoing to talk about some best practices thathave been discovered over the last few yearsof working with the go language about howto use interfaces in your actual productionapplications. Okay, so let's get started bylearning the basics of using interfaces ingo. So to start our discussion about interfaces,I'm going to actually build our first applicationup a piece at a time now often I just dropin code and talk about it. But I want to takethis one step at a time so that we're workingtogether and understanding what's going on.So the first thing that we're going to dois we're going to introduce our first interface.So interfaces are a type, just like structsor type aliases. So we're going to start withthe type keyword, then we're going to enterthe name of our interface. And then the typethat we're creating is a type interface. Andthen we're going to surround the definitionof this interface with curly braces, justlike we do when we're defining a struct. Nowwith a struct, we would add in here, the datathat we want that struct to hold on to becausestructs are ultimately data containers. Andso that's how we work with them. interfacesdon't describe data, interfaces describe behaviors.So instead of entering a bunch of data typesthat we're going to be storing inside of awriter interface here, we're actually goingto be storing method definitions. So I wantto create a write method here. And this isactually an interface from the IO package,we're just going to be working with it hereas if we created it. But this is exactly thesame interface that you would find in theIO package under the writer interface. Sothis method is going to accept a slice ofbytes. And then it's going to return an integerand an error. Now on the writer interface,the way this works is anything that implementsthis interface is going to take in that sliceof bytes, write it to something that somethingmight be the console, it might be a TCP connection,it might be the file system, we don't know,we just know that we're writing a slice ofbytes to something. And then the integer anderror that get returned, of course, the erroris there in case something goes wrong withthe write operation. And the integer is normallythe number of bytes written. So now that wehave the interface defined, let's go aheadand implement it. So we're going to implementthis with a console writer implementation,and that'll be a struct. And that's all weneed to do with the struct definition. Now,if you come from another language, you mightbe looking for an implements keyword or somethinglike that. Well, in go, we don't actuallyexplicitly implement interfaces, we're goingto implicitly implement the interface. Andwe're going to do that by actually creatinga method on our console writer that has thesignature of a writer interface. So let mejust drop that in. Because if I try and typeall this out, I will screw it up, and thenI'll have bugs that I have to go through.So it's much easier just to drop it in. Butnotice what I've done here, I've got a methodon my console writer called write. So it'sgot the same name as my writer interface,it's accepting a slice of bytes, and it'sreturning an integer and an error. Now theimplementation is whatever I want it to be.Now, in this case, all I'm going to do isconvert that byte slice into a string andprinted onto the console to keep things easyin the playground. But I can have my writerdo whatever I want.So what's the value of doing this? Well, thevalue of doing this is up in my main method,I can actually create a variable that's oftype writer. And let me just drop that codein and format it and set that equal to a consolewriter instance. So the W variable here isholding a writer, which is something thatimplements the writer interface. I don't actuallyknow the concrete type, though. So when Icall the write method down here on line nine,I know how to call that because that's definedby the interface. But I don't actually knowin my main function, what's being writtento, that's the responsibility of the actualimplementation. So I could replace this witha TCP writer, I could replace it with a filewriter, I could replace it with any otherkind of writer. And so I get what's calleda polymorphic behavior. Why nine doesn't carewhat it's writing to, I specify that behaviorbefore that. But then anything that's goingto use this w object just knows that it canwrite to it. And so it can take advantageof that behavior. So if I go ahead and runthis application, you see that I do get alogo printed out to the console, just likeI would expect. So the key takeaway here aswe're learning the basics of interfaces, isthis concept of implicit implementation. Andso what that means, for example, is if youneed to wrap a concrete type, and somebodyhasn't published an interface, you can actuallycreate an interface that their type implements.So we did it the other way, we created aninterface, and then we created a concretetype that implemented it. But there's nothingto say we can't go the other way around. Wecould, for example, go to if I travel to golang.org, and go into packages, this is actuallysomething that I just ran into, in order totest SQL database connections. So if I comedown to the database package, and go intothe SQL package, if we look at this, noticethat the DB type is a struct. So we don'thave an interface here. So if our go applicationis talking to a SQL database, we've got concretetypes all over the place. So for our transactions,we're interacting with this DB object. everythingthat we're doing sending SQL statements makingqueries are all through this concrete DB object.So how do I test that without a database?Well, the way that you test that without adatabase is you actually create an interfacethat replicates this method signature, andthe DB object from the SQL package will automaticallyimplement it, so I don't have to worry aboutcreating inner phases at design time if Idon't need them myself, because consumersof my library or whatever I'm creating canalways create interfaces later. And theirinterfaces can be shaped to exactly what theyneed for their application. Now another thingthat I want to talk about before I move onhere is a naming convention. Now obviously,the name of the interface should representwhat that interface is going to be doing foryou. And there is one special case, if you'vegot single method interfaces, which are verycommon in the go language, then the conventionis to name the interface with the method nameplus er. So if we're defining an interface,like we have here with the right method, thenthe interface name should be writer, if we'regoing to create an interface with a read methodon it, then the interface name should be areader. Now if you got more than one methodin the interface, things can get a littlebit more challenging. But at the end of theday, you should name your interface by whatit does. And in the case of a single methodinterface, just add er onto the end of themethod name. Okay, now, in this example, weuse the struct, which is probably one of themost common ways to implement interfaces,but you don't need to any type that can havea method associated with it can implementan interface. And as we've talked about before,any type can have methods associated withit. So in order to demonstrate that, let mejust drop in this example here. Now in line16, through 18, I've defined a new interfacecalled incrementer. And that increment isgoing to be a method that only returns aninteger, so it's going to increment something.So whatever we're going to implement thisthing with, is going to increment values.So down here on line 20, I defined the typealias for an integer called an int counter.And then I added a method to that custom typeon lines 22 through 25. And that's going tobe my implementation for the incrementer interface.So the method name is called increment, andit's going to return an integer. Now, in thiscase, look at what I'm doing, I'm actuallyincrementing. The type itself, since I'vegot a type alias for an integer, it's a number,so I can go ahead and increment that. Andthen I'm going to return it as the resultof this method call.So I've actually got a type defined on aninteger, and the integer itself is storingthe data that the method is using. So up herein my main function, I'm going to go aheadand create that integer counter. And I haveto cast an integer to an encounter. In orderto do that. That's what I'm doing here online eight. And then I create my incrementerand assign that to a pointer of the my nsobject. And we'll talk about why that hasto be a pointer toward the end of this video.And then I'm just going to loop from zeroto nine. And I'm going to print out the valueof the increment method every time I callit. So if I go ahead and run this, I see nobig surprise, I get the values one through10 printed out. So what's the takeaway here?Well, you don't have to use structs. To implementinterfaces, you can use any kind of customtype. Now I couldn't add a method directlyto the entity type. Because the event typeisn't under my control that's defined in anotherpackage. It's a matter of fact, that's a primitivetype, and you can't modify it. But any typethat I do have control over that I can create,I can add methods to it. And if I can addmethods to it, I can implement interfaceswith it. Now the next thing that I want totalk about is how to compose interfaces together.Because this is another very powerful conceptin the go language, and is one of the keysto scalability, because if you remember Imentioned a little while ago, single methodinterfaces are very common and very powerfulin the language, because they define a veryspecific behavior. But by having a singlemethod, they don't have a lot of opinions.And so they can be implemented in a lot ofways. So for example, the IO dot writer interfaceis one of the most common interfaces in theentire go language, because all it does istalk about how to write two things. And wewrite two things all the time. So by takingas little opinion as possible, we actuallymake the interface very, very powerful andvery, very useful. So let me go ahead andpaste this example here. Because what happensif we need more than one method, but we candecompose the interfaces down. So in thiscase, I've created an interface that's composedof other interfaces. So I've got my writerinterface that we started the video with.And then I've added this closer interfacethat just hasa closed method on it, and returns an errorjust in case something happened when we triedto call this method. Now the writer closerinterface is actually composed of the writerinterface and the closer interface. And thisis done exactly the same way that you do embeddingwith structs. We're just embedding interfaceswithin other interfaces. So the writer closeris going to be implemented. If an object hasthis method on it, and this method on it,then we can treat that as a writer closer.So as an example, I've created this structhere, a buffered writer closer. Now, I'm notsaying that this is an efficient way of doingthings. This is just an example of how youmight use this writer closer interface ina way that runs in the playground easily.So in this case, I've got my write methodthat I'm going to be implementing. And whatI decided to do is I'm going to write outwhatever gets sent into the buffered writercloser. I'm going to print that out to theconsole in eight increments. So that's whatall this code is doing, when you pass datainto the write method, it's going to storethat in this internal buffer that the structuredefines. And then as long as the buffer hasmore than eight characters, it's going togo ahead and write that out. But it won'twrite anything out if it's got less than eightcharacters. So we're basically buffering thedata that we're sending in. And then downhere in the close method, I've got to implementthat too. And so what we're gonna do thereis we're going to flush the rest of the buffer.So I'm pulling the next eight characters out.And I'm going to write that out to the console.And keep doing that until the buffer is empty.Okay, up here in the main method, I simplycreate a writer closer variable, and definethat using the new buffered writer closerfunction. And just to show you that I didn'ttalk about it, that's down here at the bottom,that's just a constructor function that'sreturning a pointer to a buffered writer closer.And I need to do that because I need to initializethis internal buffer to a new buffer. So Ihave a constructor method there just to makesure that everything has been initializedproperly. So if I come back up to the mainfunction, and look at that, then I'm goingto call the right method. And I'm convertingthe string hello youtube listeners, this isa test over to a byte slice, because that'swhat the right method expects. And then I'mgoing to call the close method. So if I goahead and run this, you see that I get themessage printed out to the console in eightcharacter chunks. And eventually I get allthis printed out. But if I comment out thislast method, call here, you see that I don'tget the a test part of the string, becausethat's actually a partial. And so we didn'tget that full eight characters that's requiredfor the right method to print it out. Andso I didn't actually flush the buffer. Okay.So I know that maybe a little bit of a complicatedexample, to show a fairly simple thing. ButI just wanted to show you this is how youcan compose interfaces together. And as longas you implement all of the methods on theembedded interfaces, then you actually implementthe composed interface as well. The next examplethat I want to talk about is how we can dotype conversion. So I'm going to go aheadand replace my main function here with thiscode here, get rid of the extra curly brace,actually, I'm going to get rid of this too,I guess I pulled in the whole function signature.And then I made a little bit of a change downhere in line 13. So lines nine through 11are our original implementation, where we'recreating the new buffered writer closer orwriting a string out, and then we're callingthe close method on it. But on line 13, I'mactually doing a type conversion. So usingthis syntax here, where I've got an object,dot, and then in parentheses, I've got a typethat I'm going to try and convert this variableto. And then I can assign that to a variablesuch as this PwC variable right here. Now,if that succeeds, then everything's fine.And I can go ahead and work with it. Now,there's nothing useful I can do with this.But I'm just printing out the variable, becauseI have to use the variables and go. So I'mjust going to go ahead and print that out.So if I run this, I see I get exactly thesame output I had before. But now I get thememory address of this buffered writer closer,so that tape conversion succeeded. And thereforeI can work with this no longer as a writercloser, but as a buffered writer closer. Sofor example, if I needed to access the bufferdirectly, then I would be able to do thatnow. Whereas with the writer closer, it'snot aware of the internal fields of a specificimplementation. And so I wouldn't have accessto that data. Now there is a problem. However,if I import the IO package, and try and convertthis to a type that it doesn't implement.So for example, if I try and convert thisover to an IO reader, which is another interface,and that IO reader interface requires a readmethod on it. So if I try that now, let'sgo ahead and run that. And we see here weput the application into a state that it can'tmanage. And so what it does is whatever goodgo program does, when it can't figure outwhat to do it panics. And the panic messagesinterface conversion, it can't figure outhow to cast a buffered writer closer intoan IO reader. And so it's going to fail onus. Now, it does give us some useful informationabout why it couldn't do that. It says it'smissing a method read. And then it's gonnagive us a stack trace letting us know wherethat error occurred. Now, this isn't reallygreat, because sometimes we need to try andconvert an interface into something else.And we're not sure if it's going to work ornot. So it's not going to be good for ourapplication to be panicking all the time,because then we're going to have recoversand we're going to be using that as a primarycontrol flow concept. And we want to avoidthat in the go language, because panickingis pretty expensive. So we need another wayaround it. Well, we just so happen have anotherway around it. And so I'm going to show youthat. All we need to do let me rename thisvariable, because PwC doesn't make sense fora reader anymore. I'm going to paste thiscode in. So I'm going to now try and castit to a variable called R. I'm going to dothe same type conversion, but now I'm usingthis comma, okay, syntax. So we've seen thisbefore, when we were trying to pull a valueout of a map and we weren't sure if it wasthere or not. Well, we have the same abilitywith type conversion. If we add a comma, okay,this is going to be a Boolean result. Andthen we can test against that to see if wecan work with it. So if the conversion succeeds,then we're going to get an okay value backout. If the conversion fails, then we're Goingto get the zero value of whatever type wewere trying to convert to. So an IO readeris an interface. And so it's zero value isgoing to be nil. So if we go ahead and runthis now, we see that our conversion failed,but our application didn't crash. If we switchthis back to a pointer to a buffered writercloser, let me go ahead and drop that in.And we run that we got to drop out our packagehere that we're no longer using. And thenwe see that we're back to having things successfullyconverted. And we could work with that howeverwe needed to. So this is really importantto be aware of, especially if you're not sureif you're going to get a pointer or a valuetype. So for example, it would be really easyto write this and have a problem because weimplemented the interface with a pointer,not with the value itself. And so we can'tactually do the conversion to the underlyingvalue type. So let's go ahead and run thisagain, we see that that all works. And onemore thing I want to show you let me justpull this error back up again, you'll noticethat the reason this type assertion failedis because buffered writer closer does notimplement writer closer now that might seema little strange to you, because our bufferedwriter closer has a write method, and it hasa closed method, and they have the right signature.So for some reason this works when I askedit to convert it to a pointer, but it doesn'twork when I asked it to convert it to theunderlying value. Now we'll come back in justa second and talk about why that happens.But I want to finish our discussion of typeconversions first. So stay tuned, and we'lltalk about why that works the way it does.So the next thing that I want to talk aboutis something called the empty interface. Nowthe empty interface is exactly that. It'san interface that has no methods on it. Andwe describe that using this syntax here. Now,this isn't a special symbol, this is justan interface that we're defining on the fly,and we don't have any methods on it. So it'scalled the empty interface. But there's nothingspecial about it, we could create this asthe empty interface exactly the same way byjust deleting the method out. And that's anempty interface now, so you see it like thisall the time. Just be aware, there's nothingspecial about this, it's just an interfaceto find on the fly that has no methods onit. Now the nice thing about the empty interfaceis everything can be cast into an object thathas no methods on it even primitives because,well, an integer has no methods. And so itcan be cast to the empty interface. And sothis can be very useful in situations whereyou've got multiple things that you need tobe working with. But they aren't tight compatiblewith one another. And you need to apply somelogic later to figure out exactly what youreceived. But we do have a problem with theempty interface. Because we now have thismy object variable that's defined as an emptyinterface, we can't actually do anything withit, because my object has no methods thatit exposes because it's an empty interface.So in order to do anything useful with a variablethat has the type of an empty interface, you'regoing to need to do either type conversion,or you're going to need to start using thereflect package in order to figure out whatkind of an object you're dealing with. Soin this case, on line 10, I'm actually tryingto type cast into a writer closer, and I'musing the comma okay syntax to see if thatworked. If it does, then go ahead and callthe write and close methods like I saw before.And then I've got this other type conversionthat I've done that before just to keep thingsconsistent. So if I run this, we see thatI forgot to re import the IO package, letme go ahead and pull that back in.And we see that everything works as normal.So the empty interface is very common. Butjust keep in mind, it's almost always goingto be an intermediate step. And you're goingto define a variable of the type empty interface.And then you're gonna have to figure out exactlywhat you receive before you can do anythinguseful with it. The last thing that I wantto talk about in the context of type conversionsare type switches. So I want to revisit thatconversation from a few videos ago. And justto show you, we can do something like this.So in line eight, I've got a variable i that'sdefined as the empty interface, and I'm settingit equal to the integer zero. And then I'mgoing to use this switch block here, and I'mgoing to use this syntax. So I've got my variablename I, and then I'm going to use this dotand inside of prims, I'm going to put type.And so what this is called is this is calleda type switch. So each of the cases in thistype of switch are actually going to be adata type. So in this case, I'm looking tosee if I've got an integer or a string, orI've got a default case, which is going tobe handled by our application, just sayingit doesn't know what AI is. So let's go aheadand run this, I properly identify as an integer.So we execute this case here. If I put paramsaround this, then of course, I is now goingto contain a string. And so if I run that,again, it identifies it as a string. And ifI change this, once again, maybe we can makethis a Boolean, gotta spell true correctly,and run that then it has no idea what it is.So this is commonly paired with the emptyinterface in order to list out the types thatyou're expecting to receive. And then youwould add in the logic of how to process thosedifferent types. Now, I promised you thatwe would come back and talk about that weirdtype conversion behavior, where we could convertour writer closer into a pointer to a bufferedwriter closer but Couldn't convert it intothe value itself. So now I want to go throughand have that conversation about why thathappened. So let me just drop this code in.This is a much simpler implementation thanwhat we had before. I've actually not reallyimplemented these methods anymore, in orderto keep things as clean as possible for youto see. So all I'm doing is I'm going to createa my writer closer, and that's down here asmy writer closer struct with nil implementationsfor the methods. But I do have the methodsimplemented. So I can create this object asa writer closer, and then I'm just printingout the value of the variable just so we havesome use for that variable. So the go runtimewill actually compile and run this, and theinterface for the writer closers to find exactlythe way we had before. So if I run this, everythingworks out just fine. However, what happensif I change the receiver of one of these methodsto a pointer? Well, if I run this, now, Iget an error. And the reason that I get thaterror is it can no longer convert my writercloser into a writer closer interface. Andit gives us an interesting message here, itsays my writer closer does not implement writercloser, the right method has a pointer receiver.And this is the key to understand what happenedwith this. So when we define types, and weassign methods to them, each one of thosetypes has what's called a method set. Nowwhen you're working with the types directly,the method set is all of the methods regardlessof the receiver types associated with thattype. With interfaces, however, things changea little bit when I implement an interfacewith the concrete value. So notice here I'mcreating my writer closer, I'm not takingthe address of my writer closer, I'm usingmy writer closer directly. So WC is definedas holding the value my writer closer. Sothe method set for a value when we're talkingin the context of an interface is any methodthat has a value as the receiver. So the reasonwe're not implementing writer closer is becausea write method no longer has a value receiver,it's going to point a receiver. And so itsmethod set is incomplete. And now we can fixthis by using the address of operator andrunning again. And notice now everything works.And the reason for that is the method setfor a pointer is the sum of all of the valuereceiver methods, and all of the pointer receivermethods. So let's go through that. Again,when I'm implementing an interface, if I usea value type, the methods that implement theinterface have to all have value receivers.If I'm implementing the interface with a pointer,then I just have to have the methods there,regardless of the receiver type. So the methodset for a value type is the set of all methodsthat have value receivers. But the methodset for a pointer type is all of the methodswith value receivers, as well as all of themethods with pointer receivers. So there'sa couple of ways that we could fix this. Nowin this case, we don't need access to theunderlying data. So we could just go backto a value receiver. And then this is goingto work just fine. This is actually the initialexample we had. If we have one method that'sgoing to appoint a receiver, however, we'regoing to need to switch that over to a pointertype. And notice I can actually remove this.And it continues to work. Or I can make bothof these pointer receivers. And this continuesto work as well. So this is an important conceptwhen you're implementing your own interfaces.If any of the methods require a pointer receiver,you're going to have to implement that interfacewith a pointer. If not, though, if all ofthe methods except value types, then you cango ahead and use a value type if that's whatyou want. But you could also use a pointer.Okay, the last thing that I want to talk aboutare some best practices when using interfacesin your own go applications. So let's takea look at those. Okay, when we're workingwith interfaces, there's a couple of rulesand guidelines that I'd like you to keep inmind. And these have been developed over thelast few years by the NGO community, and aregenerally accepted as some of the best waysto use interfaces, if it's practical in yourapplications. The first is prefer many smallinterfaces versus large monolithic ones. Now,if you need large, monolithic ones, that'sfine, go ahead and compose smaller interfacestogether to make those but the smaller youcan make your interfaces, the more usefuland powerful they're going to be. And that'snot actually unique to go. No matter whatlanguage you're working in interfaces there.They're generally having many smaller interfacesis preferable in the long run to having afew monolithic ones. Now, some examples thatare in the go standard library are the IOdot writer interface, the IO dot reader interfacein the empty interface. Now these are arguablythree of the most powerful interfaces in theentire language. And if you think about it,writer has one method reader has one methodand the empty interface has zero methods.So it's interesting support to the argumentthat smaller interfaces are better that someof the most powerful interfaces in the languagecontain one or zero methods on them. Now whenyou're working with interfaces, if you'recoming from a language that has explicitlyimplemented interfaces, You're going to bevery tempted to create interfaces and exportthose. So here's the guidance for that ifyou don't need to export the interface yourself,so if you don't have any particular reasonto do it, go ahead and don't. So there aresome good examples of why you would want todo that. But often, it's perfectly acceptableto export the concrete type.I'll take as an example, the database slashSQL package that we looked at earlier in thevideo, where we saw that the DB object wasexported as a concrete struct. And it hadall sorts of methods that pointed to otherconcrete structs. So you can't directly mockthat out for testing right out of the box.However, by not exporting an interface, itallows you as the consumer of that structto define your own interface that you canuse for testing. And the beauty of that is,if you don't use every method on the DB object,your interface doesn't have to have everymethod on it, you can just expose the methodsthat you need, however, do export interfacesfor types that you will be using. So if you'regoing to pull a value in, go ahead and acceptan interface instead of a concrete type, ifat all possible. So this is going to be almostexactly backwards from how other languagesconsider interfaces. And the reason is thatwhole idea about implicitly implementing interfacesinstead of explicitly doing it. So if youwere working in Java or C sharp, you couldnot do this, because you have to define theinterface before you implement the interface,because they're explicitly implemented. Butsince go has implicit implementation, youcan go ahead and defer the creation of theinterfaces until exactly when you need them.So if you're creating a library that otherpeople are going to consume, you can definethe interfaces that you accept. And then theycan provide whatever implementations thatthey want. Now, if your library has reasonabledefaults, then you could export those concretetypes as well. But make sure that you're acceptinginterfaces whenever possible. And that's whatthis third point is talking about. designyour functions and methods to receive interfaceswhenever possible. Now, that's not alwayspossible. If you need access to the underlyingdata fields, then certainly taking the concretetypes. But if you're accepting behavior providers,then go ahead and try and accept those asinterface types instead of the concrete types.Okay, so that covers what I want to talk aboutwith interfaces. today. Let's go into a summaryand review what we've talked about. In thisvideo, we've talked about interfaces and howto use them in the go language, we startedwith a discussion of the basics of interfaces,so how to create them and how to implementthem. And we ended up with code that lookssomething like this. So we're defining aninterface as a type. So we're going to startwith the type keyword, the name of the interface,and then the keyword interface. And then insideof curly braces, things are going to be alittle bit different than if we were defininga struct. For example, if we were defininga struct, we would put data fields insideof the curly braces, because we're definingthe data that that structure is holding. Withinterfaces. we're defining behaviors, however,so instead of adding data fields, we're gonnaadd method signatures. So we see here on thisexample, we're going to define a write methodthat accepts a slice of bytes, and returnsan integer and an error. And then we implementthat interface by creating a method on ourtype that has the same signature. So we don'thave to explicitly state that we're implementingthe interface, we implement the interfaceby implementing the interface by having themethods there that match the type signaturefor the interfaces methods, then we talkedabout how to compose interfaces together,and how this is a preferable approach versuscreating a single monolithic interface, ifyou can break that interface down into smallertypes, and then compose them together. Andwe did that something like this. So we'regoing to create multiple interfaces. So wehave a writer interface and a closer interface.And then when we compose them together, justlike when we compose structs, by embedding,we can embed interfaces into one another.So we can create a writer closer interfacethat embeds the writer interface and the closerinterface. So to implement that writer closerinterface, you have to implement the rightmethod, because it's defined by the writerinterface. And you have to implement the closedmethod as as defined by the closer interface.So by doing this, you can actually pass smallerchunks of your interface around your application.So for example, if a method only needs a writer,it doesn't need a closer, then you can actuallypass this writer closer as a writer, and itworked just fine versus passing the entirewriter closer along, and potentially exposingmethods to the consumer that aren't reallynecessary. Then we talked about type conversionand how we can drill through the interfaceto get at the underlying types in case weneed to work with those directly. So we hadan example here, where we created a writerclosure instance, and the underlying typewas a pointer to a buffered writer closer,and how we could cast that back to a pointerto a buffered writer closer by using thissyntax here, where we have a dot after theobject and then inside of print, we put thetype We want to cast toknow, we learnedthat when we did this, if the type assertionfailed, then we're actually going to panicor application. So remember to use that comma,okay syntax, if you want to get a booleanvariable out that you can run tests againstto see if that type conversion succeeded.And then we talked about the empty interfaceand type switches. The empty interface isnothing magic, it's just an interface to findon the fly that has no methods on it. Now,it's special in that every type in go implementsthe empty interface. So you can store anythingyou want in a variable of type empty interface.And then very often, we're going to pair thatwith what's called a type switch. And we seean example of that here, where we're goingto use the switch keyword, we're going tohave the object and then dot and params, likewe do with a type assertion. But instead ofhaving a concrete type that we're assertingagainst, we put the keyword type in there.And then in our case statements, we're actuallygoing to put in the data type that we're assertingagainst. So in this case, we're looking forintegers or strings, or we have a defaultcase, in case the value stored in i is neitheran integer nor a string. After that, we talkedabout implementing with values versus pointers.And we learned about a concept called methodsets. Now, when you're working with typesdirectly, you never have to think about thisbecause the methods are always all of themethods assigned to that type. But with interfaces,the rules change a little bit. The methodset of a value is all of the methods withvalue receivers. So if you're going to tryand implement an interface with a value type,than all of the methods that implement thatinterface, have to have value receivers. Withpointers, things are a little bit more flexible,because pointers always have access to theunderlying tape as well. The method sets fora pointer is all of the methods regardlessof the receiver type, so all of the valuereceivers, as well as all of the pointer receivers.So pointer types are definitely more flexiblewhen you're implementing interfaces. Justkeep in mind, you don't want to assign pointerreceivers everywhere without thinking aboutthe idea that that gives access to the underlyingdata of that type. And so that can allow methodsto alter that underlying data, even if youdon't want them to. So be careful when youmake that choice about using pointer receiversor value receivers. The last thing that wetalked about were some best practices thathave evolved over the last few years aboutusing interfaces in the go language. And wetalked about use many smaller interfaces wheneverpossible. And then if you need larger interfaces,go ahead and compose those together with interfacecomposition. Don't export interfaces for typesthat will be consumed. So if you're creatinga library, and somebody else is going to beconsuming a type, go ahead and publish thatconcrete type out there don't create an interface,assuming you know how people are going touse it, allow them to create the interfacesthat your type will implement. That way, theydon't have to implement a whole bunch of methodsin their test suite that they never even use.Do export interfaces for types that you willbe consuming however. So again, these twopoints are exactly opposite of how you'regoing to think about interfaces, if you'recoming from another language, such as C sharpor Java, that have explicit implementationof interfaces. So when you're defining a typethat you're going to be consuming in yourpackage, then go ahead and export interfaces.That way whoever's using your package cancreate their own concrete types, and implementthe interfaces that you need. So you don'tneed to worry about the implementation, youjust need to worry about the behaviors thatthey're exposing to you. And then, if possible,define your functions and methods to receiveinterfaces. Don't get too crazy with this.So don't go over the top. Use common sensewith this. But if you have the option of receivinginterfaces, for example, if you don't needaccess to the underlying data, then go aheadand define an interface that you're goingto be receiving. That way it makes your methodsand functions more flexible. Since you canhave multiple implementations that you neverthought about at design time. And your functionsand methods will continue to work. Even whenthose new concepts are thrown at your application,I want to have a conversation about the toolsthat we have available to implement concurrentand parallel programming in the go language.Now,if you come this far in the series, or you'vedone any research and go at all, concurrentprogramming is one of the hottest topics thatis talked about, especially among people whoare learning to go language for the firsttime. So we're going to talk about this conceptof a go routine, and how that enables us tocreate efficient and highly concurrent applications.We'll start our conversation by learning howto create go routines themselves. So thisis going to be the basics of how we creatego routines and how we can work with thema little bit. Then we'll move into a conversationabout synchronization. And we'll talk abouttwo concepts, weight groups and mutexes. Andhow we can use those to get multiple go routinesto work together. Because one of the challengesthat we're going to have with go routinesis also one of the greatest advantages. Goroutines are going to allow our applicationto work on multiple things at the same time.However, you're often going to run into situationswhere you need a certain bit of functionalityin your application to weight into one ormore of those concurrent calculations is complete.So we'll talk about how to use synchronizationprimitives in order to do that. Then we'llmove into a discussion of parallelism. Nowup to this point, our conversation is goingto be about concurrency in the go language.And concurrency is just the ability of theapplication to work on multiple things atthe same time, it doesn't mean it can workon them at the same time, it just means ithas multiple things that it can be doing.When we talk about parallelism, we'll talkabout how we can take our NGO applicationsand enable them to work on those concurrentcalculations in parallel, or in other words,introduce parallelism into our applications.And finally, we're gonna wrap this video upagain, with a little section on best practices,just to talk about some of the gotchas thatyou can run into with concurrent and parallelprogramming, and some of the tools that areavailable to help keep your application safeand away from those minefields. Okay, so let'sget started by talking about how to creatego routines. Okay, so the first thing thatyou're going to notice is that we're in VisualStudio code right now. Now, the reason forthat is while we can certainly play with goroutines in the playground, when we startto get into parallelism, that's going to belimited by the playground, because the playgroundonly enables us to use one core at a time.So when we're running locally, we can useas many cores as we want. So we can trulyrun our applications in parallel. So someof the things that I want to show you aregoing to be easier to illustrate in this environment.So the first thing that I want to show youis how we can create our very first go routine.So the first thing that we're going to needto do is we're going to need to have a functionhere. So I will create a function called Sayhello. And this is going to be a very simplefunction, all it's going to do is well sayhello. So we'll start with that. And that'sgoing to be just enough for us to get startedseeing what's going to happen with our application.So we can of course, call the say hello functionand call that from the main function. So wecan run this application by just using gorun and pointing it to that file. And of course,it says hello, so no big surprises there.Now, to turn this into a go routine, all wehave to do is in front of the function invocation,just type the keyword go. Now what that'sgoing to do is that's going to tell go tospin off what's called a green thread, andrun the say hello function in that green thread.Now I need to take a little bit of a momenthere to talk about threads. most programminglanguages that you've probably heard of andworked with us, oh s threads are used operatingsystem threads. And what that means is thatthey've got an individual function call stackdedicated to the execution of whatever codeis handed to that thread. Now, traditionally,these tend to be very, very large. They have,for example, about one megabyte of RAM, theytake quite a bit of time for the applicationto set up. And so you want to be very conservativeabout how you use your threads. And that'swhere you get into concepts of thread poolingand things like that, because the creationand destruction of threads is very expensive.And so we want to avoid that in most programminglanguages, such as Java, or C sharp. Now,in go, it follows a little bit of a differentmodel. And as a matter of fact, the firstplace I saw this model was used by the Erlanglanguage. And this is using what's calledGreen threads. So instead of creating thesevery massive heavy overhead threads, we'regoing to create an abstraction of a threadthat we're going to call a go routine. Now,inside of the go runtime, we've got a schedulerthat's going to map these go routines ontothese operating system threads for periodsof time, and the scheduler will then taketurns with every CPU thread that's availableand assign the different go routines, a certainamount of processing time on those threads.But we don't have to interact with those lowlevel threads directly. we're interactingwith these high level go routines. Now theadvantage of that is since we have this abstractiongo routines can start with very, very smallstack spaces, because they can be reallocatedvery, very quickly. And so they're very cheapto create and to destroy. So it's not uncommonin a go application to see 1000s or 10s of1000s of go routines running at the same time.And the application is no problem with thatat all.Now, if you compare that to other languagesthat rely on operating system threads thathave one megabyte of overhead, there's noway you're going to run 10,000 threads inan environment like that. So by using go routines,we get this nice lightweight abstraction overa thread, and we no longer have to be afraidof creating and destroying them. So anyway,let's go ahead and run this and see what happens.And it's going to be a little disappointingbecause you notice that our message doesn'tprint out. And the reason for that is ourmain function is actually executing in a goroutine itself. So what we did here in linesix was we told the main function to spawnanother go routine, but the application exitsas soon as the main function is done. So assoon as it spawn that go routine, it finished,it didn't have any more work to do. So thesay hello function never actually had anytime available to it to print out its message.So we can get around that a little bit byusing a horrible practice, but it's good enoughto get us started in understanding this. Sowe'll just put an arbitrary sleep callinghere in order to get the main function todelay a little bit Now when we run the application,we see that we do get our Hello message printedout. Now, as opposed to our first run of this,it's not actually the main function that'sexecuting this code. It's a go routine thatwe're spawning off from the main function.And that's what's responsible for printingout the message. Okay, now, this is a prettytypical use case of go routine where we'reusing the go routine to invoke a function.But we don't have to do that. As a matterof fact, let me just drop in this examplehere, which is basically the same, exceptfor instead of using a named function, I'musing this anonymous function here. So noticethat I've got this anonymously declared function,and I'm invoking it immediately. And I'm launchingit with go routine. Now, what's interestingabout it is I'm printing out the message variablethat I've defined up here on line nine, downhere inside of the go routine. So if I runthis, we do in fact, see that it works. Nowthe reason that it works is go has the conceptof closures, which means that this anonymousfunction actually does have access to thevariables in the outer scope. So it can takeadvantage of this MSG variable that we declaredup here on line nine, and use it inside ofthe go routine. Even though the go routineis running with a completely different executionstack. The go runtime understands where toget that MSG variable, and it takes care ofit for us. Now, the problem with this is thatwe've actually created a dependency betweenthe variable in the main function and thevariable in the go routine. So to illustratehow that can be a problem. Let me modify theexample just a little bit. So I'm declaringthe variable message and setting it equalto Hello, and then printing it out in thego routine. And then right after I launchedthe go routine, right here on line 13, I'mreassigning the variable to goodbye. So ifI go ahead and run this, you'll see that wein fact, get goodbye printed out in the goroutine, not Hello, like you might expectbased on how the program is written. And thereason for that. And it's not always goingto be guaranteed to execute this way. Butmost of the time, the ghost scheduler is notgoing to interrupt the main thread until ithits this sleep call on line 14. Which meanseven though it launches another go routineon line 10, it doesn't actually giveit any love yet, it's still executing themain function. And so it actually gets toline 13 and reassigns, the value of the messagevariable before the go routine has a chanceto print it out. And this is actually creatingwhat's called a race condition. And we'llcome back and talk about race conditions atthe end of this video. But this is a bad thing.And generally, it's something that you wantto avoid, so that you can access variablesvia the closure, it's generally not a goodidea to do that. So if that's not a good idea,what are your other options? Well, noticethat we have a function here. And this isjust a function invocation, there's nothingspecial about it, just because we put thego keyword in front of it, it's just a function.So functions can take arguments. So what happensif we add a message argument here, and thendown in the prints, where we're actually invokingthe function? What if we pass in the messageparameter? Well, since we're passing thisin by value, so we're actually going to copythe string hello into the function, then we'veactually decoupled the message variable inthe main function from the go routine, becausenow, this message that's going to print outis actually a copy that we're passing in whenwe're invoking the function for the go routine.So now if we run this, we see that we getHello printed out. So this is generally theway that you're going to want to pass datainto your go routines, use arguments to dothat, and really intend for the variablesto be coupled together. Now, this exampleso far is working. But it's really not bestpractice. And the reason it's not best practiceis because we're using this sleep call. Sowe're actually binding the applications performanceand the applications clock cycles to the realworld clock. And that's very unreliable. Soin order to get your applications to work,you're typically going to have to sleep fora very long time relative to the average performancetime in order to get the performance to bereliable. So we don't want to use sleep callsin production, at least not for somethinglike this. So what are the other alternatives?Well, one of the other alternatives that wehave is to use what's called a weight group.So let's go ahead and add one in. And thenwhile we're doing that, we'll talk about whatthey are. So I'm going to create another variable.And it looks like my auto formatting justhelped me here. And we'll pull that from thesync package. And we'll create an object oftype weight group. So I just need to put mycurly braces here to initialize it. Now whata weight group does is it's designed to synchronizemultiple go routines together. So in thisapplication, we've got two go routines thatwe care about, we've got the go routine that'sexecuting the main function. And we've gotthis go routine that we're spawning off hereon line 13. So what we want to do is we wantto synchronize the main function to this anonymousgo routine. So we're going to do that by tellingthe weight group that we've got another goroutine that we wanted to synchronize to itstarts off synchronizing to zero. And so we'regoing to add one because we want to tell itthat we're going to synchronize to this goRight here. Now once it's done, we don't needthis line anymore. Once it's done, we're goingto go ahead and exit the application. Andwe will do that by just waiting on the weightgroup. And we do that by using the weightmethod right here. Now when the go routineis done, then it can tell the weight groupthat it's actually completed its execution.And we do that by using the done method. Soif we execute that, basically, what that'sgoing to do is it's going to decrement, thenumber of groups that the weight group iswaiting on. And since we added one, and it'sgoing to decrement by one to be down to zero,and then the weight method will say, Okay,it's time for us to go ahead and finish upour application run. So if I save this off,and I go ahead and run it, we see in factthat our application is performing as it didbefore. But now it's taking just enough timeto complete the execution. We're not relyingon the rolling clock anymore, and having toJimmy run with variables and hope that everythingstays consistent. Now, in this example, we'rejust synchronizing to go routines together.But only one of the go routines is reallydoing any work. The main function in thisexample is just storing data, and spawningother go routines. But we can have multiplego routines that are working on the same data.And we might need to synchronize those together.And that can be a little bit tricky. So letme drop in this example here, and we'll talkabout it. So I'm creating weight group again,up here on line eight. And then I'm initializinga counter variable. inside of my main function,I'm actually spawning 20 go routines, becauseinside of this for loop, each time I run through,I add two to the weight group to let it knowthere are two more go routines that are running.And then I spawn a say hello, and then I spawnan increment here. And then I just have await method call here on line 17, just tomake sure that the main function doesn't exitout too early now and say hello, all I'm goingto do is I'm going to print out Hello, andI'm going to print out the counter value.And then in the increment function down here,I'm just going to increment the counter byone. Now after each one of those is done,I'm going to call the done method on the weightgroup. And everything should be just fine.Now notice that I've broken my own rule here,the weight group is actually being accessedglobally in this application. And that makessense, because I actually do want to sharethis object, and the weight groupis safe to use concurrently like this. It'sdesigned to be using this way. So let's goahead and run this application and see what'sgoing to happen. So our intuition says thatwe're going to print say, Hello. So it shouldprint say hello, zero, because the countersvalue is zero right here. And then it's goingto increment it. And then it's going to sayhello, again, it's going to increment it.So should say hello, number zero, hello, numberone, hello, number two, and so on. So let'sgo ahead and run this and see what happens.And we see that we get a mess, we in fact,don't have any kind of reliable behavior goingon here, we printed one twice, and then 2345.So that seemed to work consistently. And thenwe jumped all the way to nine, we printed10 out twice, and then we went back to ninefor some reason. And if we run this again,we'll get a completely different result. Sowhat's happening here is our go routines areactually racing against each other. So wehave no synchronization between the go routines,they're just going hell bent for leather,and going as fast as they can to accomplishthe work that we've asked them to do, regardlessof what else is going on in the application.So in order to correct this, we need to finda way to synchronize these guys together.Now, we could probably find a way to use aweight group on this. But we've already talkedabout weight groups. So I want to talk aboutanother way to do this. So we're going tointroduce this concept of a mutex. So witha mutex. let me paste this example in here,and then we'll talk about what it does. Buta mutex is basically a lock that the applicationis going to honor.Now in this case, you see on line 11, I'mcreating what's called an rW mutex, whichis a read write mutex. Now a simple mutexis simply locked or unlocked. So if the mutexis locked, and something tries to manipulatethat value, it has to wait until the mutexis unlocked. And they can obtain the mutexlock itself. So what we can do with that iswe can actually protect parts of our codeso that only one entity can be manipulatingthat code at a time. And typically what we'regoing to use that for is to protect data toensure that only one thing can access thedata at a single time. With an rW mutex, we'vechanged things a little bit. We've basicallysaid as many things as want to can read thisdata, but only one can write it at a time.And if anything is reading, then we can'twrite to it at all. So we can have an infinitenumber of readers, but only one writer. Andso when something comes in and makes a writerequest, it's going to wait till all the readersare done. And then the writer is going tolock the mutex. So nothing can read it orwrite it until the writer is done. So in thismodification, actually, I don't want to talkabout that line yet. We'll come back and revisitthat. So in this modification, what I've donehere is I'm attempting to use a mutex to synchronizethings together. So the modification is downhere in my say, Hello, I'm just reading thevalue of the counter variable. And that'swhat I'm trying to protect. I'm trying toprotect the counter variable from concurrentreading and writing because that's what wasgetting us into trouble. So on line 22, Iobtained a read lock on the mutex and thenI print out my message and And then I releasedthat lock using the R unlock method. Now inthe increment, that's where I'm actually mutatingthe data. So I need to write lock. And soI'm going to call the lock method on the mutex,increment the value. And then I'm going tocall unlock. Now, if I run this application,I actually haven't gotten quite where I wantto be. So I don't get the weird random behaviorthat I was seeing before. But you notice thatsomething seems to be out of sync still, becauseI get Hello, one, hello, two, and then itstays at two. And if I keep running, thisactually can get different behaviors. Butnotice that I'm always going in the properorder. So I fixed part of my problem, butI haven't fixed all of it yet. So I can keeprunning. Actually, that one got pretty close.But there's obviously something else goingon here. With the reason that we have an issuehere is we're still working within the goroutines. So if this say hello, function getsexecuted twice by its go routines, and theincrement function doesn't get called in between.That's where we get this behavior here, wherewe actually get the same message printingout twice, because we don't have a chanceto lock this mutex before we try and readit that second time. So the way to addressthis is we actually have to lock the mutexoutside of the context of the go routine.So we have a reliable execution model. Solet's go ahead and paste in a small modificationhere. Now all I've done is I've moved thelocks out here. So the locks are now executingbefore each go routine executes. And thenI unlock them when the go routine is done.So if I run this, we actually see that I nowget the behavior that I expect, I see zerothrough nine printed out. And if I run itagain, and I run it again, and run it again,everything is working great. So the reasonthat this is working is I'm actually lockingthe mutex is in a single context. So the mainfunction is actually executing the locks.And then asynchronously, I'll unlock themonce I'm done with the asynchronous operation.Now the problem with this application is Ibasically have completely destroyed concurrencyand parallelism in this application. Becauseall of these mutexes are forcing the datato be synchronized and run in a single threadedway. So any potential benefits that I wouldget from the go routines are actually gone.As a matter of fact, this application probablyperforms worse than one without go routines,because I'm mucking around with this mutex.And I'm constantly locking it and unlockingit. So this is an example where if this isall that this application needed to do, wewould actually be much better served by removingthe go routines, and just running this witha single execution path and removing any concurrencyat all. However, there are often situationswhere you can get a significant advantageby running things in parallel. And so youcan use weight groups, or mutex is in orderto synchronize things together, and make surethat your data is protected, and everythingis playing well together. Now I have thisline in here, and I apologize for that I reallyshouldn't have had that in these earlier examples.But I do want to talk about this functionfrom the runtime package called go max procs.So in modern versions of go, if you look atthis go max procs variable, let's just goahead and execute this simple program, allit's goingto do is it's going to tell me the numberof threads that are available. So it printsout that there are four threads availablein the application ends. Matter of fact, letme just add this carriage return in here andrun this again, that way things look a littlebetter. And you see that I have four threadsavailable. So by default, what go is goingto do is it's going to give you the numberof operating system threads equal to the numberof cores that are available on the machine.So in this virtual machine, I've exposed fourcores to the VM.So I have by default four oh s threads thatI can work with. Now, I can change that valueto anything I want. So for example, I canchange that to one. And now my applicationis running single threaded. So now I havea truly concurrent application with no parallelismat all. So this can be useful in situationswhere there's a lot of data synchronizationgoing on. And you really need to be carefulto avoid any kind of race conditions thatparallelism can incur. And maybe there's nobetter way to do it. Now, I would say there'san architecture problem there. But it is possibleto run an application in a single threadedway, by setting go max procs equal to one.Now if you're wondering what this negativeone does, when you invoke the go max procsfunction, it actually returns the number ofthreads that were previously set. And if youpass a negative number, then it doesn't changethe values. So this go max procs, negativeone, all that's doing is that's letting usinterrogate how many threads we have available.Now we can also set this to for example, 100.There's nothing stopping us from creatinga massive number of operating system threads.Now what I found in working with NGO is thatgo max procs is a tuning variable for youto work with. So the general advice is oneoperating system thread per core is a minimum.But a lot of times you'll actually find thatyour application will get faster by increasinggo max procs beyond that value. Now if youget up too high Like, for example, 100, thenyou can run into other problems, because nowyou've got additional memory overhead. Becauseyou're maintaining 100 operating system threads,your scheduler has to work harder becauseit's got all these different threads to manage.And so eventually, the performance peaks andit starts to fall back off, because your applicationis constantly rescheduling go routines ondifferent threads. And so you're losing timeevery time that occurs. So as you get yourapplication closer to production, I wouldencourage you definitely develop with go maxprocs greater than one because you want toreveal those race conditions as early as possible.But just before you release to production,you might want to run your application througha performance test suite with varying valuesof go max procs, to see where it's going toperform the best. Now, the last thing thatI want to talk about are some best practicesto keep in mind when you're working with goroutines in the go language. So let's takea look at those next. Go routines in the golanguage are very powerful, and it can beeasy to let them get a little bit out of hand.So I want to go through and give you someadvice on how to work with go routines inyour own applications. The first bit of adviceis, if you're working in a library, be very,very careful about creating go routines yourself,because generally, it's better to let theconsumer control the concurrency of the library,not the library itself. If you force yourlibrary to work concurrently, then that canactually cause your consumers to have moreproblems synchronizing data together. So ingeneral, keep things simple, keep things singlethreaded, and let the consumer of your librarydecide when to use a go routine and when notto now, this advice can be softened a littlebit, if you have a function called that'sgoing to return a channel that will returnthe result, then having the go routine inthere might not be such a bad thing, becauseyour consumer never really has to worry abouthow that unit of work is getting done. Theydon't really care if it's running concurrentlyor not. Because they are just going to belistening for the result on a channel. Butwe haven't talked about channels yet. So we'llrevisit that topic in the next video. Butfor now, if you're creating a library, tryingto avoid go routines, at least go routinesthat are going to be surface to the consumerand have them forced to work with them. Whenyou create a go routine, know how it's goingto end. Now we're gonna see how to do thisa little bit more when we talk about channels.But it's really easy to have a go routinelaunched as kind of a watch or go routine.So it's going to be just sitting out therelistening for messages to come in. And it'sgoing to process those messages as they arrive.However, if you don't have a way to stop thatgo routine, that go routine is going to continueon for ever. And so it's constantly goingto be a drain on the resources of your application.And eventually, as the go routine ages, itcould even cause other issues and cause yourapplication to crash. The other thing thatI want to give you some advice about is checkfor race conditions at compile time. So Iwant to jump back over to the editor and showyou how to do that. But it's very importantand very simple to do in most environmentsthat go runs in. So let's jump over to theeditor and take a look at that in order tosee if we don't have to go any further thanthis example. Now I know this is right fromthe beginning of the video, and it's got sleepsin there. And it's got some bad practices.But if you remember, if we run this application,then it prints goodbye instead of the Hellomessage that we originally printed. So howcould we have detected this without runningthe application? Well, you might not thinkit's terribly important to be able to do that.Because it's obvious, we've got some kindof a problem here. And all we have to do isapply our debugging skills. But there areother cases where this is very, very subtleand very, very hard to track down withouta little bit of help. Well, fortunately, thego compiler has quite a bit of help availableto you. And it's as simple to invoke as justadding a dash race flag to go run, go install,go build whatever you're using to get yourapplication up and running. So let's go aheadand try that and see what it says about ourlittle application here. And you notice itdoes run the application because we invokedgo run so we see goodbye printed here. Butnotice what we got up here we got this datarace message. So it's telling us it sees thatthe same area of data is being accessed bytwo different executing go routines. So itsays the first one that it found was in goroutine six which an internal identifier unlesswe're profiling, we've got no idea what goroutinesix is, but it does tell us it was invokedon line 11. So apparently in this run, goroutine six was this go routine right here.And it was accessing the MSG variable. Italso sees that we access the MSG variableon line 13, which is in our main function.And so by adding the dash race flag, we getall of this additional information where thego compiler itself, analyzes our applicationand tries to determine if we have any raceconditions. So I would strongly encourageyou if you get any kind of concurrency atall in your application, you're going to wantto run this because it's very simple checkit runs very, very quickly. And it's goingto help you prevent very subtle bugs fromgetting into your production code.Okay, so that's what I have to talk about.With go routines, maybe you are expectingmore. But go routines are really quite simple.Now when we get into our next conversation,which will be about channels, things get alittle bit more complicated, but go routinesare relatively straightforward. So let's gointo a summary and review what we've talkedabout in this video. In this video, we learnedabout go routines and how we can use themto create concurrent and parallel executionpaths in our applications. We started by learninghow to create go routines themselves. Andwe learned that by adding the go keyword infront of a function call, we've created ago routine. And that's all that it takes.There's no special semantics, there's no specialthings that need to be done. It's simply afunction call with the keyword go in frontof it. Now when we're using anonymous functions,we in general want to pass data as local variables.So you want to add a parameter into that anonymousfunction and pass data into the go routine,instead of relying on the closures to preventany kind of race conditions as you try andmanipulate that data. And that's not alwaystrue. We saw with weight groups that we accessthat globally, because that was our intention,we truly did want that to be available inmultiple scopes. But even then we could passa pointer in, in order to be very clear aboutwhat information that go routine should haveaccess to. Then we talked about the differentways that we can synchronize multiple go routinestogether, one of the challenges that we havewith go routines is now we've got all sortsof things happening. And there's no way toensure without synchronization, how they'regoing to interact with one another. Now fora lot of concurrent calculations, that's nota problem at all, because the two might notbe related to one another. But often you getinto situations where you're relying on theresult of multiple calculations, or somethingneeds to know the result of the work that'sbeen done, or you've got a shared memory issue.And you need to make sure that those go routinesaren't trying to manipulate the same dataat the same time. So we can use weight groupsto wait for groups of go routines to complete.So we saw that we have three methods thatare interesting. There, we have the Add methodto inform the weight group that there aremore go routines for it to wait on, we havethe weight method that's going to block thego routine that is called on until the weightgroup is completed. And then we have the donemethod available on that weight group thatlets the weight group know that one of thego routines is completed with its work.We also talked about the mutex and the rWmutex, and how those are generally used toprotect data. So if you have a piece of datathat's going to be accessed in multiple locationsin your application, then you can protectthat access by using a mutex or an rW mutex.To ensure that only one go routine is manipulatingthat data at one time. We then talked aboutparallelism and how parallelism can introducesome really tricky challenges into your goapplications. We talked about how by defaultgo will use the number of CPU threads equalto the number of available cores on the computerthat is running on. We talked about how wecan change that by using the go max procsfunction from the runtime package. And wetalked about how more threads will generallyincrease performance. But too many can actuallyslow it down. So in general, if you're developingan application, you want to start from dayone with go max procs greater than one tofind any concurrency and any race conditionsearly on in your application development.But don't settle on a final number until rightas you get close to production and you havea performance test suite that you can workwith. To find out what the best value forgo max procs is for your application. Becausewhile the starting number is a very good numberto start with, a lot of applications actuallyperform better with a value higher or lowerthan that default value. And finally, we wrappedup with a discussion of some best practicesto keep in mind when you're working with goroutines. We learned that if you're a librarydeveloper, you should avoid the creation ofgo routines that are going to be exposed tothe consumer of your library. Let the consumercontrol the concurrency of your applicationbecause they're the ones that are in the bestplace to know if something needs to be executedsingle threaded, or if it can be executedconcurrently. When creating a go routine,know how it's going to end. It's very easyto get into situations where go routines startleaking memory because they're never cleanedup because they never quite get done withtheir work. Now normally a go routine is killedas soon as it finishes its execution. Andwe saw that with the main function, the mainfunction runs in a go routine. And that goroutine terminates as soon as the main functionexits. We also saw in our say hello function,as soon as it printed its message out andthe function exited. That go routine was killedand it was cleaned out. So it was very clearwhen those go routines life cycle is goingto be over. However, if you've got go routinesthat are listening for messages in a continuousloop, then make sure that you code in a wayto shut those go routines down so that onceyou're done using them and clean up the memorythat they're using. Also, as you're goingalong with your application development, checkfor race conditions, it's not that hard todo. You just have to add dash race onto thego command that's compiling your application.And then the go compiler is going to analyzeyour application and try and locate placesin it that have the potential of access Seeingthe same memory at the same time, or in anunsynchronized way, causing very subtle andpotentially very disastrous bugs where yourapplication when it gets to production. Overthe course of this video series, we've talkedabout a lot of structures and techniques andtools that are available. In order to getstarted successfully programming with thego language. Well, I want to wrap up thatdiscussion in this video by talking aboutone of the features that makes go really standout when you're looking for different languagesto work with. And that is this concept ofchannels. Now most programming languages thatare out there, were originally designed witha single processing core in mind. And so whenconcurrency and parallelism came into play,they were really kind of bolted on to theside. And so a lot of times, you're actuallygoing to be working with third party librariesand packages in order to help with data synchronizationand things like that. Well go was born ina multiprocessor world. So every computerthat was out there when go was invented, hadmore than one processing core. So it madesense as the language was being designed toconsider concurrency and parallelism fromthe beginning. Now in the last video, we talkedabout go routines, and how go abstracts theconcept of a thread into this higher conceptcalled a go routine to allow hundreds or 1000sor even 10s of 1000s of things to be goingon in your application at the same time. Onthis video, we're going to be talking aboutchannels, and how those can be used to passdata between different go routines in a waythat is safe, and prevents issues such asrace conditions, and memory sharing problemsthat can cause issues in your applicationthat are very difficult to debug.So we're going to start this talk by talkingabout the basics of channels. So we'll talkabout how to create them, how we can use themhow we can pass data through them, then we'lltalk about how we can restrict data flow.Now a basic channel is a two way street, wecan send data in and we can get data out.But that's not always what you want to beable to do with the channel. Sometimes youwant to send only channel or receive onlychannel. And we'll talk about how to do thatin the second section. Then we'll talk aboutbuffered channels, and how we can actuallydesign channels to have an internal data storeso that they can store several messages atonce just in case the sender and the receiveraren't processing data at the same rate. Thenwe'll talk about how we can close channelsonce we're done with them. We'll then revisitthe topic of four range loops. And we'll learnhow we can use channels with a four rangeloop. And then we'll wrap up our discussionby talking about SELECT statements, whichis kind of like a switch statement, but specificallydesigned to work in the context of a channel.Okay, so let's go ahead and dive in and learnthe basics of working with channels.So when we're working with channels in thego language, we're almost always going tobe working with them in the context of goroutines. And the reason is because channelsare really designed to synchronize data transmissionbetween multiple go routines. So let's goahead and get started by creating some goroutines. Well, actually, the first thingthat I need to do is I need to create a weightgroup. Because as you remember from the lastvideo, we use weight groups in order to synchronizego routines together. So we're going to usethe weight group to synchronize the go routinesto make sure that our main routine waits forall of our other go routines to finish. Andthen we're going to use channels in orderto synchronize the data flow between them.So we got two different synchronization mechanismsgoing on in this little application. The nextthing we need to do is we need to create achannel. Now channels are created with thebuilt in make function. And there really isno shortcut around this. Now a lot of usesof the make function, you can actually useother forms. When you're creating a channel,there's enough internal mechanisms that needto fire that you have to use the make functionin order to allow the runtime to properlyset up the channel for you. Now on the simplestform of the make function with working withchannels, we're going to use the channel keywordto say that we want to create a channel. Andthen we're going to provide the data typethat's going to flow through the channel.Now you can pick any data type that you want,we're just going to be using integers here.But keep in mind that this means that thechannel is strongly typed, you can only sendintegers through this channel that we're creatinghere. Similarly, if we provided strings, wecould only pass in strings. If you providedpointers to integers, you can only send inpointers to integer, you get the general idea.So when you create a channel, you're goingto create that channel to accept messagesof a certain type. And it's only ever goingto receive them send messages of that type.Now in this initial example, we're going tohave to go routines, I'm going to spawn soI'm going to add two items to my wait group.And then we'll go ahead and create those goroutines, and then we'll talk about those.So let me just drop in the rest of the codehere. And you can see my first go routineis an anonymous function actually both ofthe marm and this first one is going to bereceiving data from the channel. So this goroutine is actually going to be my receivinggo routine. And then this channel is actuallygoing to be my sending go routine. So theway that we send a message into a channelis as you see here, we're going to use thisarrow syntax, so we're going to use a lessthan and a dash, and when we're putting datainto the channel we list the channel first,then we have this arrow and then the datathat we want to pass in. So imagine that thearrow is pointing in the direction that wewant the data to flow. So we want the datato flow into the channel, and so the arrowis pointing toward the channel. Similarly,if we want to receive data from the channel,then we're going to put the arrow on the otherside. So we're going to use that same lessthan and dash, but it's going to be beforethe channel. And so we're going to be pullingdata from the channel. So in this line righthere, on line 14, we're going to be receivingdata from the channel and assigning it tothe variable i. And then after we're done,we're going to call the done method on ourweight groups. And we're just going to printthe value out. So all we're doing here isthis go routine is going to be sending thevalue 42, this go routine is going to be receivingwhatever value comes out of the channel, whichin this case, of course, will be 42. And it'sgoing to print that out to the console. Solet's go ahead and run that. And we see thatin fact, it does work. So the nice thing aboutdoing this is since we're sending a copy ofthe data to the channel, we could manipulatethe variable assigned here. So for example,we could actually start this off with I setequal to 42. And we can pass in I and thenafterwards, we can reassign I, and it doesn'tmatter because like with all other variableoperations and go, when we're passing datainto the channel, we're actually going topass a copy of the data. So when we manipulateit afterwards, the receiving go routine doesn'treally care that we change the value of thevariable, it's not affected by that at all.Now another common use case for go routinesis if you have data that's asynchronouslyprocessed, so maybe you can generate the datavery, very quickly, but it takes time to processit. Or maybe it takes a long time to generatethat data. So you've got multiple generators,but it can be processed very quickly. So youmight want to have a different number of goroutines that are sending data into a channel,then you have receiving. So let's take a lookat how we can do that. So it's a slight modificationto the example that we just went through,instead of just having the go routines fireonce, I'm actually creating go routines insideof this loop here. So I'm going to createfive sets of go routines. So each one of thegroups is going to have a sender like we havehere, which is exactly what we had before.And then we're going to have a receiver, whichis again, just like we had before. So by thetime the application is done, we're goingto spawn 10 go routines here, five sendersand five receivers, and all of them are goingto be using this single channel to communicatetheir messages across. So if we go ahead andrun this, we see that we do get five messagesreceived.Okay, so this works out really well, well,but I will warn you, if you start playingaround with this, and you decide to startmoving the senders and receivers to make themasymmetrical, things won't work very well.So one of the things you might want to doto play with this example is take this goroutine, and move it outside of the for loop.So you're gonna have one receiver and multiplesenders at the end of this, well, that actuallyisn't going to work right now. Because ifyou think about how this go routine is goingto process, it's going to receive the messagecoming in from the channel, it's going toprint and then it's going to exit, but thendown here in the loop, we're actually goingto spawn five messages into that channel.So we can only receive one, but we're sendingfive. And if we run this, we're actually goingto run into a problem. And that is we seeall routines are asleep, that we have a deadlockcondition. And the reason for that is becausewe have these go routines down here that aretrying to push messages into the channel.But there's nothing that can process them.Now an important thing to keep in mind hereis the reason that this is a deadlock. Andthe reason for that is this line of code hereis actually going to pause the execution ofthis go routine right at this line until there'sa space available in the channel. So by default,we're working with unbuffered channels, whichmeans only one message can be in the channelat one time. So our first go routine in thisloop gets happily spun up, it pushes a messageinto the channel, and then it exits and thencalls this done method on the weight group.And then that message gets processed by thisgo routine here and everything's happy. However,this go routine then exits. And then our nextgo routine comes along and tries to push anothermessage in. Well, it blocks right on thisstatement. And there's nothing in our applicationthat's going to receive that message. Andthat's why we see the go runtime. Notice thatand it's going to kill the application becauseit notices that we have a problem, and itdoesn't know how to resolve it. Now I wantto go back to our previous example. And actuallyI'm going to modify things slightly here.Because I want to show you that notice thatwe're just working with the raw channel. Sothis is perfectly valid code for us to write.As a matter of fact, if I go ahead and runthis, we see that we get two messages printedout but look at how that's happening. So thisgo routine is pushing a message into the channel.That message is then being received up inthis go routine and printed out this go routinethen the one that received this message isthen putting a message back into the channel.And that is then being received down hereand this go routine, which is then printingthe message out. So both of these go routinesare actually as readers and writers, now thatmay be a situation that you want, but veryoften, you want to actually dedicate a goroutine to either reading from a channel orwriting to a channel. So in order to do that,what we're going to do is we're actually goingto pass in the channel with a bias on thedirection that is going to be able to workwith. So the way we're going to do that isby accepting variables in our go routines.So we'll start with this first one here. Andwe want this to be a receive only channel.So the way we're going to do that is we wantdata to flow out of the channel. So you noticewe're using that similar syntax, we're goingto list the type of the channel, and thenwe're going to have this arrow coming outof it. So data is flowing out of the channel.And so this is going to be a receive onlychannel. Similarly, if we want a send onlychannel, we're going to give it the variablename, we're going to say that it's a channel,now we put the Send only operator right here,and then we put the data type. So this isgoing to be sending data into the channelonly. And this is going to be receiving datafrom the channel. And then of course, we haveto pass the channel into the go routines asarguments. So when we run this, we're actuallygoing to get an error. And the reason we getan error is because we're trying to pass datainto this channel. But this is a receive onlychannel. So it's invalid to send data intoit. And then similarly, we have an error downhere on line 21, because we're trying to receivedata from the Send only channel. So if wego ahead and wipe out these lines here, thisline, and this line, and run, then everythingworks as it did before. But now it's muchmore clear what the data flow is in the goroutine, we know that we're going to be receivingdata on one side, and we're going to be sendingdata on the other. Now something that's alittle unusual with this is notice that we'repassing in a bi directional channel. So thisis just a normal channel, and we're receivingit a little bit differently. So this kindof feels like a little bit of polymorphicbehavior. And this is a special aspect ofchannels, the runtime understands this syntax.And so it actually is going to, I'm goingto use the word cast here, it's going to castthis bi directional channel into a unidirectionalchannel. But that's not something you cangenerally do in the go language. That is somethingthat is specific to channels. Now one of theproblems we ran into on a previous exampleis we had a situation where we tried to pushfive messages into a channel, but we onlyhad one receiver. And we noticed that theapplication deadlock, well, we can get aroundthat in a couple of different ways. Now I'mgoing to show you one way to get around thatthat really is nice deal for solving thatproblem. But I will talk about the problemthat it is solving. And that is by using bufferedchannels. So if I go ahead and paste in thisexample, here, we will see an example of theproblem we might run into. I've simplifiedit a little bit from the previous examplewe ran into. So we've got our initial examplewhere we've got a receive only go routine,we've got a send only go routine. But in oursend go routine, we're actually sending twomessages. But since we're only receiving one,we expect that we're going to run into a problem.So let's go ahead and run this. And we seethat we do in fact have a problem, we receivedthe 42 out and printed it. But there's nothingto deal with this message here that's in thechannel. And so the application blows up,because this go routine can never complete,because it's blocked on this line. So we needa way to get around that. Now a simple wayto get around that is by simply adding a bufferhere. So if we add a second parameter to themake function up here, and provide an integer,that's actually going to tell go to createa channel that's got an internal data storethat can store in this case, 50 integers.Now what that's going to do is it's actuallygoing to allow our application to complete.But we do have a little bit of a problem herebecause this message is lost. So it did eliminatethe panic. And I guess in one way, you couldsay it solved the problem. But it did createanother problem in that we lost this message.Now this isn't the problem that buffer channelsare really intended to solve. But I do wantto show you that it does create that internalstore, so we can receive multiple messagesback out. As a matter of fact, what we cando is we can just copy this line down hereand reformat this, and we don't need thiscolumn right here. And if we run this, wesee that we do get both messages printed backout. Now what a buffered channel is reallydesigned to do is if the sender or the receiveroperate at a different frequency than theother side. So you can imagine if we had adata acquisition system, and maybe we retrievedata from our sensors, and a burst transmission,so maybe we're acquiring data from seismometers,and we're monitoring earthquakes, well, maybethose seismometers in order to conserve power,don't send their data continuously, they'regoing to send a burst transmission, maybeonce an hour. So every hour, we're going toget a burst transmission that maybe last fiveor six seconds, that's going to contain theentire hours worth of data. So in that case,our sender is going to be inundated with datawhen that burst happens. And it's going tohave to have a way to deal with it, one ofthe receivers might take a little while toprocess that data. So in that case, what wemight want to do is create a buffer here ofthese signals that are coming in from ourseismometer, that's going to be able to acceptthat one hours worth of data. And then ourreceivers can pull that data off, as they'reable to process it, and keep things workingsmoothly, so that the channel that's receivingthe data from our sensors, doesn't get lockedup, because it doesn't have a place to putthe next message. So that's really what bufferedgo routines are designed to work with, iswhen your sender or your receiver needs alittle bit more time to process. And so youdon't want to block the other side, becauseyou have a little bit of a delay there. Nowif this isn't the right way to handle thissituation, what is the right way? Well, theway that we typically handle something that'sgoing to happen multiple times, such as passinga message into a channel, is by using somekind of a looping construct. And that's nodifferent with channels as is with anythingelse. So let's paste in this example, whereI instead of processing the message once andthen having this first go routine exit, I'mactually going to use a for range loop. Butnotice what I'm arranging over,instead of ranging over some kind of a collection,such as an array, a slice or a map, I'm actuallyranging over the channel. Now the syntax changesjust a little bit. Because if this were aslice, the first index that we pull back isgoing to be the index in the slice. And thenthe second variable we pulled out, if we had,for example, a second variable here wouldbe the value. Well, when you're arrangingover a channel, things are a little bit different.When you pull a single value, you're actuallygoing to get the value that's coming out ofthe channel. And so if we run this, we seethat we do in fact get 42 and 27. But we stillhave a deadlock condition. So what's causingthat deadlock condition? Well, before we hadthis four range loop, we actually deadlockedthis go routine right here, and everythingdied. We're in our new application, we'reactually deadlocking in the four range loop.And the reason for that is because we're continuingto monitor for additional messages, but westopped sending messages. And so now thisfour range loop doesn't know how to exit.And so this go routine is now causing thedeadlock condition. So we've improved thesituation, we kind of move the needle wherewe're no longer dead locking in our sender,but we are still dead locking in our receiver.So how do we handle that? Well, the way thatwe're going to handle that is we have to understandhow the four range loop works. So if you'reusing a for range, loop over a slice, howmany times does that iterate? Well, it executesthe loop once for every item in the slice.So if you've got a slice with five elementsin it, you're going to run through the fourrange loop five times, well, how many elementsare in a channel? Well, there can be an infinitenumber of elements in a channel because youcan constantly push a new message into it.So what is the way to signal a four rangeloop with a channel that there are no moremessages coming? Well, the answer is we needto close the channel. So anything that hasaccess to the channel can do this, we're goingto use the built in close function. And we'regoing to pass in the channel like you seehere. So what we're doing on our sending sideis we're passing in two messages, we're passingin 42, and 27. And then we're letting thechannel know we're done working with you.So we're going to go ahead and close the channel,this four range loop is going to detect that.And when we run this, now everything runswell, because we're passing in the message42, that gets processed in the for loop, we'repassing in 27, that gets processed, then weclose the channel, that gets processed bythe for range loop, which notices that thechannel is closed, and it's going to exitand it's going to terminate the loop. So whenwe terminate the loop, then we call the donemethod on the weight group. And then we exitthe go routine, all of our go routines exitproperly, and we have no moredeadlocks.Now we do have to be a little bit carefulin closing channels, because when you closea channel, you really have to mean that you'reclosing the channel down. So let's try closingthe channel right here and then pushing anothermessage into it. So if we run this, we actuallyget a bad thing happening. So in this case,the application panicked. And why did it panic,because we tried to send a message on a closedchannel. So the issue here is we close thechannel right here on line 21. And then online 22, we tried to pass another messageinto it. So that is a no, no, you are notallowed to pass a message into a closed channelbecause the channel is closed. So you mightask, Well, how do I recover from this? Howdo I reopen the channel or undo that or whatever?And the answer is, you can't. As a matterof fact, you can't even detect if a channelis closed, except for by looking for the applicationpanicking. So call that a limitation of thego language or not, I don't know. But youdo have to be very careful that when you closea channel, nothing else is going to send amessage into it. So if that is a possibility,then the only option you really have is tohave a deferred function and use a recoverin there to recover from the panic that getsthrown because in this situation, you willhave a panic and there is no way to avoidit. So if in your application, that's a situationthat's likely to happen, then again, you'regoing to have to use that recover function.And you can review the video where I talkedabout using those. Now on the receiving side,we do have a little bit of a different storyhere, because this issue is on the closingside. So we cannot send the message into aclosed channel. And we can't detect if a channelis closed before we try and send a messageinto it. However, if we go on the receivingside, then the story gets a little bit brighter.So you might ask the question, how does thefour range loop know that the channel is closed,it has to have some way of detecting it, whatturns out that there's more than one parameterthat you can pull back from the channel. Sojust like when we're querying maps, and we'retrying to get a value out of a map, and wecan use that comma, okay, syntax, well, thatsyntax works for channels as well. So if Ichange this example up a little bit, and thisis going to do exactly the same thing as ourcurrent example, here using a four range loop,but instead of using the four range loop,and having go automatically processed theclosed channel for us, we're going to processthis manually. So let me paste in this exampleand show you so notice that I'm in a for loopin this go routine, and I don't have any conditionson it. So this is going to execute forever.Down here, then I'm receiving a message fromthe channel, and I'm using the comma. Okay,syntax. So I'm going to get the value fromthe channel and I and I'm going to get a Booleanletting me know if the channels open or notin the okay variable. So if the channel isopen, then okay is going to be true. If thechannel is closed, then Okay, it's going tobe false. So the happy path, if okay is true,then I'm going to go ahead and print out mymessage. Otherwise, I'm going to break outof this for loop here, because the channelsclosed, and I'm not going to be receivingany more messages from it. So this is functionallyexactly the same as the four range construct.But we're explicitly seeing this comma, okay,syntax. So which one would you use?Well, in this situation, it would make moresense to use the for range construct. Butthere may be situations where you're receivingdata from a channel, and you're not in a loop.So maybe you're spinning off a new go routinefor every time you're processing a message.And so the loop is going to contain the spinningoff of the go routines. And so you're goingto need this comma, okay syntax, because itmight not make sense to use the for rangeloop. Now, the last thing that I want to talkabout in this video are what are called SELECTstatements. So let me go ahead and paste inthis code here. So we talked about in thelast video, how there can be situations whereyou create go routines that don't have anobvious way to close. And that's what I wantto try and illustrate here. So if I go aheadand run this, we see that we do get thesemessages printed out. So I'm just doing asimple logger implementation. So what yousee here is I've got some constants that aredeclaring my log level, I've got a structthat I've declared the holding the timestampfor the login tree, the severity of the loglevel, and then whatever message I'm tryingto print out, then I'm creating a log channel.And the way this application works, is thefirst thing the main function does is it spinsup this go routine, that's going to be mylogger. And what it's going to do is it'ssimply going to monitor that log channel forlog entries that are coming from throughoutmy application. So the idea is I've got acentral logger, and anything that could dologging in, my application just needs to knowabout this channel. And all of my logginglogic can be hidden within the processingof those log channel messages. So the loggeris down here, we've got a four range loopthat's listening for messages from the logchannel. And all it's doing is it's printingout a formatted message that's got the timestamp,it's got the log level, and it's got the messagefrom the log. So no big deal here, nothingterribly exciting. Then my main function goeson to exercise that a little bit, it sendstwo messages into the log channel, one lettingit know that the application is starting anotherone letting the application note shuttingdown. And then I've got a sleep call herejust to make sure that the logger co routinehas enough time to process that. Now you noticemy timestamps are a little funny here. That'sbecause I'm working with the playground, Ipromise you this code does work. If you shiftedover to Visual Studio code, you will actuallyget real timestamps. But for some reason,the playground doesn't give you the currenttime when you call the now function. And sothis is just something that we're going tohave to work with in this example. Now theproblem I want you to consider is when doesthe logger go routine closed down. So obviously,the logger go routine has to terminate sometimebecause the program finishes execution, andwe get the results back from the playground.So what's happening here is remember, an applicationis shut down as soon as the last statementof the main function finishes execution. Sowhen we finish this sleep call here, the applicationterminates and everything is torn down andall resources are reclaimed as the go runtimereturns all of the resources that it was usingback to the operating system. So what thatmeans is that our logger go routine is beingtorn down forcibly. There's no graceful shutdownfor this go routine. It's just being rippedout because the main function has done. Nowin some situations like this one that maybe acceptable. But there are many situationswhere you want to have much more control overa go routine. Because remember what I saidin the go routine video, you should alwayshave a strategy for how your go routine isgoing to shut down when you create your goroutine. Otherwise, it can be a subtle resourceleak, and eventually, it can leak enough resourcesthat it can bring your application down. Sothere's a couple of different things we coulddo here, right, we could of course, do a defercall here, we can pass in an anonymous function.And inside of that, we could go ahead andclose the log channel. So what that's goingto do is when the main function exits, it'sgoing to go ahead and close the channel, andthen we are gracefully shutting down thatchannel. And that works just fine. There'sno issues with that we are intentionally closingdown the channel, we know how our go routineis going to close. And so this is perfectlyacceptable in this use case. But this isn'twhat I want to show you. So this is certainlysomething you could use in this use case.But I want to show you another way that verycommonly used in these kinds of situations.So the way that I want to show you is usingwhat's called a select statement. So let mego ahead and paste in that code. And we'llwalk through that. So the applicationis basically the same, I've got the same constantshave here, I've got the same struct, I dohave this additional channel here. And noticethe type signature for it. So it's stronglytyped, but it's strongly typed to a structwith no fields. Now struct with no fieldsin the go language is unique in that it requireszero memory allocations. So a lot of timesyou will see a channel set up like this. Andthe intention is it can send any data throughexcept for the fact that a message was sentor received. So this is what's called a signalonly channel. There's zero memory allocationsrequired in sending the message. But we dohave the ability to just let the receivingside know that a message was sent. So thisis pretty common, you might be tempted ifyou're new to the language, like I first didyou send a Boolean in here. But that doesactually require a variable to be allocatedand copied. So it is actually better to usean empty struct because it saves a coupleof memory allocations. It's a little bit minor.But it is something that if you are goingto use a channel as a pure message, then youmight as well go with the conventions anduse this approach. So our main function isexactly the same as it was before. We've gotour logger, we've got our log channel, sendingin a couple of messages. And then we got asleep call here. And then inside of our loggerfunction, we've got an infinite loop now,and we're using this select block. So whatthis SELECT statement does is the entire statementis going to block until a message is receivedon one of the channels that it's listeningfor. So in this case, we've got a case listeningfor messages from the log channel, and thecase listening for messages from the donechannel. So if we get a message from the logchannel, then we're going to print out ourlog entry. If we get a message from the donechannel, then we're going to go ahead andbreak out of this for loop. So what this allowsus to do is at the end of our application,we can go ahead and pass in a message intoour dumb channel. And that is going to bean empty struct. And I'm just going to definethat empty struct on the fly here. So thisis a little bit confusing syntax. But thisis the type signature for my struct. So I'mdefining a struct with no fields. And thenI'm initializing that struct using these curlybraces here. So if I go ahead and run this,you see that the application runs properly.So I do process my log messages. And thenI pass in this message into my done channelwhen I wish the logger to shut down. So thisis a common situation for you to use whenyou're monitoring channels. And you need away to have the go routine that's monitoringthose handles be able to terminate. So veryoften you're going to send in normally asa parameter, you're going to send in thisdone channel. And then whatever's ready tokill the go routine, will go ahead and senda message into that done channel. And it'llgo ahead and kill it. Now one more thing thatI do want to talk about. And I'm not goingto actually run it because it's going to breakour application here. But you can have a defaultcase here. And if you do, then this no longerbecomes a blocking SELECT statement. So whatthis is going to do is if there's a messageready on one of the channels that are beingmonitored, then it's going to execute thatcode path. If not, it will execute a defaultblock. So this is useful. If you want to havea non blocking SELECT statement, then youneed to have that default case in there. Ifyou don't have the default case, then theselect statement will block forever untila message does come in. Okay, so that's whatI have to talk about with channels. Let'sgo into a summary and review what we've talkedabout. In this video, we talked about channelsand how we can use them to synchronize datatransmission between go routines. We startedout by talking about the basics of workingwith channels. And we learned that we canmake our channels using the built in makefunction and how that's really the only waythat we have available in the go routine tomake a channel. When we do make those channels,those channels are strongly typed. So we'regoing to use the chain keyword to indicatethat we wish to create a channel and thenwe have to follow that with the data typethat the channel is going to be Send and Receive.Now that data type can be anything, it canbe a primitive like we see here with an integer,it can be a struct, it can be an interface.But it does have to be strongly typed, wecan send a message into the channel usingthis arrow syntax. And the position of thearrow kind of indicates the direction thatthe data is going to flow. So in this case,we list channel, we have the arrow and thenthe value that we wish to send into the channel.So notice that the arrow is pointing intothe channel. But when we went to receive messagesfrom the channel, then the arrow is leadingout of the channel. And so we're going touse the same arrow syntax, but the channelis going to be added after the arrow insteadof before. And we can have multiple sendersand receivers. As a matter of fact, it's verycommon. As a matter of fact, it's very commonfor one channel to be distributed among multiplego routines. And that way, you can have multipledata generators that are sending messagesinto the channel, as well as multiple datareceivers. And that allows you to balancethe performance between senders and receivers.So if you can generate data 10 times as fastas you can process it, then you can create10 times as many receivers. And that way youcan balance the workload out between sendersand receivers.We then talked about how to restrict dataflow. Buying default, channels are bi directionalconstructs, so you can send and receive datainto a channel. Now very often what we want,though, is our go routines to be designedto handle channel data only in one direction.So we saw that we can do that by passing inthe channel. But then on the receiving side.So for example, in the argument list of thefunction, we can actually specify the directionthat we can work with by again adding thatarrow, and we either add it before or afterthe chain keyword. Depending on what kindof channel that we want to make, we can makea send only channel by putting the arrow afterthe chain keyword, and we can make a receiveonly channel by adding the arrow before it.We then talk about buffered channels, andhow buffered channels contain internal datastores that allow us to get around this limitationof channels that by default, a channel willblock the sender side until a receiver isavailable, and the receiver side will be blockeduntil a message is available to come out ofthe channel. So you can actually block a goroutine on the sending side or the receivingside of the channel. So if there's no positionavailable in the channel to add the message,then the sending side will be blocked untila space does become available. And if there'sno message in the channel, then the receivingside is going to be blocked until a messagebecomes available for it to work with. Soin order to decouple that we can add an integeras a second argument to the main function.And that's going to allow the channel to havean internal buffer to decouple your sendersand receivers, just in case there are situationswhere data is generated faster than it's received.So just like it says here, we want to usebuffered channels when sending and receivinghave asymmetric loading. So if we can generatemessages faster than we can receive them,then a lot of times a buffered channel isa really good way to go. We then moved onto talk about four range loops, and specificallyhow to work with them with channels. And welearned that they basically work the sameway. But there are a couple of subtle differences.The first thing is the first parameter thatyou're going to receive from the four rangeloop when working with channels is the valueitself, not the index, like we saw when wewere using for range loops over arrays, slicesand maps. And we saw how we can use for rangeloops to monitor channel and process messagesas they arrive. So the four range loop isjust going to keep pulling messages as theycome in off the channel. And it'll processthem as they come. Then when the channel getsclosed, the four range loop is going to detectthat and it will go ahead and exit the loop.And finally, we talked about SELECT statements,and how they work kinda like switch statements,but they work only in the context of channels,and how they allow a go routine to monitorseveral channels at the same time. Now ifthey block if all channels are blocked, soif there's no messages available on any channel,then the select statement will block by default.And then when a message comes in, it willgo ahead and process that on the proper case.If multiple channels receive value simultaneously,then the behavior is actually undefined. Sobecause of the highly parallel nature of manygo applications, you can get into situationswhere messages arrive on two channels at virtuallythe same time. So one of those cases willget the nod from the Select block, but youcan't be sure of which one's going to getit. So there is no rule like in switch blockwhere the first one that matches is goingto get it, it could be anyone. So the orderingof the cases in your SELECT statements reallydoesn't matter from the standpoint of howthose conflicts are going to get resolved.Now if you do want a non blocking SELECT statement,remember that you can add that default casein there. So if there are no messages on anyof the monitored channels, then the defaultcase will go ahead and fire and so the selectstatement will process and execution of thego routine will continue from there.Okay,so that wraps up what I have to talk aboutwith channels. And really it brings us tothe end of the discussion that I have forthis introduction to go series for now. Thisis Mike vansickle wishing you luck in allof your gopher endeavors.Take care\n"