We use glibc by default, so while Rust statically links all Rust code by default, that's still dynamically linked. You can use MUSL to remove that, where appropriate, but it's not the deafult.
To add an additional clarification here for others (I know Steve knows this :P), by default, at least on Linux, whether Go produces a dynamic executable or not depends on which things you import. For example, a simple hello world program will produce a static executable:
$ cat main.go
package main
import "fmt"
func main() {
fmt.Println("Hello, world!")
}
$ go build
$ ldd scratch
not a dynamic executable
But anything using the network, like a simple HTTP server, will cause the Go tool to build a dynamic executable:
Now of course, you can set flags to force Go to always produce a static executable, but the difference here isn't too much different with Rust. With Rust, you do need to install MUSL, add the target and then rebuild with an extra flag, but it's all very simple.
(To be clear, I am not criticizing Go here! There are good reasons why they link to libc by default when network stuff comes into the picture.)
The problem is that you have to think about it at all. If you compile on a machine with a new libc, and then try to run it on a machine with an old libc, it won't work. So you end up doing what most people do, even outside of Rust, which is take the oldest CentOS box you can stand and do builds on that.
The Autopackage project had a solution for this problem years ago called apbuild. It would search for the versions of glibc symbols and use the oldest possible ones, which resulted in portable binaries. With some quick googling I found the code here: https://github.com/DeaDBeeF-Player/deadbeef-plugin-builder/t... Probably doesn't work anymore though :/
It's a bit sad that this is still unsolved, probably due to the GNU people's hatred for closed-source distribution.