One of the most common mistakes in Go is about to be fixed

  Рет қаралды 27,238

Dreams of Code

Dreams of Code

3 ай бұрын

They finally fixed it.
This mistake is one of the more common mistakes you can make when writing Go code. The Go developers have realized this, and so they're going to be resolving it in the next major release of Go.
We don't have to wait until then, however, as we can actually test the fix today.
Become a better developer in 4 minutes: bit.ly/45C7a29 👈
Video Links:
Golang FAQ on the issue: go.dev/doc/faq#closures_and_g...
Blog Post with Fix: go.dev/blog/loopvar-preview
Join this channel to get access to perks:
/ @dreamsofcode
Join Discord: / discord
Join Twitter: / dreamsofcode_io

Пікірлер: 108
@megaxlrful
@megaxlrful 3 ай бұрын
I haven't experienced this bug "in the wild" but it's one of the larger footguns in Go. I am very satisfied with the way the Go team has decided to fix this bug. Very nice!
@kallekula84
@kallekula84 2 ай бұрын
not really a bug in go code, if you reason about it the feature works as expected and is why functions accept arguments, this is more of a gotcha and skill issue. But if you think about it, it would be like declaring your variables in the global scope and then be surprised when you see race conditions in the end result...
@LordHelixe
@LordHelixe 3 ай бұрын
A little note: The call of wg.Done() should be wrapped in a defer and put as the first line in the goroutine. Otherwise a deadlock can occure if the code inside of the goroutine panics. Defer makes sure the wg.Done is called regardless of any panics.
@kevgoeswoof
@kevgoeswoof 3 ай бұрын
If a goroutine panics, the whole app crashes (unless you use recover). You should still use defer, so if you return early by accident in the goroutine, it still reduces the WaitGroup counter and won’t deadlock.
@cat47
@cat47 3 ай бұрын
oh thanks for that tip. been coding go for years and i always just did wg.Done every time i wanted to return
@dreamsofcode
@dreamsofcode 3 ай бұрын
+1 for using defer for wg.Done() :)
@darrennew8211
@darrennew8211 3 ай бұрын
C# had the same problem, and it was a big deal to make it a breaking change such that it doesn't happen again. Basically, the "for" loop gets desugared to putting the declaration of the variable inside the loop instead of once on the outside. I ran into it trying to test five different beeps for some xbox code, and kept getting the same beep each time, and it took me a day to figure it out.
@conaticus
@conaticus 3 ай бұрын
Great video! Glad the Go team decided to fix this, seems real easy to miss
@knolljo
@knolljo 3 ай бұрын
rust does this very elegantly with moves
@krzysztof-ws9og
@krzysztof-ws9og 3 ай бұрын
gotta love rust's borrow checker
@mick_io
@mick_io 3 ай бұрын
I find it’s good practice to pass any variables that will be used my a go routine as arguments
@dreamsofcode
@dreamsofcode 3 ай бұрын
It most certainly is!
@OldKing11100
@OldKing11100 3 ай бұрын
This bug got me so badly when working with Fyne GUI. I was working with generating a list of buttons. That was an hour of my life questioning my own existence.
@prashlovessamosa
@prashlovessamosa 3 ай бұрын
Hey Bud, please make more Golang Stuff.
@dreamsofcode
@dreamsofcode 3 ай бұрын
Absolutely!
@NigelAtkinson256
@NigelAtkinson256 3 ай бұрын
I managed to hit the same concurrency style bug in Lua once. That was from the nested co-routine function using the global instead of a closure. All my UI buttons ended up sending the same id. Fun times. 😆 Great video - looking forward to 1.22
@Wock__
@Wock__ 3 ай бұрын
I've personally made this mistake more than once. Even without concurrency it's easy enough to capture the loop variable inside a closure that will be called later. The for .. range syntax also makes it less obvious of an issue than the C-style for loop. Luckily both times had pretty obvious side-effects and didn't take too long to find, but it always leads to the most misleading and confusing outcomes. As you mentioned, it's hard to imagine a case where someone would want to capture the loop variable, so I welcome this change.
@hiimgood
@hiimgood 3 ай бұрын
I remember making a tkinter applet using python, and when binding a method to buttons using for i in range loops, every single one of them behaved like it what the last one, so I guess in python it is also not very rare
@JustKatoh
@JustKatoh 3 ай бұрын
Have been met with this problem more than once as I prefer goroutines more often than not. So happy 1.22 is going to improve upon this :)
@dariusatudore979
@dariusatudore979 3 ай бұрын
Your videos always look so good. What are the fonts and themes you use?
@dreamsofcode
@dreamsofcode 3 ай бұрын
Thank you! Poppins for titles JetBrainsMono or Monaspace by Github for code Colorscheme is Catppuccin
@oblivion_2852
@oblivion_2852 3 ай бұрын
I have seen this bug more than once. Most languages solve it by forcing the developer to explicitly pass all required variables to the closure. Which can be annoying if you have a number of variables outside the loop scope and only one loop scoped var. I like the fact they're making a hidden pass by value to the closure instead of just lazily using pass by reference especially when coroutines have latency overhead
@vytah
@vytah 3 ай бұрын
"Most languages"? I can recall only C++ doing that.
@lucass8119
@lucass8119 3 ай бұрын
@@vytah C++ does not do this. You don't have to explicitly state which variables you pass to a lambda. You just need to state if you intend to pass by value [=] or by ref [&]. You only specify specific vars if you mix and match.
@andantebass6113
@andantebass6113 3 ай бұрын
What video editor do you use? Your videos look amazing!
@dreamsofcode
@dreamsofcode 3 ай бұрын
Thank you! I use Davinci Resolve.
@wandermaus4571
@wandermaus4571 3 ай бұрын
Your video editing skills are impressive! Are you using Linux for editing, and if so, could you share the tools you utilize? The videos look fantastic! :)
@dreamsofcode
@dreamsofcode 3 ай бұрын
Thank you! I use Davinci Resolve for most of my editing, which is available on Linux! In other videos I also use After Effects which is not available for Linux unfortunately. I highly recommend Davinci! Its been a joy to work with. I also have a bunch of other tools and effects I made myself to speed up the process.
@wandermaus4571
@wandermaus4571 3 ай бұрын
@@dreamsofcode Appreciate your input! :) Explored Davinci Resolve, and it looks promising. Adapting to a new tool can be a bit challenging initially, but I'm up for the learning curve!
@iamtheV0RTEX
@iamtheV0RTEX 3 ай бұрын
Yet another problem that can't happen in Rust. If you move a shared reference into a closure on a separate thread or an async future, then that variable needs to have a static lifetime and cannot be mutated while the closure exists; which means you can't have multiple closures all referencing the same value that you're trying to update in a loop. The compiler would reject this as invalid and force you to either specify a value move (with the "move" keyword) or some kind of synchronization structure (like an Arc or similar).
@TheClonerx
@TheClonerx 3 ай бұрын
Neither in C++, lambda captures are even more explicit there
@jongeduard
@jongeduard 3 ай бұрын
@@TheClonerx This is one of the rare things in C++ that may actually be better than Rust indeed, the fact that you have a per variable setting instead of 1 single move keyword for all variables at the same time. It's quite long ago that I used C++, but that specific thing was pretty nice, and if possible, it would be great if Rust got a similar feature. However C++ does not at all protect you from any concurrent usage or early drops of your references from memory. It's still extremely easy to mess things up without noticing.
@jongeduard
@jongeduard 3 ай бұрын
@@TheClonerx Indeed pretty nice in C++ that instead of a single move keyword you have a reference vs value setting per variable. A bit long ago now that I wrote something in C++ but that specific feature was pretty nice, and maybe they should introduce a similar thing in Rust. If possible. However C++ does not protect you from any data races or early drops of your referenced variables from memory. It is still extremely easy to do things wrong without noticing.
@orbital1337
@orbital1337 3 ай бұрын
@@TheClonerx Ehhhh. I would agree if we're talking about ordinary lambdas. But there is one massive footgun which is if your lambda is a coroutine. Then, there is no guarantee that your captured variables are still valid while the body of the lambda executes. That's because the captures are not stored in the coroutine frame but in the lambda object which can go out of scope before the coroutine finishes.
@AnthonyBullard
@AnthonyBullard 3 ай бұрын
@orbital1337 one thing to love about goroutines is that the lambda and goroutine are the same object (if I remember correctly, and if not that’s functionally true)
@jogurtnaturalny
@jogurtnaturalny 3 ай бұрын
Go Golang GO
@amyshaw893
@amyshaw893 3 ай бұрын
I keep getting hit by this in c# when using delegates in a loop. After doing some research, the team were aware of this and have decided not to do anything about it, so I've just got to remember each time when I'm having weird bugs lol
@georgehelyar
@georgehelyar 3 ай бұрын
Is there a way to define closures as non-capturing in go? i.e. to force you to pass in through arguments? I know captures in other languages can be expensive anyway but I don't know about go.
@AnthonyBullard
@AnthonyBullard 3 ай бұрын
Not without you writing your own linter. Closures are always more expensive than normal functions since they require allocation of a backing struct to store the captured environment(unless the closure is called immediately and then you can do an optimization to pass the captured variables into a rewritten function, goroutines make that impossible)
@mr.togrul--9383
@mr.togrul--9383 3 ай бұрын
This is great, glad to see golang getting better. Do u guys think they should also fix possible nil dereference problems by adding option type to standard library?
@dreamsofcode
@dreamsofcode 3 ай бұрын
Id love them to fix nil dereference! I have a video idea in my backlog to build out a nil safety package using Optionals.
@AnthonyBullard
@AnthonyBullard 3 ай бұрын
The biggest thing I don’t enjoy about Go is the way that pointers are used for so many purposes: performance, nillability, and mutability. I wish that the perf part was figured out statically, and the other two were separate syntactic constructs.
@AnthonyBullard
@AnthonyBullard 3 ай бұрын
And let’s not get into interface values
@TehKarmalizer
@TehKarmalizer 3 ай бұрын
Optional types are just window dressing for the same issue. You still have to handle the cases differently where you have a value or you don’t.
@AnthonyBullard
@AnthonyBullard 3 ай бұрын
@@TehKarmalizer I think you aren’t understanding the point. If you have a semantic construct for optionality (nillability), you are forced to handle it for only values that are marked as such - meaning you don’t have to worry about it elsewhere. Separating those semantics from passing a reference for mutation purposes, or for perf purposes is valuable.
@vaisakhkm783
@vaisakhkm783 3 ай бұрын
yeaterday started learning go, and it' great i came to know about this foot gun early :)
@throwaway3227
@throwaway3227 3 ай бұрын
Shared mutability should never be the default...
@LeonarEuler-iz5di
@LeonarEuler-iz5di 3 ай бұрын
Tell that to C :D
@throwaway3227
@throwaway3227 3 ай бұрын
​@@LeonarEuler-iz5di If you wrote all programming languages on paper pieces and put them all up randomly on a wall and threw a dart, you would most certainly hit a language which does shared mutability by default...
@jongeduard
@jongeduard 3 ай бұрын
🦀
@guavavodka
@guavavodka 3 ай бұрын
what theme is this? it looks like catpuccin but the symbols are a different color
@antonaparin
@antonaparin 3 ай бұрын
RIIR and problem fixed :))
@Fomoerectus-wu1xefom
@Fomoerectus-wu1xefom 3 ай бұрын
Maybe crazy question, but what color theme is that :D
@dreamsofcode
@dreamsofcode 3 ай бұрын
Catppuccin!
@Fomoerectus-wu1xefom
@Fomoerectus-wu1xefom 3 ай бұрын
thank you kind ser@@dreamsofcode
@duckhuntergaming4713
@duckhuntergaming4713 3 ай бұрын
Mate honesty I might come off like a grumpy old time C hacker, but when we were being taught concurrent programming back in my day, there was a clear understanding and separation of state that is thread local or differently. *These kids of today have it too easy*. But in all seriousness, never could i imagine that a language would change to prevent this type of error. Maybe its for the better.
@itztlacoliuhqui
@itztlacoliuhqui 3 ай бұрын
Can i ask what theme do you use here?
@perz1val
@perz1val 3 ай бұрын
Probably catpuccin
@clementdato6328
@clementdato6328 3 ай бұрын
I think it is not the reference or value distinction but name or value distinction. It does not pre-put the referenced variable in the function, it only looks at the value of the NAME when actually used. I think the reference example is irrelevant
@uri.
@uri. 3 ай бұрын
😔
@ebouls9210
@ebouls9210 3 ай бұрын
Do you have courses in Golang ?
@AceofSpades5757
@AceofSpades5757 3 ай бұрын
Python has a similar issue I've run into when creating a closure in a for loop using one of the iterators. The closure will always use the last value.
@vikingthedude
@vikingthedude 3 ай бұрын
Js has the same issue when using var
@sledgex9
@sledgex9 3 ай бұрын
To be fair to fall victim for this bug in C++ you must be explicit and intentional. You must use a reference and/or pointer. In GO that's hidden. In C++ an assignment usually copies the value, unless you explicitly take a reference/pointer. However, the problem GO has (or had) should be compared with the same thing in Javascript.
@kisaragi-hiu
@kisaragi-hiu 3 ай бұрын
Expecting for loops to give each iteration its own scope (while they actually don't) seems to be a common mistake across languages. Changing the language itself to actually give each iteration its own scope is one of those idealistic solutions to this issue, and I'm surprised Go is able to make the change. Kudos to Go for this! And then there's Common Lisp: dolist and loop are both able to do iteration across a list, but dolist scopes per iteration yet (loop for x in lst ...) does not. (let (funcs) (loop for x in '(1 2 3) do (push (lambda () x) funcs)) (mapcar #'funcall funcs)) ; 3 3 3 (let (funcs) (dolist (x '(1 2 3)) (push (lambda () x) funcs)) (mapcar #'funcall funcs)) ; 3 2 1 ;; in the same language lol
@RenderingUser
@RenderingUser 3 ай бұрын
Why is 5 still printing first? Shouldn't it print in order?
@dreamsofcode
@dreamsofcode 3 ай бұрын
This is a great question. The answer is: Concurrency. There's no guarantee of order when running concurrent closures (go routines). This is the same as with threading. The scheduler is responsible for executing tasks. Likely in my case the closures of 1-4 were scheduled on one thread and the 5th closure on another. This caused 5 and 1 to run first with 5 winning out.
@RenderingUser
@RenderingUser 3 ай бұрын
@@dreamsofcode I see. Thnx
@roboticbrain2027
@roboticbrain2027 3 ай бұрын
It's very suspicious that the last item is printed first. Does the go compiler do some tail call/loop unroll optimization where it uses the main thread (which would enter a blocking wait anyways) to process the last go routine? This would save one context switch for the scheduler.
@dreamsofcode
@dreamsofcode 3 ай бұрын
@@roboticbrain2027 I just ran the code about 10 times to see if it was a repeatable pattern. 9/10 times it was the same, 5,1,2,3,4 however on the 1/10 time it was 5,3,4,1,2. In the 5,3,4,1,2 my estimation is that [5], [3,4], and [1,2] were all scheduled on different threads. This could also be the case for [5], [1,2], [3,4] as well (meaning two closures per thread). Edit: Played around some more. If you set the max number of threads using: GOMAXPROCS=1000 GOEXPERIMENT=loopvar go run main.go then the randomness is even more guaranteed.
@tears_falling
@tears_falling 3 ай бұрын
👀
@JeanBagarre
@JeanBagarre 3 ай бұрын
Erm, sorry to ask, it might sound stupid for anyone that know Go, but I can’t understand one thing, so I’d be glad if someone can explain it to me: In the corrected example, the output printed is 5,1,2,3,4. Why is that? The base array is 1,2,3,4,5, and I can’t understand why the range will start by the last elements of the array, then go back to the start… I might be missing something obvious, but if someone can explain why it’s behaving like that, it will be very nice. Thanks
@adicandra9940
@adicandra9940 3 ай бұрын
it's ran in parallel. so whichever finish first would be printed first. It's because of in this video, he use goroutine (the `go func()` in the code is goroutine) to run the loop in parallel instead of doing it sequentially. normal loop (without go func() ) would print 1,2,3,4,5 like normal.
@ky3ow
@ky3ow 3 ай бұрын
When you do go func(){}() you create goroutine, essentially you do multithreading So multiple threads ran some part of loop and it just happened that thread printing 5 finished first
@JeanBagarre
@JeanBagarre 3 ай бұрын
Oh, thanks for educating me!
@wilfredv1930
@wilfredv1930 3 ай бұрын
is better to develop using golang today than 7+ years ago. Yet simple improvement takes too long to be included in a release.
@ITR
@ITR 3 ай бұрын
C# has the same issue
@jongeduard
@jongeduard 3 ай бұрын
Yes this is a famous type of bug! And Go is also not the only language in which they decided to go for breaking a change. In 2012 this EXACT same type of problem was fixed with the foreach loop in C# 5. However, there is likely still an amount of code that now relies on it, and is expected fail after this change, regardless of anyone their opinion on that code. Since this problem is really common in Go, maybe they should consider introducing a keyword instead of breaking things. I am not sure here.
@AnthonyBullard
@AnthonyBullard 3 ай бұрын
You can still get this behavior by lifting the control variable out of the for and then just assigning to it
@jongeduard
@jongeduard 3 ай бұрын
@@AnthonyBullard An old school C style for loop is indeed not magically fixed if you mean that. This is indeed really just about automatic range style loops in languages which hide implementation details, and which I believe is the concern which causes confusion. In a classic loop where you manage your own counters it's more obvious that your variable has loop lifetime and not single iteration lifetime.
@not-lucky2202
@not-lucky2202 3 ай бұрын
favourite emoji 😭😭😭
@Fudmottin
@Fudmottin 3 ай бұрын
I used this emoji recently. 🙀
@cylian8422
@cylian8422 3 ай бұрын
Now we just wait for proper enums in go
@dreamsofcode
@dreamsofcode 3 ай бұрын
I'm looking forward to that day 🙏
@AnthonyBullard
@AnthonyBullard 3 ай бұрын
I’ve learned to just live with it. Go will probably never introduce such a big change and Go 2 is not a “thing”
@vikingthedude
@vikingthedude 3 ай бұрын
JS devs: first time?
@vorant94
@vorant94 3 ай бұрын
Came here just to say this exact thing))
@prashlovessamosa
@prashlovessamosa 3 ай бұрын
😅
@worgenzwithm14z
@worgenzwithm14z 3 ай бұрын
Mutability and imperative loops What could Go wrong
@envadeh
@envadeh 3 ай бұрын
Hello I would like to request a Haskell setup for neovim using nvchad
@anon_y_mousse
@anon_y_mousse 3 ай бұрын
Wait a minute, are you telling me that goroutines are concurrently executed as opposed to being in order for when they're setup? If that's the case, then it's no wonder they decided to not call them coroutines, and would be yet another reason to hate the language.
@Yoolayn
@Yoolayn 3 ай бұрын
🤓🤓🤓🤓
@CoolestPossibleName
@CoolestPossibleName 3 ай бұрын
😶‍🌫️
@iwolfman37
@iwolfman37 3 ай бұрын
why do people always talk about golang problems that i've never even encountered instead of talking about good things you can do in go. if your problem stems from the fact you decided not to have your anonymous function take in a parameter like you would any other normal function, that sounds like a logical mistake on your part, not the language itself
@dreamsofcode
@dreamsofcode 3 ай бұрын
Apologies if this video came off as negative! I actually love Go. The Go devs have acknowledged this behavior causes issues and it can trip up junior developers often. All in all, it's a really good thing that they're fixing it. It shows the language is maturing and evolving as well.
@orbital1337
@orbital1337 3 ай бұрын
Why even have the feature of capturing variables implicitly in the language if you're supposed not to use it? Are you saying the entire feature is a mistake in the language? I think if a feature is there by default, it should be reasonably safe to use not create footguns.
@thirtque
@thirtque 3 ай бұрын
How is this a fix? The issue is the lack of understanding of how memory and loops work.
@syndek64
@syndek64 3 ай бұрын
🫠
@khai96x
@khai96x 3 ай бұрын
JavaScript (and TypeScript) is the devil I know. Go is the devil I don't know. Rust is not a devil. Therefore, when choosing language, I would choose Rust if possible, JavaScript if web, and Go if I hate my coworkers.
@roboticbrain2027
@roboticbrain2027 3 ай бұрын
Even better if go is mandatory for new projects because of company policy.
@AnthonyBullard
@AnthonyBullard 3 ай бұрын
You can learn go in a week, maybe a day. Thanks to the compilation speed, you can iterate in Go sometimes faster than you can in JS/TS(especially with modern tool chains), and avoid most all of the issues present in JS. And get 90% of the difference between JS and Rust in runtime performance. Without fighting the borrow checker. This is from a 25 year JS developer. There are a small number of projects I would absolutely use Rust for if I have a choice, but I’d probably just use Go and lose some conveniences(namely ADTs and explicit mutability) to stay in flow.
@shyampadia
@shyampadia 3 ай бұрын
love your content but sometimes you go too fast through things. need slow down a bit for others
@dreamsofcode
@dreamsofcode 3 ай бұрын
Thank you for the feedback May I ask which part was a little too fast in this video? That way I can improve next time!
@johngrade9817
@johngrade9817 3 ай бұрын
Too fast
@rob-890
@rob-890 3 ай бұрын
Go is pathetic 😂
@FunctionGermany
@FunctionGermany 3 ай бұрын
😔
When RESTful architecture isn't enough...
21:02
Dreams of Code
Рет қаралды 239 М.
Tmux has forever changed the way I write code.
13:30
Dreams of Code
Рет қаралды 890 М.
Osman Kalyoncu Sonu Üzücü Saddest Videos Dream Engine 118 #shorts
00:30
Did you find it?! 🤔✨✍️ #funnyart
00:11
Artistomg
Рет қаралды 116 МЛН
Stupid man 👨😂
00:20
Nadir Show
Рет қаралды 30 МЛН
ХОТЯ БЫ КИНОДА 2 - официальный фильм
1:35:34
ХОТЯ БЫ В КИНО
Рет қаралды 1,9 МЛН
Solving one of PostgreSQL's biggest weaknesses.
17:12
Dreams of Code
Рет қаралды 170 М.
Function Iterators might just change the way we write loops in Go
11:35
Using docker in unusual ways
12:58
Dreams of Code
Рет қаралды 384 М.
This is perhaps the greatest feature of modern programming languages.
5:56
Optimizing Loops In Go | Prime Reacts
10:34
ThePrimeTime
Рет қаралды 80 М.
The standard library now has all you need for advanced routing in Go.
13:52
Всё про конкурентность в Go
23:36
defer panic
Рет қаралды 12 М.
Tonic makes gRPC in Rust stupidly simple
19:08
Dreams of Code
Рет қаралды 39 М.
Теперь это его телефон
0:21
Хорошие Новости
Рет қаралды 1,7 МЛН
Samsung Android Mobile Battrey
0:39
Gaming zone
Рет қаралды 342 М.
Обманет ли МЕНЯ компьютерный мастер?
20:48
Харчевников
Рет қаралды 182 М.
😱НОУТБУК СОСЕДКИ😱
0:30
OMG DEN
Рет қаралды 2 МЛН
Which Phone Unlock Code Will You Choose? 🤔️
0:14
Game9bit
Рет қаралды 11 МЛН