Go Pointers: When & How To Use Them Efficiently

Sdílet
Vložit
  • čas přidán 12. 06. 2023
  • ► Join my Discord community for free education 👉 / discord
    ► Learn Golang Mastery 👉fulltimegodev.com
    ► Learn how I became a self-taught software engineer 👉fulltimegodev.com/#mystory
    ► Follow me on Twitter 👉 / anthdm
    ► Follow me on GitHub 👉 github.com/anthdm
    ► Secure and easy file transfers 👉 sendit.sh
    DESCRIPTION
    ------------------------
    In this Golang tutorial I will show you when you should use pointers in Golang.
    Everything you need to know about pointers in Golang video: • Everything You Need To...
    SUBSCRIBE OR NO MARGARITAS
    ╔═╦╗╔╦╗╔═╦═╦╦╦╦╗╔═╗
    ║╚╣║║║╚╣╚╣╔╣╔╣║╚╣═╣
    ╠╗║╚╝║║╠╗║╚╣║║║║║═╣
    ╚═╩══╩═╩═╩═╩╝╚╩═╩═╝

Komentáře • 115

  • @anthonygg_
    @anthonygg_  Před 11 měsíci +1

    ► Join my Discord community for free education 👉 discord.com/invite/bDy8t4b3Rz
    ► Learn how to become a Golang master 👉 fulltimegodev.com
    Thanks for watching

  • @joseburgosguntin
    @joseburgosguntin Před 11 měsíci +94

    The length of the byte slice ( []byte ) does not contribute to the size of the User struct due to it's size, it will always be a 3 word data structure (in 64 bit systems, that means a total of 24 bytes). It will always be 3 words because golang has unified the types that rust would call the vector (Vec, as we know dynamically sized array stored in the heap) and the slice (&[T] a view). Meaning that it'll need to acomadate for the larger one of these, a vector needs 3 words (ptr, len, cap) and the slice only 2 (ptr, len). TL;DR a large array like [10000]byte would contribute to the size of the structure since it's stored right there in the struct, while a []byte only do 3 words (in 64 bit system usually 24 bytes), not matter what size it has.

    • @kruczek1986
      @kruczek1986 Před 11 měsíci +1

      User will have the same size always, you have right. but i have question about this slice still... when you pass User (no pointer) this slice will be copied? but at the end this is reference so not should be a "problem", correct?

    • @joseburgosguntin
      @joseburgosguntin Před 11 měsíci +7

      @@kruczek1986 No, it wouldn't be a "problem" in the sense that the bytes of the big file won't be copied (only the slice (ptr and len), like u said); but since this slice still points to the underlying file, be carful to not mutate it accidentally cuz it will change the contents of the file of the original User instance (and this could be bad because it could confuse most of the devs that'll use the function since mutations to a variable after passing it by value (a copy) into another function is pretty uncommon)
      Sidenote: if you do these five things in order you might get even more confused (this wouldn't happen in rust btw because you can't have a mutable borrow while having an imutable one at the same)
      1. create a slice (a dynamically sized array) `slice_1 := []byte{1, 2, 3}`
      2. create a slice (a view into the array) `slice_2 := slice_1[:]` or `slice_2 := slice_1
      3. append to slice_1 `append(slice_1, 4)`
      4. change the first element `slice_1[0] = 9`
      5. print slice_1 and slice_2 and you'll get (sometimes) slice_1: [5, 2, 3, 4], slice_2: [1, 2, 3]
      this is happening because the the new element didn't fit in the original underlying array so a new one was created somewhere else, leaving the slice pointing to old one, and this only happens sometimes because if the original slice has enough capsity it'll just put it where you would expect it to

    • @bashscript2805
      @bashscript2805 Před 11 měsíci +6

      You are right, the byte site will not affect to the User size. Anthony was wrong here

    • @3266711732665514
      @3266711732665514 Před 7 měsíci +1

      Sure, he was wrong in this specific scenario, but if it was a large data structure the explanation still makes sense, I think that this was only him trying to come up with an example on the fly

  • @ricardor8044
    @ricardor8044 Před 11 měsíci +28

    I love that you code stuff from zero to explain your concepts. It shows how good you understand of that subject

  • @lokthar6314
    @lokthar6314 Před 11 měsíci +26

    You forgot to mention one big point about pointers: Escape Analysis. The moment you reference a pointer to something and move it beyond the function scope (e.g passing a reference to another function or having a pointer receiver) the Go Compiler is moving the data to the heap. Accessing data from the heap rather than from the stack can be around 100x - 1000x slower.
    func Foo(bar *string) {} // bar is moved to the heap, when accessing bar.ANYTHING, then the runtime has to follow the pointer address and do couple of memory operations
    func Foo(bar string){} // bar is in the function stack, runtime can access bar with o(1) operation
    You could avoid accessing the heap by making use of Golangs CPU inline caching, which requires you to make decisions about locality and temporality of your data and how you access it.

    • @anthonygg_
      @anthonygg_  Před 11 měsíci +10

      Im learning each day. 🤝

    • @gearboxworks
      @gearboxworks Před 11 měsíci

      @@anthonygg_ - yes, and also potential thrashing for garbage collection if a lot of heap is used and released, constantly.

    • @Davidlavieri
      @Davidlavieri Před 8 měsíci +2

      sharing pointers down typically remains on the stack, depending on what you do with the pointer, it may go on the heap, IIRC from this talk czcams.com/video/ZMZpH4yT7M0/video.html

    • @lokthar6314
      @lokthar6314 Před 8 měsíci

      ​@@Davidlavieri this is only the case if the compiler decides to inline though, on which you can't rely

    • @lokthar6314
      @lokthar6314 Před 8 měsíci

      thanks for the great share@@Davidlavieri

  • @nas337
    @nas337 Před 11 měsíci +13

    Pretty simple explanation.
    Another possible reason to not use pointers is to reduce memory allocation in the heap. Allocation is not free operation and less memory allocation leads to less GC cycles. Coping the structure usually is not a problem because this memory will be allocated on stack frame and will be unloaded after function execution which not involve GC at all.
    Anthony thanks for the video. Now I am willing to watch a guide of zero allocation programs🙂

  • @TheQxY
    @TheQxY Před 9 měsíci +29

    A few pointers (pun intended) for new users summarized:
    - Do not use getters if you don't need to obfuscate the field. Just make the field public.
    - Do not mix value and pointer receivers.
    - As a rule of thumb, start by writing your methods as value receivers. If you really need to be able to mutate the receiver, rewrite all methods to use pointer receivers. This will generally give you the best performance and clearest behaviour.
    - When in doubt, benchmark and test.

    • @ForeverZer0
      @ForeverZer0 Před 8 měsíci +7

      Genuine question: how does passing structs around by value offer better performance than pointers to them? I am somewhat new to Go (coming from C background), but this seems fundamentally incorrect for any struct that is larger than the machine's word size. I don't discount there might be some knowledge gap specific to how the Go compiler works, and perhaps there some automagic voodoo going on in this regard, but I don't understand how that would be the case, even in theory.

    • @TheQxY
      @TheQxY Před 7 měsíci +3

      @@ForeverZer0 Because the data pointers point to is stored on the heap. Small types (including small structs) are stored on the stack, making it potentially a good bit faster. You can verify this yourself with some simple benchmarks. I learned this from Jonathan Amsterdam, one of the leads of the Go team at Gophercon this year.

    • @TobiasSample
      @TobiasSample Před 6 měsíci

      Are there drawbacks to mixing value and pointer receivers?

    • @DonFeedtehTroll
      @DonFeedtehTroll Před 6 měsíci +1

      @@TobiasSample It creates problems with interfaces. An interface expects all receivers for the implemented methods to be of the same type. If you have an interface that has 2 or more methods, mixing pointers and value gives you errors.

    • @Aedaeum
      @Aedaeum Před 4 měsíci

      ​@@TheQxYI wish this kind of information was more readily available.

  • @hamm8934
    @hamm8934 Před 11 měsíci +5

    Love the level of depth you explain at. You provide the “why”, not just the how. The use case is always the most important part and you make sure to always drill this home.
    Keep up the great content!

  • @siddhantkashyap5934
    @siddhantkashyap5934 Před 11 měsíci +2

    No one can explain any better!!! Thank you!

  • @SeanSmithPit
    @SeanSmithPit Před 6 měsíci +1

    Great video! I appreciate how you were candid about your nil return, while not being strictly idiomatic is still a personal preference. Always nice to hear how other programmers think and what they find intuitive.

  • @akionka
    @akionka Před 11 měsíci +14

    8:55 string size is 16 (ptr+len) bytes, slice size is 24 (ptr+len+cap) byte so only these 16/24 bytes get copied, the actual underlying data is stored on the heap and is not copied

    • @anthonygg_
      @anthonygg_  Před 11 měsíci +5

      Thanks you for this in depth explanation.

    • @ekadet7882
      @ekadet7882 Před 4 měsíci

      The presenter simply doesn't understand how the String type works

  • @goldilockszone4389
    @goldilockszone4389 Před 11 měsíci +1

    I am starting on my Go journey and I so of the topic you are covering are no covered by anyone. I am glad you are doing a great job ! _Subed_

  • @demyk214
    @demyk214 Před 11 měsíci +3

    Finally a youtube channel that gets to the point, u’d make a killer teacher btw.

  • @christopherchalcraft1134
    @christopherchalcraft1134 Před 10 měsíci +1

    Really love your style and pace of video. Great job.

  • @tpotjj2979
    @tpotjj2979 Před 11 měsíci +2

    Once again a valuable lesson!🙏

  • @silakanveli
    @silakanveli Před 2 měsíci +2

    One of the best explanation of why!

  • @anyDimka
    @anyDimka Před 11 měsíci +1

    Thanks for the video! Also, I use pointers when struct uses locks or other structures that holds their own state and are stored as values.

  • @dougsaylor6442
    @dougsaylor6442 Před 9 měsíci

    This definitely trips up Go newbies. Good video! 😀

  • @neufaldahouli2352
    @neufaldahouli2352 Před 11 měsíci +2

    You are great dude! Keep it up!

  • @EdgarDev5010
    @EdgarDev5010 Před 2 měsíci

    As always, what a great explanation Anthony. Keep posting valuable content!

  • @robsonandradev
    @robsonandradev Před 10 měsíci +1

    Great vídeo man thanks!
    I like to think about it as I think about passing parameters by value or by reference, when I need just the value I will probably not need a pointer, in another hand when I need the reference I will for sure need to use a pointer.
    Of course we shouldn't ignore the memory optimization.

  • @jamesarias2363
    @jamesarias2363 Před 11 měsíci +1

    thanks for the pointers!

  • @lorenzochukwuebukaobi8898
    @lorenzochukwuebukaobi8898 Před 11 měsíci +1

    Thank you so much for the explanation

  • @nekoill
    @nekoill Před 5 měsíci

    What a great channel, so happy I got it in my recommendations

  • @freivincampbell
    @freivincampbell Před 2 měsíci +1

    I am starting into this journey and I am very happy to found out your channel 🎉 thanks a lot … blessings from Costa Rica 🇨🇷

  • @CodyDostal
    @CodyDostal Před měsícem

    Thank you! As someone who has used Python for many years, I’m finally leaning a new language and chose Go. So having to learn when to use pointers and get a better understanding of them has been hard. Your video really helped!

  • @psycodreamcx7739
    @psycodreamcx7739 Před 11 měsíci

    Thanks for the video!
    I'd love a video that covers edge cases (and of course, if you want and have the time haha) to understand better different scenarios

  • @SergeyKhaylov
    @SergeyKhaylov Před 11 měsíci +1

    Thank for the video.

  • @CR33D404
    @CR33D404 Před 9 měsíci

    What a syntax!,I was just scrolling and suddenly decided to take a look on the go lang, I’m now curious about that first parentheses witch is placed before the func name does it mean that the function you define like that is going to be added to that struct or class as a member class or smth? Doesn’t go support defining methods inside the struct/class?

  • @Alexedtion
    @Alexedtion Před 11 měsíci +2

    Cool video. It will be amazing a video about AI with Golang (not directly). Vector databases is trending recently bc LLMs, the most popular ones are built with Go and use some ML algorithm, some with C++ interop but is hard to find content about this topic and even if is a great idea to boast AI models consumption with GO

  • @matthew78uk
    @matthew78uk Před 5 měsíci

    Amazing content!

  • @tomassarmiento2199
    @tomassarmiento2199 Před 9 měsíci

    What if in the database you want some attributes to be null. In the model when working with an ORM, would you set that same variable with a pointer?

  • @sujaelee2191
    @sujaelee2191 Před 10 měsíci +1

    another use case -
    When field can be optional and null value should be differentiated from empty value.
    For example, let's say a student struct can have score field, but the score can be null when the score is not yet announced. In this case 0 score should be differentiated from null.

  • @kruczek1986
    @kruczek1986 Před 11 měsíci +3

    nice! i have a question about struct if it is a service with some repository, validators etc, you know "classic" service layer. When i have constructor for it (func New) should i return pointer or not, and method for this service should be a pointer or not?

  • @smjure
    @smjure Před 11 měsíci

    Another great video Anthony. Could you please make some good video on profiling (with goroutines)?

  • @adambright5416
    @adambright5416 Před 7 měsíci

    Value receivers make copy of a struct, however, it is not a deep copy, so beware. If your struct has pointers in it (slice is a pointer to an array, so it counts as well as maps), than you will still be able to update the values... so calling method like below, will still alter user in this scenario
    func (u User) editFile() {
    u.file[0] = '0'
    }

  • @anakinkylo.thepomenerianan9084

    It makes sense to return a pointer because of the default which will be applied to an empty struct of User. So easier to check a nil reference. Prefer using pointers also

  • @Markus-qf6qi
    @Markus-qf6qi Před 11 měsíci

    Would a 3rd reason to use pointers to have properties which are nil/nullable (e.g. when working with json/rest api) or would you prefer converting the values later for representation only?

  • @edocdrah
    @edocdrah Před 5 měsíci +1

    loved the video.

  • @dawnrazor
    @dawnrazor Před 3 měsíci

    There are a couple of other issues when not using pointer receivers. 1) I read somewhere (i forget where) that for a particular type of object do not mix pointer and non pointer receivers, stick to either one or the other. The problem is most objects need to be able have some methods mutate state which means non pointer receivers are not appropriate. The other issue is lint errors. Even if it is valid to use a non pointer receiver but that object has many members, the linter will emit an error saying that what you’re doing is inefficient. For these reasons I rarely use non pointer receivers. But there is something else that troubles me about over using pointers and that is to do with escape analysis. Sometimes it is paradoxically more efficient to not use pointers because escape analysis is quicker. This is still an area I’m a bit hazy in so I need to do more research

  • @blessedpigeon6304
    @blessedpigeon6304 Před 3 měsíci

    i looove how in go the so called syntactic sugar makes things more confusing
    but i guess it's always the case

  • @XxMissingDataxX
    @XxMissingDataxX Před 11 měsíci +2

    Man please can you do a series on subjects like that “ when and how to use them” in the golang language? Example maps

  • @schroedingerundkatze
    @schroedingerundkatze Před 4 měsíci

    Start learning Go my first thought was: "Why there is no way to express that a function won't change a parameter passed as a pointer?" May be it is not easy to guarantee, but even showing this intention in the signature would help the caller and the implementer what is the right way to use/write that function.

  • @dulanjala
    @dulanjala Před 4 dny

    thanks!

  • @b3owu1f
    @b3owu1f Před 8 měsíci +1

    Two thoughts/questions. First, IDE's (and maybe linters) like to complain if you use * and no * on same struct.. not sure why this is? Seems to me for readability reasons as well as your points here, mixing * and no * should be perfectly fine.
    Second thought.. regardless of size of struct.. it seems to me that you almost always benefit using pointers. That only 8 bytes are copied every time, means faster execution. I argue that this is better because it is not horrible difficult to read code with or without * in this case. The argument that without * tells us that the code is read only.. e.g. not updating anything is really up to the developer of the receiver function to ensure they don't change anything. Anyone else calling it will benefit from the faster execution knowing that the function is not changing anything. So really the one time this is a concern is IF some rogue/dummy developer changes something but doesnt indicate they are doing so in comments.. like if you were using a 3rd party library calling a receiver function and it was assumed nothing would change, but it does.. then that's a bug on the developer of that library. So I guess in that sense, removing the concern that a developer doesn't know that they could be modifying the provided receiver object may be good, but if the receiver function is one called a LOT, and every invocation copies 9+ bytes to it, then the pointer would execute faster. I know.. I KNOW.. some will argue that execution speed is not always important and could be considered early optimizing and not necessary. I say as long as you know when writing the functions that you could arbitrarily change things.. you either make a copy locally for local use, or you just.. uh.. be a better developer and not change anything to pick up that extra speed AND less memory usage on the stack per call.

    • @anthonygg_
      @anthonygg_  Před 8 měsíci +1

      True should be fine. Unless you you are setting members without using *. Thats an ineffective operation

  • @awesomedavid2012
    @awesomedavid2012 Před 7 měsíci

    Would it be premature optimization to use a pointer in a getter to avoid making a copy? It may actually be faster to copy a small struct than perform the pointer indirection... But I am correct that without a pointer, go will simply copy the struct right?

  • @Maniac-007
    @Maniac-007 Před 8 měsíci

    9:55 “Can I type, please?” Bro you hade lost there 😂 +1 sub

  • @guidow9616
    @guidow9616 Před 2 měsíci

    Simple PassByReference vs PassByValue stuff. If you have an extension function on something without a pointer (u User) the user you use in that function is a copy of that original, and it's lifetime ends when the function ends. If you use that it would be better to return that user.

  • @carriagereturned3974
    @carriagereturned3974 Před 6 měsíci

    pointer is an address. When to use it?
    Use if: want to change value of variable stored at that address.
    Typically you pass variable's value to function, func cannot change its value, because func doesn't know address.
    So if you want to change var's value - pass pointer (a.k.a. address). Pointer's value is literally (actually) an address, only it is called a pointer (so it is a type, and a word you can refer in your speech).

  • @wMwPlay
    @wMwPlay Před 11 měsíci

    I prefer named return values, so I don't have to type User{}. But I also avoid naked return.

  • @user-cb3le9oh9p
    @user-cb3le9oh9p Před 5 měsíci +1

    Off topic I know, but I love your terminal color scheme. What is it?

  • @rockandrolldevil665
    @rockandrolldevil665 Před 3 měsíci

    awesome

  • @JohanStrandOne
    @JohanStrandOne Před 11 měsíci +2

    *Commenting for engagement*

  • @RA-xx4mz
    @RA-xx4mz Před 11 měsíci +2

    I just fixed a MASSIVE edge case bug because someone was using a pointer when they should have been using a copy of a value.
    Pointers are dangerous!

  • @annguyen6166
    @annguyen6166 Před 11 měsíci +1

    Love your video, but are there any rule for attribute, when the attribute should be pointer ?

    • @anthonygg_
      @anthonygg_  Před 11 měsíci +1

      Good question. Lets dive deeper in another video.

    • @bramvanneerven
      @bramvanneerven Před 9 měsíci

      @@anthonygg_ Would be nice if you could cover shallow vs deep cloning in this case as well. I sometimes need a freezed/cloned representation of the struct to e.g. send it into a gRPC stream (because editing the sent struct after sending it is not safe). What do you do in this case when the struct contains pointers?

  • @professorgoodwin
    @professorgoodwin Před 9 měsíci +4

    1. Slice always 24 bytes, because it's a pointer to underlay array, lengrh and capacity.
    2 . When you use pointer you get Escape to Heap and GC. You should keep it in mind.
    3. What Go will do if you call non-pointer receiver from pointer or vice versa. It's an interesting stuff, that you did not explain in this video. In two words if you have pointer receiver and call it from non-pointer object it will work fine - Go create reference under the hood and vice versa. So, this is NOT just a syntax sugar.
    In your example , if I wtire something like:
    u := &User{email:"email"}
    s := u.Email()
    it will work correctly.
    Resume : probably, you should better learn Go before teaching other people

    • @timhilt1185
      @timhilt1185 Před 8 měsíci

      Exactly. Receivers allow access to private struct members. If he would have run the „non receiver implementations“, it wouldn‘t have compiled.

    • @masterchief1520
      @masterchief1520 Před 8 měsíci +1

      ​@@timhilt1185could you please explain a bit better I got confused reading the above explanation

  • @avi7278
    @avi7278 Před 4 měsíci

    coming from a Typescript background and also dart/flutter. I'm exploring Go and find your videos quite helpful. But I donno is if it's just me but Go makes me ask what and why tf a lot. Also, you messed up your first example right? whether it's updateEmail or UpdateEmail is irrelevant in respect to your example, correct? Maybe on your first example you didn't mean to create a method but rather just a function that you pass the user to?

    • @oktay9784
      @oktay9784 Před 2 měsíci

      Go is a case sensitive language. So anthony created two functions called updateEmail and UpdateEmail. Probably (but not for sure) if they were both exactly the same name, linter would throw an error and show the line in red.
      Also, you can access the updateEmail function from any object you create from the User struct, but the same is not true for UpdateEmail.

  • @phoehtaung
    @phoehtaung Před 11 měsíci +1

    Would you be able to share the theme you are using?

  • @vitiok78
    @vitiok78 Před 10 měsíci

    I use the rule that never lets me down. If one method of a type uses a pointer receiver then in general you should use a pointer receiver in all of the methods. It is just safer.

  • @AmirGolmoradi
    @AmirGolmoradi Před 6 měsíci

    Which Vscode Theme are you using ?

  • @elisiosa3111
    @elisiosa3111 Před 10 měsíci +1

    Anyone knows what theme and font is Anthony using?

    • @anthonygg_
      @anthonygg_  Před 10 měsíci +1

      Font: consolas theme: Gruvbox

  • @brickshick
    @brickshick Před 10 měsíci +1

    How can you be in great shape and at the same time learn coding? Please teach us some time management my lord

  • @AchwaqKhalid
    @AchwaqKhalid Před 8 měsíci +1

    Please use a bigger font for your future videos 💡
    Edit: fixed ✅

  • @dj-yv7oi
    @dj-yv7oi Před 4 měsíci +1

    quick question, anthony, why do you hate getters and setters ? 🙃

    • @anthonygg_
      @anthonygg_  Před 4 měsíci +1

      No clue, need to rewatch. I never use getters and setters. So it might be some educational case

    • @dj-yv7oi
      @dj-yv7oi Před 4 měsíci

      @@anthonygg_ i been thru comments and some possibility is that you can just make struct fields public and modify/get them that way 😅

  • @tobiasprimatjahja381
    @tobiasprimatjahja381 Před měsícem

    The “Can I type please” makes me laugh

  • @minciNashu
    @minciNashu Před 10 měsíci

    So copies are actual copies of objects, instead of copies of references, as expected in most GC languages.

  • @Luix
    @Luix Před 4 měsíci

    interesting. As much as I like Go I think Java does the setters better.

  • @MartinBenesh
    @MartinBenesh Před 6 měsíci

    You are making assumptions regarding go's memory management that are unfortunately wrong. Passing by value doesn't necessarily mean copying the whole struct. For instance your example with 'func(u User) Email() string' would most likely be inlined. In fact the compiler goes extra length, to try and keep structs on the stack and making it a pointer pretty much forces it to escape to heap.
    It would be interesting to test, when size of struct passed on stack would actually become slower than heap. My guess would be, that only for structs with impractical number of attributes, or a massive array (not a slice - that one is internally a pointer).
    I would suggest sticking to: use value types by default, only use pointers when you actually need the pointer. that is, when you need to share the data.
    And please, test your assumptions before you claim them as facts.

  • @pablo1gustavo
    @pablo1gustavo Před 10 měsíci +1

    what is this vscode theme?

  • @AHN1444
    @AHN1444 Před 3 měsíci +1

    When the parameter is an slice ? don't need to pass as pointer because the slice is already a pointer?

  • @JugaadTech
    @JugaadTech Před 9 měsíci +1

    I have seen you somewhere, are you a doctor or plumber as well by any chance

    • @anthonygg_
      @anthonygg_  Před 9 měsíci +1

      Dont forget the fireman and the astronaut.

  • @sunilanthony17
    @sunilanthony17 Před 9 měsíci

    Thank you, love it but your typing raises my OCD to a different level. 🙂

  • @dixztube
    @dixztube Před 2 měsíci

    How come e-mail isn’t e

  • @axMf3qTI
    @axMf3qTI Před 3 měsíci

    Is it good practice in Go to make getter/setter methods for structs?
    ..Oh haha 2 secs after posting he says he hates getters and setters so I guess not.

  • @choleralul
    @choleralul Před 11 měsíci +1

    How Long is a Chinese

  • @shadyabhi
    @shadyabhi Před měsícem

    This video didn't answer the most basic question, "how big is big when the overhead of copying the whole struct is larger than garbage collection"? Also, garbage collection overhead is one of the biggest reasons when discussing this topic.

  • @vyacheslavgorbov6652
    @vyacheslavgorbov6652 Před 8 měsíci +1

    9:50 , 1 gay bee.

  • @Dozer456123
    @Dozer456123 Před 4 měsíci

    lol "I hate setters and getters", proceeds to make a setter with a different name.
    Immutability is really the only way to write go code that is tolerable