Any language with non-flat naming structure has to decide how it will
encode those names when interacting with a traditional Unix linker and
its flat name space.
You've surely seen what C++ does. The design there follows directly
from the decision that any C++ name must mangle to a valid C
identifier, primarily because C++ was originally compiled via C.
That's an important property, but insisting on it creates names that
are essentially illegible when encountered in a C program, in the
output of nm, or other contexts.
For Go, the gc compilers encode names into unique identifying strings,
but the encoded strings use punctuation like . and /, so that the
encoded symbol name is the entirely readable "encoding/json.Marshal"
instead of something like "P2E8encodingE4jsonN7Marshal".
Although Go does not compile via C, occasionally C code does need to
refer to Go identifiers. Since we chose not to restrict the mangled Go
names to the space of valid C names, we must add some mechanism to
refer to Go names from C. That mechanism is:
1. In the assemblers and C compilers, which already accepted all
non-ASCII Unicode code points in identifiers, the Unicode characters ·
and / rewrite to ordinary . and / in the object files.
2. A symbol with a leading . has its import path inserted before the .
when being linked: inside encoding/json.a, a reference to ".Marshal"
is equivalent to "encoding/json.Marshal".
Because of 2, we went a long time without needing a special character
for slash. Recently the introduction of race detection has made it
convenient for package runtime to be able to refer to a few symbols in
runtime/race, hence the new slash lookalike.