Check out a free preview of the full Polyglot Programming: TypeScript, Go, & Rust course

The "Projector Object in Go" Lesson is part of the full, Polyglot Programming: TypeScript, Go, & Rust course featured in this preview video. Here's what you'd learn in this lesson:

ThePrimeagen walks through creating the Projector object in Go. The functions getConfig, getOperation, and getArgs retrieve the needed information or throw errors, and the NewProjectorConfig creates a new Projector object.


Transcript from the "Projector Object in Go" Lesson

>> Let's go do this, man that pun will never get old, right? All right, so I'm gonna go back into my projector folder, so remember I'm in package projector. A little bit different than the typescript world and I'm gonna add the word config.go. Now, something that's kind of interesting in here is, notice that I'll use the term package projector, right?

Because that is my containing folder, right? It is projector but look at what's kind of neat about that is I like this type Foo struct and just do that really quickly I'm gonna jump back to my main file. Noticed that my projector file now also has Foo but it also has the Opts.

So all files underneath the same kind of sub package of package, they will share all of their exports. But it also means you get access to some private things, which is kind of neat so you can kind of do some nice splitting up of code things is a little bit easier to manage within a package.

I'm trying to think of the correct, I think it's equivalent to the Java no scope, right? Everything within your package can share it, but everything on the outside can only be access to the public. I believe that is what it is, it's a weird scope, right? It's not a easy scope to kind of reason about but that's effectively it.

So, let's jump back, I'm gonna remove that really quickly and remove that. So let's do the exact same thing. We're gonna create a operation enum, and then we're gonna create a config struct, and then we're gonna fill in all those values that we need. So exact same principle right here.

So let's go type Operation equals int, and then const Print Operation equals iota. Have you ever seen Ace Ventura? When nature calls, when Ace Ventura has the spear in his leg and he says, iota don't just stand there throw me a spear. That's what I think of every single time I do.

I know this is a Greek word but for me, it's just like that scene pops up in my mind every single time I do that. Don't just stand there, throw me as beer, it's just the greatest lie, okay? Anyways, that was weird. So now, that we got that done let's do the config as well, and so let's create another type Config struct.

And so we'll do the exact same kind of setup because go doesn't allow you to do something different. Now, if you're thinking about Rust we could probably do something a little bit different here, right? Because we have access to these some types. So, we can be a lot more clever about how we store information whereas with this one right, we have to kind of go with this whole array approach.

But the array has multiple or single values or no values so we'll see, that's why I feel like Rust is less error prone cuz you can be a little bit more specific with your types, all right? So let's go here, type Config struct, we'll have Operation, remember always have to be capitalized, which is an operation odd looking.

Let's do a present working directory, which will be a string lowercase, and we'll have a Config which will be string lowercase. And let's also have at the top a string array, so there you go. That's what we need, of course, proper indenting is always desired by go. So there you go, they like to align those.

If you don't like the alignment, you can always add an extra space in there, re-execute it and then it'll align various groups. Because every now and then you'll get just some silly one that just goes on for a while. And then you try to do that and you're like, gosh, I hate this, I hate that.

And so put a space if you need a space. All right, so there we go, we have the exact same thing. Now, we have to build the same methods, we already know the same methods we need. So I'm just gonna write them all out in order, do the export and then we'll work our way backwards, right?

So we need a first, we need to get present working directory, which is gonna return a string or an error. Because remember everything in go just always assume there's probably going to be an error. You'll be a lot happier in your life. Let's do the exact same thing, Config but we also need the opts what is called, opts there we go, we want that one, right?

There we go, we'll do that with ConfiggetOperation. We'll assume that this one is probably no error, right? That's just seems right. And then we already know that getArgs will definitely have an error, right? We've already pre-planned it, it should be a string, array, or wrong side for the array.

Remember it's, Error, there we go. That seems about right those are the same methods we had before, so hopefully we can just go through now, and just do it easily. So I'll go like this func NewConfig and that's gonna return out a pointer to a Config or an error, awesome.

I'm getting no LSP answers, when I see nothing's getting any LSP problems, I just assume there's a problem, and of course, I added all these colons. Don't add colons, they're not good for your health, there we go, fantastic. Always a good identifier, you mess something up is when the LSP stops working that's why I got so worried when Rust wasn't working, I was like man, I'm screwing something up but I don't see it.

All right, so let's just start from present working directory. We'll work our way down because we're gonna have a lot of errors down here, and I don't want to try to guess what that's gonna look like. Let's just get it all done up here first. All right, so of course, standard thing if opts present working directory is not an empty string.

Just like before, right, we do that and a nil. Fantastic, pretty easy to do. The other one was just not empty or didn't exist, this one's just simply an empty string. So, if it is an empty string, we need the present working directory. Now, we have this package called OS that has, I wanna say, it has the word Dir in it, current, my goodness, present Get working directory, that's what it is.

