SoFunction
Updated on 2025-03-05

How to use interface function in Go

In the net/http package, there is an implementation of an interface function:

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

Why can't we directly get the view function HandlerFunc based on the route and then execute with brackets in a multiplexer?

On the contrary, we need to implement the Handler interface in one go, and then wrap the function and call HandlerFunc(f).ServeHTTP(w,r) ​​.

value

It can not only use ordinary function types (need to type conversion) as parameters, but also use structures as parameters, which are more flexible and readable. This is the value of interface functions.

Example 1 (net/http)

You can map request paths and processing functions. The definition of Handle is as follows:

func Handle(pattern string, handler Handler)

The second parameter is the interface type Handler.

func home(w , r *) {
    ()
    _, _ = ([]byte("hello, index page"))
}

func main() {
    ("/home", (home))
    // (home)->HandlerFunc->The default multiplexer will call its ServeHTTP() method    _ = ("localhost:8000", nil)
}

Another function, HandleFunc is defined as follows:

func HandleFunc(pattern string, handler func(ResponseWriter, *Request))

The second parameter is a normal function type.

func main() {
    ("/home", home)
    _ = ("localhost:8000", nil)
}

The two writing methods are completely equivalent. HandleFunc converts the second writing method into the first writing method.

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    if handler == nil {
        panic("http: nil handler")
    }
    (pattern, HandlerFunc(handler))
}

The second parameter is also the interface type Handler, which uses the built-in route of the standard library net/http, so the value passed in is nil.

If the structure passed in this place that implements the Handler interface, all HTTP requests can be fully hosted, and you can customize how to route, how to handle, and what functions to add before and after the request. Gradually, it became a feature-rich web framework.

Example 2 (tutu)

// A Getter loads data for a key.
type Getter interface {
    Get(key string) ([]byte, error)
}

// A GetterFunc implements Getter with a function.
type GetterFunc func(key string) ([]byte, error)

// Get implements Getter interface function
func (f GetterFunc) Get(key string) ([]byte, error) {
    return f(key)
}

Suppose there is a method:

func GetData(getter Getter, key string) []byte {
    buf, err := (key)
    if err == nil {
        return buf
    }
    return nil
}

How to pass parameters to this method?

Method 1: Functions of type GetterFunc as parameters (anonymous functions)

GetData(GetterFunc(func(key string) ([]byte, error) {
    return []byte(key), nil
}), "hello")

Method 2: Normal functions

func test(key string) ([]byte, error) {
    return []byte(key), nil
}

func main() {
    GetData(GetterFunc(test), "hello")
}

The test cast type is GetterFunc, which implements the interface Getter, which is a legal parameter. This method is suitable for scenarios with relatively simple logic.

Method 3: Implement the structure of the Getter interface as a parameter

type DB struct{ url string}

func (db *DB) Query(sql string, args ...string) string {
    // ...
    return "hello"
}

func (db *DB) Get(key string) ([]byte, error) {
    // ...
    v := ("SELECT NAME FROM TABLE WHEN NAME= ?", key)
    return []byte(v), nil
}

func main() {
    GetData(new(DB), "hello")
}

DB implements interface Getter, which is also a legal parameter. This method is suitable for scenarios with relatively complex logic. If a lot of information is required for the operation of the database, the address, the user name, the password, and many intermediate states need to be maintained, such as timeout, reconnection, locking, etc. In this case, it is more suitable to encapsulate as a structure as a parameter.

In this way, ordinary function types (need to be converted) can be used as parameters, and structures can be used as parameters. It is more flexible and readable. This is the value of interface functions.

Summarize

The above is personal experience. I hope you can give you a reference and I hope you can support me more.