Go and C are quite wise to use pass by value and opt-in references. It avoids the programmer's burden of immutability by default while making it more obvious* when to be aware of potential mutation. A programming style incorporating local mutation also comes very naturally, which is simple, practical, and remains easy to reason about.
*Go and Rust would be wiser if they retained -> from C so that this obviousness would remain without having to check type declarations which may even have been inferred.
Python gets hit the worst by this combination. On top of having mutable references everywhere, it also doesn't make a big deal about object identity (unlike e.g. Java) and has a lot of declarative constructs. Which means you get surprising stuff like this.
>>> foo = [[]] * 10
>>> foo[0].append("bar")
>>> foo
[['bar'], ['bar'], ['bar'], ['bar'], ['bar'], ['bar'], ['bar'], ['bar'], ['bar'], ['bar']]
>>> def baz(arg = []):
... print(arg)
... arg.append("qux")
...
>>> baz()
[]
>>> baz()
['qux']