So you just have to remember what these things are, there we go. Spend a little time letting your brain remember what it could be. So we're gonna have present working directory and an error. So I was right, of course there's always an error. So let's do this except for we don't return nil we can just return an empty string, awesome.

Else we have our present working directory let's return present working directory and of course nil. Now, if you look at this thing though, what does it return? It actually returns the exact interface we're returning. So we can kinda shortcut this whole process and use its two arguments it returns for our two arguments.

So someone else can handle it, kinda like a nice little shortcut. So if something returns two arguments, you return two arguments. They can kind of flow through, it feels good, right, feels good. So I'm actually gonna just take this whole thing and I'm gonna go down to getConfig, replace present working directory with Config, if a Configs there we'll just use their Config.

But at this point we have to do something a little different, let's go. I'm gonna call this XDG, and of course, we're gonna have err and we're gonna go OS.GetUser, there we go, UserConfiDir. So this actually has a nice little strategy where they actually have their own method for you so you don't have to know a lot about it.

I find this to actually be pretty nice, right? That they provide this for you. I think this is why go is often used in a lot of tooling, is that it has a lot of nice little functions for handling these differences. It also has a really powerful add parser, the one we use is really simple.

But if you ever wondered how docker has like all these different positional ones with different flags and all that, they have ufav-CLI parser I think is what it's called. Ufav, yeah, ufav-CLI something, it actually has it, it's really amazing. So I really appreciate a lot of the go kind of CLI stuff.

So anyway, so we have this. We obviously have the ConfigDir here, we'll call it just Config for now. And we have the err if there is an err, we do need to return an empty string. If there's not an error, now we can do something with it. Remember we have to mutate it, we have to add in extra stuff.

So I'm gonna return a path, this looks really familiar, right? Is it Join? Look at that, it's the same thing. So we can go Config projector, right? Projector.json, right? So I didn't add nil. So notice that I did not do the home one, we could do the home one right here.

That is an option, but we can just also cut it right here. However, you guys want to do it, add in the things you want. I don't think there's a lot of value in me showing you, yes, you do an else here and then do this again, right?

All right, awesome. Exact same thing with operation we do pretty much the exact same code, right? So I'm gonna go len opts,Args, if it equals 0 return print, awesome. Else we need to kinda do that same kind of error checking, so I'm gonna go if opts.Args 0 equals add, well, then we need to do if opts.args, length does not equal 3 then we got do some stuff right.

Here's the confusing part, no, wait, that's I'm doing the wrong one return, not yet, sorry, not yet, I'm doing the wrong one there let's go like that print this and go. If it's RM do remove, else, return Print, there we go, awesome. This is the operation I was doing the Args and all that.

Awesome, so let's pretty much grab. I'm gonna grab that whole thing and go down to Args and paste it in here. If it is an empty one, well, I just simply need to return back out an empty array, right? Awesome. But this one wants to have an error.

So, I'll throw a little error in there, fantastic. Now, let's do the exact same approach we did in the typescript one. If the Args is 0, and it's an add, then we need to go if Len opts, hey, no, Args up, I've lost autocomplete for whatever reason does not equal 3, well, what do we need to do?

Well, we can return a nil, and then now we need to return an errors. There's two types of errors. I kind of showed you one earlier, we either format a new error right here or we can use the errors package, right, we can use the errors package. Is there one that I strongly prefer look even tab nine is just telling me right there this is what you should do.

And so we could use this, hey, that's really nice look at how add requires 2 arguments but got, and then we could go through and do this. But for this one, let's just use a format F because it's really easy, it's nice. So ErrorF, add requires 2 arguments, but received that len opts.Args, awesome.

So now, we just simply need to return this. So we just need to take the slice of the remaining Args. Go has an amazing thing that I wish typescript had. We can just say, hey, I want that, right? I like that they have these things available, I think it's really clever.

It's just something I've grown to appreciate is having these slices available. There you go, you just take that you just have it. We need to do this exact same thing now with remove, right? So let's just turn it to remove decrement it wants decrement at once, same code.

Now, we remove that. Wow, all right, as it's about as easy as it gets, right? We could probably be open, we don't wanna do that. We could make this a little bit more clever and I'm sure I could produce this into some sort of length checking type item and then we don't have such duplicate code.

But rule of three, Martin Fowler, I'm only doing this twice so I'm not gonna refactor it into something smart, there you go. You can be smart though if you need to and so the last one we'll just use is Print, right? Last time I think we had RM as the last one, we'll just do print as the last one this one.

So if length of opts.Args doesn't is greater than 1 then we have ourselves a bit of a problem, right? And print this out, all right, so if the length of our Args is greater than 1 we have a problem, we expect print requires 0 or 1 arguments, but received this much.

And I realized up here that we forgot to do the minus one, that'd be very, very confusing for any of err users here. And we're way over 80 characters, which is considered a mortal sin of mine, all right. So there we go, we've done that now one thing I noticed that we're doing in this one that I just kind of blazed through not really thinking about, is that I'm actually checking what type of operation is going on here.

