- Both Go and Rust use bounds checking on arrays/slices (which means out-of-bounds accesses are not allowed).
- Go is garbage collected, so memory leaks cannot occur.
- Rust encourages an ownership/lifetime model, so memory leaks cannot occur.
- Go can still have data races between multiple concurrent threads. To catch them, you must use the race detector _at run time_. Note however that Go encourages communicating information over channels, and if idiomatic code is being written the chance of data races is next to none.
- Rust cannot have data races in most cases, as it's ownership/lifetime model allows for catching them _at compile-time_.
- Both of these assumptions are based on no external C or unsafe code being in the picture.
TL;DR: Very similar.
EDIT: I used the wrong terminology -- sorry. Instead of _memory leak_ I should have said _dangling pointers_ (memory leaks can occur in _any_ language, Go/Rust/Java/JS/etc).
Well it depends on how you define memory leaks. Memory leaks can of course occur logically. Like say you create objects in a cache and never remove them. It is still a memory leak.
But at the level that you define the memory leak, it seems Rust can be just as good as Go because memory ownership is tracked by the compiler. One can say it is even better because of compile time checks and no need to have a GC @ runtime present.
However, in practice most values can be neither Rc nor Gc but single-owner, like unique_ptr in C++, which is easier to reason about than either.
So far so good. But You forget to allocate smaller array (and copy members) when you consume too much memory. So 1000000* push(something) followed by 1000000*pop() keeps plenty allocated memory not in usage.
(It's not as brutal as malloc() and forget the result but still pretty awfull situation in a large project.)
To catch _some of_ them :) No guarantee that they will all be caught.
> The cost of race detection varies by program, but for a
> typical program, memory usage may increase by 5-10x and
> execution time by 2-20x.Rust has one killer feature that Go does not have: the typesystem has semantics for memory sharing. You can declare how a pointer can be used: is it accessed concurrently, is it local to me, &c. This helps the typesystem detect race conditions where two threads access a block of memory in an unsafe way.
Go does not have this. In Go, you "share memory by communicating", instead of "communicating by sharing memory." But if you do want to share memory, you are back to using locks and your own conventions and mechanics to prevent concurrent access. The language doesn't help you.
So it's a different programming paradigm. And if you use Go like you would use Rust (memory sharing), then Go is less safe.
(...right?)
EDIT: This sounds one-sided, but I'm only talking about sharing memory, here. If you don't do that, and stick to communicating, then Go can blow you away. I don't know how Rust deals with coroutines, but Go has super light threads (goroutines) that make concurrent behavior pure joy. All your threads can so easily talk to eachother, and wait for eachother, and it's all so light and native and good!
E.g. you have a handler for a POST, but you only want to close the connection when another thread has finished doing something. And you don't want to tie up an entire OS thread, because it could take a while and that's just too expensive. In go: super easy, just do a blocking channel read. In another language? Wow, where would I start...
(How does rust do that?)
Sometimes, yes, using a mutex makes more sense -- and that does introduce the possibility of a data race.
If you follow effective Go code -- I imagine the code is on average about just as safe as Rust code is. It is partially comparable to using `unsafe` blocks in Rust -- don't do it, unless you know what you're doing. Stick with channels.
That said, channels in Rust are also nice to use, and you shouldn't not choose channels. Rust just enables other use-cases too.
Don't take this the wrong way, I'm not complaining about Go. Just know that sometimes, there are real reasons to share memory. And when you do: Rust has got your back, but in Go you're on your own.
Rust transfers ownership when you send a single-owner reference over a channel. The sender can no longer access the referenced object; the compiler prevents that. That's the safe way to do it. This is a significant advance in multi-thread programming. The rules that make this work are strikingly simple. Somebody should have thought of this 20 or 30 years ago, but as far as I know, nobody did. Rust pushes the single-owner model hard, and it seems to work. The C++ crowd beat their head against the wall on this for decades, through three generations of auto_ptr, then unique_ptr. Really getting it right requires something like the Rust borrow checker, which C++ does not have.
Go does not do anything to prevent sharing data between threads. It's very easy in Go to unwittingly create shared data, because slices are references. After you've sent a reference, the sender still has access to the shared data, as does the receiver, so there's a potential race condition. Go does nothing to prevent this. There's a lot of dancing around this issue in "(In)effective Go", and long discussions of "is this a race condition" in Go forums. Go is a useful language, but it's in spite of Go's concurrency system, not because of it.
The same claim can be made for C++ (with uniqe_ptr<T>), but we know how that faired. It is telling enough that there is a race detector tool for Go.
std::unique_ptr<int> p(new int(1));
std::unique_ptr<int> q = p;
Should fail.You can strong arm it into break but you can also reuse a pointer locally in Go after it was sent over a channel.
#include <iostream>
auto f() {
int x = 1;
return [&] () { return x; };
}
int main(int, char **) {
std::cout << f()() << std::endl;
}
Of course "using <x> correctly" prevents bugs as long as - as C++ does - you define "correctly" as generously as possible. I agree Go isn't totally memory safe, but the level of memory safety is insanely less than in C++.If you want something particularly involving unique_ptr, consider that best practices are to give non-owning sharing as raw pointers: you could say they're safe as long as you use them correctly but again that misses the point. I can also give an example (stolen from a talk) directly involving shared_ptr even when used with traditional "best practices" although I'm sure the best practices will update themselves to include this.
In some sense, Rust has been described as merely formalizing and automatically enforcing the best practices that people were already using in C++; it's the formality and automaticity that turns it from a dangerous language to a safe one.
My first thought would be to attach the connection object to the data the other thread is working on. When it is done it processing your message it drops the whole thing, closing the connection. If you want to hold it open for multiple workers you could make it an Arc<Connection> so it doesn't close until they all drop it.
(Disclaimer: I'm a Rust developer.)
Go doesn't enforce this on a compiler level -- but instead it encourages the use of sharing by communicating (over channels, where data races cannot occur), instead of sharing memory e.g. via a mutex (where you can run into a data race).
- easily callable from other languages because of the C ABI
- runtime free and not Posix dependent
- portable across CPU architectures.
Rust will provide you memory safety where other languages can't even run.
There's many things I don't like about rust but the above list make it very hard to beat.
Rust on the other hand is a completely different animal to anything I've used before. It has one of the most annoying compilers but that is for a reason, if your code compiles then it's memory safe (with a few exceptions, like 'unsafe' block)
I don't see how the fact that most languages can't be dropped into C programs makes the fact that Rust can not interesting. It seems to me to imply the opposite.