3 ms·
Go has exceptions, though. Unwind stack, calling cleanup functions in frames that need them, until caught higher up the stacks. Yah, that's exceptions.
by knorker 6y ago
Go has exceptions, though.
Unwind stack, calling cleanup functions in frames that need them, until caught higher up the stacks. Yah, that's exceptions.
- MrBuddyCasino 6y agoThis is insincere. Rust can unwind too, but as in Go, this not the usual error handling mechanism.
- knorker 6y agoI haven't learned Rust yet, but no it's not insincere. Go has a textbook definition of exceptions. The mere fact that they are less used doesn't make them "not exceptions". And importantly it doesn't matter that they are less common. Merely having exceptions means that everyone has to write exception-safe code. HTTP handlers silently swallow exceptions, so you can't rely on your program dying if there's a panic. fmt.Printf can panic as far as you know, too. Essentially you need RAII, except that because Go doesn't have RAII every single resource needs: r, err := getResource() if err != nil { … } defer r.Close() But because "Go doesn't have exceptions" people often don't bother with the defer, and then they get bugs. I see it happening frequently. You need to write exception-safe code. But also you're not allowed to use exceptions. So it's the worst of both worlds. I write and review a lot of Go code, and I like the language. But I don't like the dishonesty.
- MrBuddyCasino 6y ago> You need to write exception-safe code. But also you're not allowed to use exceptions. So it's the worst of both worlds. I wasn't aware that this is a real problem in Go land, thanks for the explanation. I wouldn't say the language is "great", was just using it as an example of a modern language where there is a consensus that "they got it right" and not using exceptions, even though error handling is still tedious right now. I think in Rust you have to consciously fuck up the panic handler to cause similar issues, but I'm not sure.
- knorker 6y agoWhat's the equivalent in rust of defer? E.g. this code in Go is buggy: mu.Lock(); fmt.Print(someoneElsesObject);mu.Unlock();return You need to get into the habit of writing: mu.Lock(); defer mu.Unlock(); fmt.Print(someoneElsesObject);return And this gets extra complicated by the fact that in Go defer runs at end of function, not end of scope. This makes every single for-loop that needs to lock anything hard to read and annoying to write. for _, a := range stuff { if err:=func()error { mu.Lock();defer mu.Unlock(); return a.stuff()}(); err != nil {return err}}
- steveklabnik 6y agoYou use RAII, to put it in C++ terms. To use your variable names, someoneElsesObject is inside mu, and is returned by mu.lock. When it goes out of scope, it will unlock. You can also use defer if you want, but I've never seen it in real code, it's more error-prone and not as flexible. You can build it on top of RAII.
- knorker 6y agoI'm not following what you're saying. I'm saying "mu.Unlock()" except when deferred, is essentially always a bug. At the very least it's a bug waiting to happen. You need to prove that everything between Lock and Unlock is exception-safe. And that's rarely possible. As I've said elsewhere you cannot rely on panic triggering program exit, since e.g. HTTP handlers swallow panics. The fact that you seem to be saying you never see an Unlock deferred proves my point that saying "Go doesn't have exceptions" hurts Go programming. And Go code is in fact full of bugs because there's all this exception-unsafe code. C++ is naturally exception safe, because RAII. Where it's not exception safe it's because RAII was not used. Even something as simple as: mu.Lock() stats[metricName]++ mu.Unlock() will throw if there's a path where stats[] map was not inited, and if called in an HTTP handler will leave the lock in place, leading to probably a deadlock of the server. Not great.
- steveklabnik 6y agoThe Rust code would be, roughly (I tried to keep names similar even though Rust would use a slightly different naming scheme) let someoneElsesObject = mu.lock(); println!("{}", someoneElsesObject); return; when someoneElsesObject goes out of scope, the mutex is unlocked. This happens no matter how it goes out of scope. You cannot forget to do it, because the only way to get access to someoneElsesObject in the first place is locking the mutex, because mutexes wrap the data that they are protecting. https://crates.io/crates/defer https://crates.io/crates/defer exists, but it would be weird to try and use it here, because it can't really be combined directly with this. I guess in theory you could put drop(someoneElsesObject) in the defer block, but like... that already happens for free.