So I could use this instead we could grab that out probably is a bit better just because you don't really want to be in the business of doing the same logic twice. It just feels a little bit cleaner. So I'm just gonna do that right now, operation just cuz it makes me feel better about my life, okay?

All right, and so let's go, operation equals Add, there you go Remove and there we go, awesome. So we have everything we need, return and then of course we're just gonna say opts.Args nil, fantastic. So that did everything we needed to do and all we have to do now is just fill in the new Config.

And so just like I told you if we have a bunch of errors we really have to think about that beforehand, that's why we're just gonna have this pretty consistent interface across everything. Which is gonna just look, let's just do them one at a time, right? So present working directory, err equals get present working directory, and just pass in of course the opts in which we forgot right here and handle the err.

Config, err equals getConfig opts, handle the err, right? Look at this, we're just flying through this. It's like it's just so easy. Print it again, let's have Args ,getArgs. There we go, handle the err. Now, all we need now is just simply to return a Config. Remember don't forget the pointer parts and of course a nil for err and do present working directory, present working directory.

Config as config, operation as getOperation because we don't actually need to worry about an error on that one, and of course Args as Args. Put that in there and there we go. We've actually done it, we've written the entire thing a little bit more boilerplate but I also liked the fact that we're not doing exceptions as control flow.

>> For me I've never been a big fan of that but this feels pretty nice right we got everything we need to do done. And if we go back to our main file, we can obviously go config, err projector, NewConfig pass in the opts, yanked out thing paste it down here.

What is this one? Err, yeah, okay. Well, I didn't put pointers everywhere people, I forgot the pointer part here. So, let's get that done really quickly. Everyone, I hope you've done pointers maybe you guys are better at paying attention than I was. [SOUND] There we go, awesome, exact same error pops up again, unable to get the option unable to get the config There we go, we have this beautiful little config now.

Now, we can even print this out just to make sure everything is working let's do a nice little go run do this and it says, hey, you have an unused import error. So there's something about go that's pretty interesting which is if you don't use something it will always add error.

And so let's go back to our config, remember when I was showing you the errors package, I imported it. I'm no longer using it, you may not compile. All right, there we go, we got a beautiful thing. Hey, we received, print requires 0 or 1 arguments, but we received two that's exactly right.

I did test foo, I did all that. So this is perfect, we exactly what we wanted to see. So if I reduce this down, we should see this beautiful Args that operation print present working directory bass config bar, fantastic, right? How does everyone feel about that? Feeling good?

What do you feel about go, cuz it's kind of it's different, right? It's not, I'd say it as easy or as loosey goosey as typescript. Definitely not as academic or difficult as Rust but it's still fairly fast, right? Maybe the error handling you don't like and it's kind of like everywhere at all times I don't know.

I don't really mind the error handling, I know people get really worked up about it. But just like, I personally would rather have everything show me it has an error, and I don't have to think about it, then something just like, it turns out that breaks every now and then, right?

Like that kind of sucks, like JSON.parse. It just doesn't tell you an error, but it can certainly error and take things down. So you should just know that it can error, I'd rather have it come out. Personal feeling, maybe people don't share the same sentiment. How do you feel about go, any any positives?

Cuz I feel like people rag on go a lot for this exact reason, but I don't look at it as a as a negative, right? I am fine with this.
>> It seems like there's a lot of boilerplate but that it's the syntax is pretty tight. It's like pretty concise

>> Yeah, that's how I feel, right? It's very simple syntax, you have to memorize just a little bit to be able to make the jump from typescript to go, and work go really wins which we're not even seeing is concurrency. It is just, you cannot use four letter words on front and master but it is amazing how easy it is to do that.

If you had some long running thing that was gonna be operationally intensive, the fact that I can just go, go func and go do something, boom, I'm concurrent all of a sudden, right? I'm doing things, that's amazing. And if you do it in Rust, you're like installing packages, your main has to be different It's hard, right?

It's really easy to screw up and deadlock yourself, go is pretty straight forward as long as you're not doing mutes you're pretty much never gonna deadlock yourself. The moment you do mutases that's multiple mutexes not to be confused with mutalist from Starcraft2. But if you do mutases you can also deadlock yourself.

It's a lot of fun, it's good times anyways. So onto my favorite of them all, which is beautiful Rust. Is everybody ready for some beautiful rust? I really didn't feel that, but I'm just gonna say you guys were a static. Twitch chat you're not here right now, but you should have saw them front end master chat.

You're not here right now, but one person even fist pumped, it was incredible. It was amazing moment of life, all right. So we're gonna go to our, that we always do. And we first need to say, hey, this is a new module we'll be using. So I'm gonna copy and paste that, and I'm gonna throw in the word config.

And I'm gonna say, hey, that doesn't exist I know we're lying to it at the moment. So let's create a, beautiful. Now, it should exist notice that it's saying, hey, it does exist, we're not getting that weird file not existing problem.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now