go language functions, methods and interfaces

There are two kinds of functions in go language, named function and anonymous function: named function generally corresponds to package level function, which is a special case of anonymous function. An anonymous function becomes a closure function when it references a variable that acts externally in. Method is a special function bound to a specific type. Methods in go language depend on type and must be statically bound at compile time. The interface defines the collection of methods, which depend on the interface object at runtime, so the methods corresponding to the interface are bound dynamically at runtime. Go language implements duck object-oriented model through implicit interface mechanism.

1. Function:

Function definition: the function consists of function name, parameter and return value.

//named function
func Add(a, b int) int {
    return a + b
}

//Anonymous function
var Add = func(a, b int) int {
    return a+b
}

A function in Go language can have multiple parameters and return values. Both parameters and return values exchange data with callees in the way of value passing. In addition, the function also supports variable number of parameters. The variable number of parameters must be the last parameter. The variable number of parameters is actually a slice type parameter.

//Multiple parameters, multiple return values
func Swap(a, b int) (int, int) {
    return b, a
}

//Variable number of parameters
//more corresponds to [] int slice type
func Sum(a int, more ...int) int {
    for _, v := range more {
        a += v
    }
    return a
}

When the variable parameter is an empty interface type, whether the caller unpacks the variable parameter will result in different results:

func main() {
   var a = []interface{}{123, "abc"}
   Print(a...)    //123 abc
   Print(a)       //[123 abc]
}

func Print(a ...interface{}) {
   fmt.Println(a...)
}

The first Print call passes in a... Which is equivalent to a direct call to Print(123, "abc"). The second Print call passes in an unpacked a, which is equivalent to calling Print([]interface{}{123, "abc"}) directly.

Not only can the parameters of a function have a name, but also the return value of a function.

func Find(m map[int]int, key int) (value int, ok bool) {
   value, ok = m[key]
   return 
}

If the return value is named, you can modify the return value by name or by defer statement after the return statement:

func Inc (v int) int {
    defer func () { v++ } ()
    return v
}

The defer statement delays the execution of an anonymous function, because the anonymous function captures the local variable v of the external function, which we generally call "closure". Closures access external variables not by passing values, but by reference.

This drinking behavior of closures can lead to some implicit problems.

func main() {
   for i:=0;i<3;i++{
      defer func() {println(i)}()
   }
}
//Output
//3
//3
//3

Because it's a closure, in the for iteration statement, each defer function references the same i iteration variable. After the completion of recycling, the value of this variable is 3, so the final output is 3.

The idea of the fix is to generate unique variables for the closure function of each defer statement in each iteration.

func main() {
   for i:=0;i<3;i++{
      i := i
      defer func() { println(i) } ()
   }
}

//perhaps

func main() {
   for i:=0;i<3;i++{
      //Pass in i through function
      //The defer statement immediately evaluates the call parameter
      defer func(i int) { println(i) } (i)
   }
}

This is an example only, it is not recommended to use defer in the for loop


2. interface

The interface type of Go is an abstraction and generalization of other types of behaviors, because the interface type will not be bound with specific implementation details. Through this abstraction, we can make objects more flexible and adaptable. The interface type is delay binding, which can realize the multi-state function similar to virtual function.

In Go language, the underlying type (non interface type) does not support implicit conversion. We cannot assign a value of int type directly to a variable of int64 type. But the Go language is very flexible for the conversion of interface types. The transformations between objects and interfaces, interfaces, and interfaces can all be implicit.

var (
    a io.ReadCloser = (*os.File)(f)  //Implicit conversion, * os.File meets io.ReadCloser interface
    b io.Reader = a                  //Implicit conversion, io.ReaderCloser meets io.Reader interface
    c io.Closer = a                  //Implicit conversion, io.ReaderCloser meets io.Closer interface
    d io.Reader = c.(io.Reader)      //Explicit conversion, IO. Closer does not meet io.Reader interface
)

We can implement pure virtual integration by embedding anonymous interface or anonymous pointer object. What we inherit is only the specification specified by the interface, and the real implementation is injected when running. For example, you can implement a gRPC plug-in:

type grpcPlugin struct {
    *generator.Generator
}

func (p *grpcPlugin) Name() string { return "grpc" }

func (p *grpcPlugin) Init(g *generator.Generator) {
    p.Generator = g
}

func (p *grpcPlugin) GenerateImports(file *generator.FileDescription) {
    if len(file.Service) == 0 {
        return
    }
    p.P('import "google.golang.org/grpc"')
}

The constructed grpcPlugin type object must satisfy the generate.Plugin interface:

type Plugin interface {
    //Name identifies the plugin.
    Name() string
    //Init is called once after data structures are built but before
    //code generation begins
    Init(g *Generator)
    //Generate produce the code generated by the plugin for this file,
    //except for the imports, by calling the generator's methods
    //P, In ,and Out.
    Generate(file *FileDescriptor)
    //GenerateImports produces the import declarations for this file.
    //It is called after Generate.
    GenerateImports(file *FileDescripor)
}

The p.P(...) function used in the GenerateImports() method of grpcPlugin type corresponding to the generate.Plugin interface is implemented through the generator.Generator object injected by the Init() function.

Tags: Go Google

Posted on Sat, 14 Mar 2020 06:37:26 -0400 by Michan