5

Here are golang code, the func newXXX return an interface, but why it does not return a struct

type _ABitOfEverythingServer struct {
    v map[string]*examples.ABitOfEverything
    m sync.Mutex
}

type ABitOfEverythingServer interface {
    examples.ABitOfEverythingServiceServer  // interface
    examples.StreamServiceServer            // interface
}

func newABitOfEverythingServer() ABitOfEverythingServer { 
//<-why not return _ABitOfEverythingServer, is it a good way?
    return &_ABitOfEverythingServer{
        v: make(map[string]*examples.ABitOfEverything),
    }
}
2
  • 3
    Questions about "Why?" on artificial code snippets don't have an answer. Maybe there is a technical reason or it is for educational purpose or to demonstrate something. Or it is a question of taste. Commented Mar 28, 2018 at 3:46
  • A stab in the dark, but because ABitOfEverythingServer is an interface, and more flexible if you want to swap it out, easy to "mock" or "stub" in tests. I suggest reading about interfaces. Commented Mar 28, 2018 at 13:21

3 Answers 3

10

I do not know if there was any particular reason for returning interface in the above snippet.

But generally, returning structs is the recommended way. Remember Accept interfaces, return structs anyone?

Returning interfaces does not simplify mocking in anyway since client can define interface when mocking is required (which is the most beautiful thing about golang interfaces).

func newABitOfEverythingServer() *_ABitOfEverythingServer { // <- returning struct ptr
    return &_ABitOfEverythingServer{
        v: make(map[string]*examples.ABitOfEverything),
    }
}

For the above refactored version (which returns structs), client can simply define an interface describing what it needs and mock only that:

type onlyStreamPart interface {
     examples.StreamServiceServer
}

// streamer does not care if the streamServer also
// implements examples.ABitOfEverythingServiceServer
// or not. (Think interface segregation from SOLID)
func streamer(stremServer onlyStreamPart) {
}

// which can be called easily as:
streamer(newABitOfEverythingServer())

This simplifies mocking a lot while testing streamer since the mock implementation does not have to implement examples.ABitOfEverythingServiceServer interface.

This is a commonly misunderstood by developers from Java, C# etc. backgrounds (where type systems are name based, not structural as in Go). Since in those languages client cannot modify the interface it is accepting because that would require adding implements newInterfaceDefinedByClient clause to all the classes that need to be passed to the client.

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

1 Comment

"Returning interfaces does not simplify mocking in anyway since client can define interface when mocking is required (which is the most beautiful thing about golang interfaces)." Yes and no. If you have a monolith backend project and need to re-use something, then it makes more sense to move this into internal and also provide the necessary interface + mocks. If you don't do it, you end up repeating yourself by copy pasting the same interface all over your project. And once you change the API of your implementation, then you have to adapt all interfaces and re-create mocks.
1

First, you need to learn the basics of Go. Take A Tour of Go.

Simplifying a complicated example,

package main

import "fmt"

type S struct{ F int }

type I interface{}

func newI() I {
    return &S{F: 42}
}

func main() {
    i := newI()
    s := i.(*S)
    f := s.F
    fmt.Println(f)
}

Playground: https://play.golang.org/p/tHbTZHEQZ-L

Output:

42

The newI function returns a value of interface type I, which contains a value of concrete type *S.

1 Comment

This answer is condescending.
0

If you notice, the struct contains a mutex. A mutex must not be copied by value. The author wants to abstract away the implementation details so that the user can use this like any other data types. Hence, the constructor returns a pointer to the struct, and hide it behind an exported interface. Now the user can use the data type without worrying whether it contains a mutex.

Comments

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.