Close
Glad You're Ready. Let's Get Started!

Let us know how we can contact you.

Thank you!

We'll respond shortly.

LABS
A Rubyist Learning Go – Types and Interfaces

While looking at the websocket library for Go, I came across the following code:

func EchoServer(ws *websocket.Conn) {
        io.Copy(ws, ws)
}

http.Handle("/echo", websocket.Handler(EchoServer))

I tried to figure out where the Handler function was defined in the websocket package but couldn’t find it anywhere. What I did find was this:

type Handler func(*Conn)

func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
        s := Server{Handler: h, Handshake: checkOrigin}
        s.serveWebSocket(w, req)
}

This code defines a type called websocket.Handler that is a “synonym” for a function that takes a pointer to a websocket.Conn. It also defines a method named ServeHTTP on the Handler type. With this discovery, the mystery of the missing function was solved. Allow me to explain…

Ruby is an object oriented language. You can define classes and subclasses with associated methods.  You use these classes to instantiate objects that have state. In Go, there are no classes. You have the ability to combine structs and type declarations to mimic classes/objects:

type User struct {
        Name string
}

func main() {
        user := User{Name: "Mike"}
        fmt.Printf("User name is %s.n", user.Name)
}

This code defines a user “class” that has a property called Name. You can “instantiate” an “object” from this type using a literal declaration (User{Name: “Mike”}). This will output ‘User name is Mike” to the screen.

Since Go is a statically typed language, unlike the dynamically types Ruby, you can also define interfaces that are checked at compile time. One place Go uses interfaces is in the fmt package in the shape of the Stringer interface.

type User struct {
        Name string
}

func (u User) String() string {
        return u.Name
}

func main() {
        user := User{Name: "Mike"}
        fmt.Printf("User %s", user)
}

The above code defines the same User struct we saw before but now it defines a method on User called String. The simple act of implementing all of the methods for the Stringer interface make the User struct a Stringer. Now when the fmt.Printf function needs to make the User a string, it calls the String method defined by Stringer.

How does this apply to our mystery code? Well, the http.Handle function is used by the code to answer calls to the “/echo” endpoint when an HTTP server is started. This function takes a string for the route endpoint (in this case “/echo”) and something that implements the http.Handler interface with its one method ServeHTTP. Getting closer…

Another thing you can do with Go types is declare a type to act as a synonym for a function with a specific signature, just like in the websockets code:

type StringPrinter func (string)

func main() {
        var x StringPrinter
        x = func(y string) {
                fmt.Printf("This is your string %sn", y)
        }

        x("My string")
}

This program defines a variable of type StringPrinter and then assigns a function that takes a string to that variable. The compiler is fine with this assignment so that must mean that any func(string) is also a StringPrinter. If you try to assign a function that takes an integer to x, the compiler will complain since the function does not take a string.

Can you see the solution? Don’t worry if you didn’t, it took me a while to figure it out as well.

The websocket code defines a type called Handler for any function that takes a websocket.Conn and returns nothing. It then implements the http.Handler interface for the websocket.Handler type so it can be passed to http.Handle. So where does the call websocket.Handler function call come from?

What I thought was a function call to websocket.Handler(EchoServer) is in fact a conversion from a function that takes a websocket.Conn to a websocket.Handler type, complete with the implementation of the http.Handler interface via ServeHTTP.

I am still scratching my head about that one but by playing a little type system magic the authors of the websocket library have created a nice DSL for allowing the user to attach their custom websocket handlers to HTTP endpoints and the only thing they have to worry about is a struct of type websocket.Conn. Pretty cool if you asked me. I am going to keep an eye out for more of these “magic” tricks in the Go libraries…stay tuned.

Comments
  1. “synonym” is probably the wrong word to use.
    The type declaration declares a new type. It’s no a synonym for any other type. Like a subclass isn’t a synonym for it’s parent class.

  2. Mike Gehard says:

    Thanks for the clarification Jesse!

Post a Comment

Your Information (Name required. Email address will not be displayed with comment.)

* Copy This Password *

* Type Or Paste Password Here *