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.