The case of typed variables

Go has short assignments and the compiler can infer its type.

import "fmt"

func AddOne(a int) int {
    return a + 1
}

func main() {
    x := 1
    fmt.Printf("%v", AddOne(x)) // 2
}

One could have also written

func main() {
    var x int = 1
    fmt.Printf("%v", AddOne(x)) // 2
}

or even

func main() {
    var x = 1
    fmt.Printf("%v", AddOne(x)) // 2
}

So which should we prefer?

In normal cases, x := 1 is preferred as it is concise and clear. However, there is a case for using the latter.

Consider the following in the gRPC library google.golang.org\grpc\server.go

// RegisterService register a service and its implementation to the gRPC
// server. Called from the IDL generated code. This must be called before
// invoking Serve.
func (s *Server) RegisterService(sd *ServiceDesc, ss interface{}) {
    ht := reflect.TypeOf(sd.HandlerType).Elem()
    st := reflect.TypeOf(ss)
    if !st.Implements(ht) {
        grpclog.Fatalf("grpc: Server.RegisterService found the handler of 
        type %v that does not satisfy %v", st, ht)
    }
    s.register(sd, ss)
}

The code registers an implementation of the ServiceDesc, ss to the server. The caveat here is ss needs to implement the interface defined in ServiceDesc.

Suppose you want to call this method directly, given that you have an implementation of the interface sd.HandlerType. The first way would be to do something as follows

type ContentService interface {
    Put (int)
    Get (int) int
}

// Assume code for myService implementing ContentService here

func main() {
    sd := &ServiceDesc{
        HandlerType: ContentService    
    } 
    impl := &myService{} // impl
    srv := &Server{}
    srv.RegisterService(sd, impl)
}

This code will compile and will run properly and all is good.

However, suppose the programmer made a mistake and implemented one of the methods wrongly

func main() {
    sd := &ServiceDesc{
        HandlerType: ContentService    
    } 
    impl := &myUnfinishedService{} // impl
    srv := &Server{}
    srv.RegisterService(sd, impl)
}

This code will still compile! But we will get a runtime error when we start it because of the incorrect signature.

This problem can be easily solved if we declared impl with a type.

func main() {
    sd := &ServiceDesc{
        HandlerType: ContentService    
    } 
    var impl ContentService = &myService{} // Compiler can now typecheck this!
    srv := &Server{}
    srv.RegisterService(sd, impl)
}

Here, we are giving the compiler enough hints to know that myService has been implemented incorrectly.

Conclusion

Sometimes, we can let the compiler help us detect programming errors by providing hints.