Dot imports—FTW or WTF?
This week, I came across two articles that praised dot imports as a great way of writing more clear code because, you know, dot imports eliminate this annoying package name prefix.
Folks, please. Don't. Use. Dot. Imports.
For newcomers, a dot import is an import statement where the package alias name is just a dot, like so:
import . "encoding/json"
import . "encoding/xml"
// Pop quiz: Where is Unmarshal defined?
func main() {
res := ...
err := Unmarshal([]byte{""}, &res)
...
}
All exported identifiers from that package can now be accessed without the package prefix, such as, Println()
instead of fmt.Println()
.
This looks like a smart way of making code clearer, but in general, dot imports do more harm than good.
- Dot-imported functions and types appear as if they were defined somewhere in the current package. Good luck finding that non-existent place! A package prefix makes it clear where an identifier comes from. In other words, package prefixes create self-documenting code.
- Dot imports pollute the current package's namespace. Every function or type you dot-import removes the option to write a local function or type of the same name (for example, as a wrapper for the imported funtion).
- Conflicts are waiting to happen. If you dot-import two packages, and both contain an identically named identifier, your code does not compile.
There may be some corner cases that justify dot imports, such as experimentation, or if everyone in a small team knows that a particular package is made specifically for using dot-imported identifiers as a sort of “domain language”. But if more people work on the code, clear is always better than clever.
Saving a few keystrokes is not worth it.