Go language Bible - Chapter 10 packages and tools - 10.1 - 10.7

Posted by ermarkar on Sun, 23 Jan 2022 06:47:22 +0100

Chapter 10 packages and tools

Now any small program may contain 10000 functions, but we can't build them one by one. Most of them come from others. These functions are reused in a way similar to packages and modules

There are more than 100 go language packages. You can use go list std wc -l to view them in the terminal. Open source packages can be found at http://godoc.com Org

go comes with a toolkit with various gadgets to simplify workspace and package management

10.1 package introduction

The package allows us to understand and update the code in a modular way. At the same time, the package can realize the encapsulation feature, so as not to make the code messy, but also facilitate our global distribution. The Go language package compiles quickly because it has three language features: 1 The imported package needs to be explicitly declared at the beginning of the file, so that the compiler does not need to read and analyze the whole source file to judge the dependency of the package. 2 The circular dependency of packages is prohibited. The relationship between packages is a directed acyclic graph. Each package can be compiled independently and may be compiled concurrently. 3 The target file of the compiled package not only records the export information of the package itself, but also records the dependencies of the package

10.2 import path

Packages are usually imported automatically through import. For packages of non-standard libraries, the path address of the Internet should be written, such as the HTML parser maintained by the Go team and a popular MySQL driver maintained by a third party

import (
    "fmt"
    "math/rand"
    "encoding/json"

    "golang.org/x/net/html"

    "github.com/go-sql-driver/mysql"
)

10.3 package declaration

Each Go source file must have a package declaration statement at the beginning, which is the identifier when imported by other packages

Access package members by package name

package main

import (
	"fmt"
	"math/rand"
)

func main() {
	fmt.Println(rand.Int())
}

Usually, the import path of a package is the package name, but they can have the same name, but their paths may be different

There are three exceptions to using the package name as the last paragraph of the import path, 1 The path of the main package itself is irrelevant, 2 Test package, 3 Add version number information after the import path, such as "gopkg.in/yaml.v2"

10.4 import declaration

Import can be used to import one by one, together, or in groups

import (
    "fmt"
    "html/template"
    "os"

    "golang.org/x/net/html"
    "golang.org/x/net/ipv4"
)

If two packages have the same name and need to be renamed during import, this naming will only work in the current file

import (
    "crypto/rand"
    mrand "math/rand" // alternative name mrand avoids conflict
)

Renaming has three advantages: one is to divide the time zone into duplicate name packages; the other is to simplify the complex package names and shorten the complex package names; the third is to avoid confusion with the duplicate name variables in the package

The circular import compiler will prompt an error

10.5 anonymous import of packages

If we import a package but do not use it, it will lead to compilation errors. However, sometimes we want to take advantage of the side effects of importing a package: it calculates the package level initialization expression and executes the init initialization function of the imported package. We need to suppress the unused import error. We can use it_ To rename the imported package_ Is a blank identifier and cannot be accessed

import _ "image/png"

This is called anonymous import of packages. It is usually used to implement a compile time mechanism, and then selectively import additional packages at the main program entry. Let's take a look at its features and how it works

package main

import (
	"fmt"
	"image"
	"image/jpeg"
	"io"
	"os"
)

func main() {
	if err := toJPEG(os.Stdin, os.Stdout); err != nil {
		fmt.Fprintf(os.Stderr, "jpeg:%v\n", err)
		os.Exit(1)
	}
}

func toJPEG(in io.Reader, out io.Reader) error {
	img, kind, err := image.Decode(in)
	if err != {
		return err
	}
	fmt.Fprintln(os.Stderr,"Input format =",kind)
	return jpeg.Encode(out,img,&jpeg.Options{Quality: 95})
}

If we give it an appropriate input, it can be successfully converted to output

$ go build gopl.io/ch3/mandelbrot
$ go build gopl.io/ch10/jpeg
$ ./mandelbrot | ./jpeg >mandelbrot.jpg
Input format = png

If there is no anonymous import, it can compile, but it will not output correctly

$ go build gopl.io/ch10/jpeg
$ ./mandelbrot | ./jpeg >mandelbrot.jpg
jpeg: image: unknown format

Here is how the code works

package png // image/png

func Decode(r io.Reader) (image.Image, error)
func DecodeConfig(r io.Reader) (image.Config, error)

func init() {
    const pngHeader = "\x89PNG\r\n\x1a\n"
    image.RegisterFormat("png", pngHeader, Decode, DecodeConfig)
}

The final effect is that the main program only needs to anonymously import a specific image driver package to use image Decode decodes the image in the corresponding format

The database package database/sql also adopts a similar technology, so that users can choose to import database drivers according to their needs, such as:

import (
    "database/sql"
    _ "github.com/lib/pq"              // enable support for Postgres
    _ "github.com/go-sql-driver/mysql" // enable support for MySQL
)

db, err = sql.Open("postgres", dbname) // OK
db, err = sql.Open("mysql", dbname)    // OK
db, err = sql.Open("sqlite3", dbname)  // returns error: unknown driver "sqlite3"

10.6 package and naming

When creating a package, it is generally named with a short name; Try not to use the name of local variable; It is generally singular, but it can also be plural with other considerations

When designing package names, you need to consider how package names and members work together, as shown in the following example:

bytes.Equal flag.Int hettp.Get json.Marshal

Let's look at the naming pattern of strings. Strings provides various operations on strings

package strings

func Index(needle, haystack string) int

type Replacer struct{ /* ... */ }
func NewReplacer(oldnew ...string) *Replacer

type Reader struct{ /* ... */ }
func NewReader(s string) *Reader

strings. Index \ strings. Replacement and so on are all operations

Other packages may only describe a single data type, such as html/template and math/rand. They only expose a main data structure and its related methods, and a function named New is used to create an instance

package rand // "math/rand"

type Rand struct{ /* ... */ }
func New(source Source) *Rand

It may also lead to repetition of some names, such as template Template or Rand Rand, which is one of the reasons why these kinds of package names are often very short

At the other extreme, there are many names and few types of data types like the net/http package, because they have to perform a complex task. Although there are nearly 20 types and more functions, the names of the most important members in the package are simple and clear: Get, Post, Handle, Error, Client and Server

Topics: Go Back-end