Go uses garbage collection, while Rust uses manual memory management with borrow-checking to ensure safety. Both are just as safe, but garbage collection is slower while Rust's manual memory management requires a lot more effort on the part of the developer. In particular, the performance of garbage collection is less predictable, making Go unsuitable for things like audio processing or video games, where you need to reliably deliver data every few milliseconds to avoid crackling audio or weird glitches. In Rust, you can predict exactly when memory will be freed, and if part of your code must always run in a predictable amount of time, this can be done. Go doesn't give you that guarantee. This isn't very important in traditional client-server apps, CLI tools etc, so go is usually fine for those.
In addition, Go requires a runtime, which is somewhat heavy. This makes it pretty unsuitable for kernels, software that runs on bare metal, microcontrollers etc. Rust doesn't have that problem.
However, Go is usually much faster to write in, as you don't have to worry about managing memory and proving to the borrow checker that you're doing it correctly. The fact that you have Goroutines instead of OS threads also makes it easier to handle lots of concurrent activities, like in a web app that concurrently handles many requests.
With a single execution context this is true. But, whereas you simply can't write data race bugs in Safe Rust† in Go you can write them and they blow up your safety guarantees. If you race something trivial Go promises (unlike C or C++) that this doesn't immediately set fire to the world, the raced trivial object (say, an integer) is ruined and you must not touch it, but if you stay away from that object your program has clearly defined behaviour. Unfortunately non-trivial objects (say, a slice) are immediately Undefined Behaviour when raced.
† This falls out of the mutability rules. A data race is when somebody else modifies something at "the same time" as you're using it, e.g. thread A changes actor to "Steve Buscemi" from "Susan Sarandon" at the same moment thread B is printing the actor out and oops, we write "Susan Sarcemin" or crash or something different happens, who knows. Rust says you can't have multiple aliases and mutability, so this never happens.
To be pedantic, Go strings are immutable and will carry different pointers to different underlying rune slices, so the reader would just display one string or the other, not "Susan Sarcemin".
That said, I'm not sure why developers freak out over races so much. Races that result in simple display of data that's one nanosecond old is typically not a failure condition in most applications. Actual failure conditions from races are usually from read-operate-writes like increments/decrements/etc. And for these, we have tons of solutions, anywhere from atomics to transaction contexts to CRDTs, etc.
> I'm not sure why developers freak out over races so much
In all languages if you cause a data race you lose Sequential Consistency. Java's experience teaches us that even if you go to extraordinary lengths to ensure that everything else is still fine, programmers cannot reason about non-trivial software without Sequential Consistency and so now they can't fix it. In something like C or C++ all races are Undefined Behaviour, all bets are off. In Go as we saw some data races aren't immediately dangerous but even if you're careful you do lose Sequential Consistency and so good luck doing anything when you don't understand what your program means any more.
> And for these, we have tons of solutions
If you put the solution in place and thus prevent a data race, you don't have a data race. Now, when was the last time you wrote a program in which you got everything right first time?
> To be pedantic, Go strings are immutable and will carry different pointers to different underlying rune slices,
I wasn't thinking specifically about Go for this example, but alas Go's strings aren't trivial objects, and so this data race would be immediate UB in Go, whereupon "Susan Sarcemin" while still very unlikely is not impossible. The text (underlying rune slices? really? I guess that would be on brand but it's a bad idea) isn't getting mutated, but your string typed value is, and that is a non-trivial object.
I don’t know what you mean by manual memory management. Memory management in rust is fully automated. The only manual thing is if you want to annotate lifetimes to ensure memory is available past implicit lifetimes, or if the line time of something can’t be automatically derived. Borrow semantics are not manual memory management. You don’t directly control when memory is freed in rust, but because it’s (often) stack based it’s usually pretty obvious memory is freed when the stack is unwound.
I feel like you’re confusing rust with c/c++ in this discussion.
I don’t find go faster to write in at all. I feel like they’re about the same, but I find go package management to be a mess and prefer cargo. Rust however does require you to be more aware of memory lifetime and ownership, and provides generally better performance in exchange.
Go uses garbage collection, while Rust uses manual memory management with borrow-checking to ensure safety. Both are just as safe, but garbage collection is slower while Rust's manual memory management requires a lot more effort on the part of the developer. In particular, the performance of garbage collection is less predictable, making Go unsuitable for things like audio processing or video games, where you need to reliably deliver data every few milliseconds to avoid crackling audio or weird glitches. In Rust, you can predict exactly when memory will be freed, and if part of your code must always run in a predictable amount of time, this can be done. Go doesn't give you that guarantee. This isn't very important in traditional client-server apps, CLI tools etc, so go is usually fine for those.
In addition, Go requires a runtime, which is somewhat heavy. This makes it pretty unsuitable for kernels, software that runs on bare metal, microcontrollers etc. Rust doesn't have that problem.
However, Go is usually much faster to write in, as you don't have to worry about managing memory and proving to the borrow checker that you're doing it correctly. The fact that you have Goroutines instead of OS threads also makes it easier to handle lots of concurrent activities, like in a web app that concurrently handles many requests.