4

I am learning about empty interfaces. I find that while there are many explanations—also on Stackoverflow—on meaning of an empty interface and how they work, there's very little information on best-practices on when / why to use them, when to avoid, what the considerations are, and the pros and cons of chosing to use them.

In Go chatrooms I've read some talk about best to avoid using empty interfaces where possible, but without the proper arguments. Others proudly responding they had zero empty interfaces in their code design.

I am most interested in use for libraries and frameworks (intended to be reused / extended by others).

Right now I am reading a framework codebase with related libraries, that is full of emtpy interfaces in many locations. Some of this confuses me, and I wonder if all usages are warranted. Like the framework offers 'user management' and things like AppConfiguration and UserPreferences are empty interfaces. This while further on in the code (near the db layer) the user's email is technically treated as a user preference. Wouldn't it be better to be more specific in the interface definition?

1

2 Answers 2

5

In Go chatrooms I've read some talk about best to avoid using empty interfaces where possible, but without the proper arguments. Others proudly responding they had zero empty interfaces in their code design.

The biggest problem is that you lose all typing; for example let's say I have a function that I want to operate on various types of numbers, so I write:

func AddOne(n interface{}) int64 {
    switch nn := n.(type) {
        case int:
            return nn + 1
        case int8:
            return nn + 1
        // ... etc...
    }
}

But what if I pass 0.42 (float64) to this function, or the string "asd"? If the function would accept an int64 (func AddOne(n int64) int64) then we would get a warning about this at compile time, but with interface{} you won't get anything since everything is valid.

The best you can do is handle this at run-time and either panic() or return an error; which is obviously a lot less clear than a compile-time error.

This is by far the biggest downside.


I'd have to see the details of those AppConfiguration and UserPreferences functions in particular, but there are some good reasons for using empty interfaces; for example when you want to accept some custom struct and then use reflection to set values on the struct based on some external data. This is essentially what packages such as encoding/json do, but it's also commonly used for parsing some config files, and so forth.

It sounds like AppConfiguration and UserPreferences may fit this, because the library/framework doesn't know what settings you need for your app's configuration, or what user preferences there are. But like I said, it's hard to be sure without details.

Another use case is when you really want to accept a wide variety of types; passing parameters to SQL queries is a common example, or fmt.Printf().


In general, avoiding interface{} is probably best, but if you find yourself bending over backwards for doing so then it's probably best to just use interface{} and live with the lack of typing.

Sign up to request clarification or add additional context in comments.

5 Comments

I think the questions was about named empty interfaces like type UserPreferences interface{}.
Thanks @martin-tournoij! With libs + frameworks I was simply referring to code intended to be reused and extended by others. There are some named empty interfaces, and also things like func (a *App) DefaultUserPreferences() interface{} which returns nil. The user preference however should include an email, which is required in the db.
One of the advantages of named/typed empty interface is that you can attach methods to them @ArnoldSchrijver; in general, it takes some effort to write some generic user auth library without interface{} (although it's not impossible either, as I've done so). But like I said, it's rather hard to say for sure; there may be features or considerations that make it hard to use interfaces.
@MartinTournoij as far as I know we can't "attach" a method to a named interface, only to a "struct type". Please correct me if I wrong?
You can attach methods to any type @AlexanderTrakhimenok (type x string, I've done this many times), but actually I'm not sure about interface types now. Looks like you can't: invalid receiver type named (named is an interface type) So I guess I was wrong about that 😅
2

Empty named interface does not make sense in Go because unlike other languages like C# for example any type (class in C#) can be cast to a specific interface if it matches interface signature. So in Go "classes" (struct types) does not need to be inherited from an interfaces (e.g. to declare interface at time of type definition).

So the answer to your question:

Best practice for empty interfaces in Go is not to defined named empty interfaces in Go.

Update: Thanks to the comments below I've changed my mind and I agree there is can be limited use cases for usage of empty named interfaces for the sake of documentation and quicker navigation in IDEs.

7 Comments

"Empty named interface does not make sense in Go" -- except when it does make sense.
Also, there are no classes in Go, and no inheritence. It's not clear if you're trying to use these terms in the context of C# or Go.
For the same reason you might use a named type of any other base type: Documentation.
If your litmus test is the stdlib, here is a famous example. And there are many others. i.e. 1, 2, ...
Thanks for the links, I really appreciate it. Though links 1 & 2 lead to test code that obviously OK to have empty named interface if you want to test behavior depending on it. Regards "famous example" it's a good & valid one for the sake of argument and after studying code I can agree it may make sense to use it for the sake of documentation. Also I can think it can be useful for IDE in some use cases. Again thanks for the education.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.