© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
B. Feigenbaum, Ph.D.Go for Java Programmershttps://doi.org/10.1007/978-1-4842-7199-5_17

17. Go Runtime

Barry Feigenbaum, Ph.D.1  
(1)
Austin, TX, USA
 

This chapter surveys several Go packages related to the Go runtime.

Errors Package

The errors package provides functions to help create and select errors. Many community extensions, some drop-in replacements, exist.

There is a built-in error type. All errors implement this predefined interface:
type error interface {
      Error() string
}
This means any type that conforms to this interface (i.e., has the Error method) can be used as an error. This means many custom errors can be created. Many Go packages provide custom errors, often including supplemental information about the failure. For example, consider the os.PathError type :
type PathError struct {
      Op string    // failed operation
      Path string  // failed on path
      Err error    // root cause, if any
}
All custom errors need to implement the error interface, such as this possibility for the preceding error:
func (pe *PathError) Error() string {
      if pe.Err == nil {
            return fmt.Sprintf("PathError %s:%s", pe.Op, pe.Path)
      }
      return fmt.Sprintf("PathError %s:%s:%v", pe.Op, pe.Path, pe.Err.Error())
}
Custom error types can be any base type, not just structs, such as strings. For example:
type MyError string
func (me MyError) Error() string {
      return string(me)
}
The errors package has these functions:
  • func As(err error, target interface{}) bool – Convert to the target type if of target type; a target is a pointer to a location to receive the cast error.

  • func Is(err, target error) bool – Test if err of target type.

  • func New(text string) error – Makes an error.

  • func Unwrap(err error) error – Get a wrapped error cause if available; err must have an Unwrap() method.

Often, errors are declared as top-level values. This lets them be tested for equality or used with the preceding functions against returned errors. For example:
var (
      ErrSystem = errors.New("System Error")
      ErrIO = errors.New("I/O Error")
      ErrOther = errors.New("Other Error")
     :
)

Flag Package

The flag package implements a simple but standardized means to parse command lines. Often, parameters are binary switches (aka flags and thus the package name). Many community extensions exist.

The flag package works mostly as a global library as it assumes there is a single command line to parse. Various possible command-line values are defined, then the command line is parsed to look for these values, and any found are set. Also provided is a help feature to describe the arguments. There are several options in the way values are defined. In general, don’t mix the Xxx and XxxVar (where Xxx is a type name) styles; use them consistently.

Some example flag definitions:
var iflag int
var fflag float64
var sflag string
func init() {
      flag.IntVar(&iflag, "IntFlag", 1, "IntFlag sets ...")
      flag.Float64Var(&fflag, "FloatFlag", 1.0, "FloatFlag sets ...")
      flag.StringVar(&sflag, "StringFlag", "", "StringFlag sets ...")
}

Note the flags are defined in an init function and thus are defined before the Parse function is called, typically in a main function.

Flags look like one of these options on a command line:
  • -flag

  • -flag=x

  • -flag x - flag must not be of Boolean type

The flag package provides this key variable:

var CommandLine – Default access to os.Args

The flag package provides these types:
  • ErrorHandling – Enums to control how errors are handled

  • Flag – The state of a flag, including the current value

  • FlagSet – A collection of flags, generally one per possible flags

The flag package provides these functions. They access a global flag set:
  • func Arg(i int) string – Returns the ith non-flag argument

  • func Args() []string – Returns all non-flag arguments

  • func Bool(name string, value bool, usage string) *bool – Make a Boolean flag

  • func BoolVar(p *bool, name string, value bool, usage string) – Wrap p as a flag

  • func Duration(name string, value time.Duration, usage string) *time.Duration – Make a duration flag

  • func DurationVar(p *time.Duration, name string, value time.Duration, usage string) – Wrap p as a flag

  • func Float64(name string, value float64, usage string) *float64 – Make a float flag

  • func Float64Var(p *float64, name string, value float64, usage string) – Wrap p as a flag

  • func Int(name string, value int, usage string) *int – Make an int flag

  • func Int64(name string, value int64, usage string) *int64 – Make an int64 flag

  • func Int64Var(p *int64, name string, value int64, usage string) – Wrap p as a flag

  • func IntVar(p *int, name string, value int, usage string) – Wrap p as a flag

  • func NArg() int – Number of non-flag arguments

  • func NFlag() int – Number of flag arguments

  • func Parse() – Parse the command line and set args and flags after all flags are defined

  • func Parsed() bool – Test if parsed

  • func PrintDefaults() – Describe defaults to the user

  • func Set(name, value string) error – Sets value for a flag

  • func String(name string, value string, usage string) *string – Make a string flag

  • func StringVar(p *string, name string, value string, usage string) – Wrap p as a flag

  • func Uint(name string, value uint, usage string) *uint – Make a uint flag

  • func Uint64(name string, value uint64, usage string) *uint64 – Make a uint64 flag

  • func Uint64Var(p *uint64, name string, value uint64, usage string) – Wrap p as a flag

  • func UintVar(p *uint, name string, value uint, usage string) – Wrap p as a flag

  • func UnquoteUsage(flag *Flag) (name string, usage string) – Get a flag’s description

  • func Var(value Value, name string, usage string) – Make a generic flag

  • func Visit(fn func(*Flag)) – Apply f to all set flags

  • func VisitAll(fn func(*Flag)) – Apply f to all flags

A Flag has this function:
  • func Lookup(name string) *Flag – Get defined flag by name

A FlagSet has these functions. Many are the same as described earlier and will not be redescribed:
  • func NewFlagSet(name string, errorHandling ErrorHandling) *FlagSet

  • func (f *FlagSet) Arg(i int) string

  • func (f *FlagSet) Args() []string

  • func (f *FlagSet) Bool(name string, value bool, usage string) *bool

  • func (f *FlagSet) BoolVar(p *bool, name string, value bool, usage string)

  • func (f *FlagSet) Duration(name string, value time.Duration, usage string) *time.Duration

  • func (f *FlagSet) DurationVar(p *time.Duration, name string, value time.Duration, usage string)

  • func (f *FlagSet) ErrorHandling() ErrorHandling

  • func (f *FlagSet) Float64(name string, value float64, usage string) *float64

  • func (f *FlagSet) Float64Var(p *float64, name string, value float64, usage string)

  • func (f *FlagSet) Init(name string, errorHandling ErrorHandling)

  • func (f *FlagSet) Int(name string, value int, usage string) *int

  • func (f *FlagSet) Int64(name string, value int64, usage string) *int64

  • func (f *FlagSet) Int64Var(p *int64, name string, value int64, usage string)

  • func (f *FlagSet) IntVar(p *int, name string, value int, usage string)

  • func (f *FlagSet) Lookup(name string) *Flag

  • func (f *FlagSet) NArg() int

  • func (f *FlagSet) NFlag() int

  • func (f *FlagSet) Name() string

  • func (f *FlagSet) Output() io.Writer

  • func (f *FlagSet) Parse(arguments []string) error

  • func (f *FlagSet) Parsed() bool

  • func (f *FlagSet) PrintDefaults()

  • func (f *FlagSet) Set(name, value string) error

  • func (f *FlagSet) SetOutput(output io.Writer)

  • func (f *FlagSet) String(name string, value string, usage string) *string

  • func (f *FlagSet) StringVar(p *string, name string, value string, usage string)

  • func (f *FlagSet) Uint(name string, value uint, usage string) *uint

  • func (f *FlagSet) Uint64(name string, value uint64, usage string) *uint64

  • func (f *FlagSet) Uint64Var(p *uint64, name string, value uint64, usage string)

  • func (f *FlagSet) UintVar(p *uint, name string, value uint, usage string)

  • func (f *FlagSet) Var(value Value, name string, usage string)

  • func (f *FlagSet) Visit(fn func(*Flag))

  • func (f *FlagSet) VisitAll(fn func(*Flag))

The Go flags package is opinionated. It supports a restricted style of flags. Different operating system (OS) types may have different flag styles. Especially Windows that typically uses the forward slash (“/”) character instead of the dash (“-”) to introduce flags.

To make your programs match the OS styles, you may need to write code to parse the command line differently. The runtime.GOOS value can be used to determine the OS type. You may find community packages that can help here.

Log Package

The log package provides a simple logging feature. Many community extensions exist. Some are drop-in replacements, while others use different styles. Many community offerings consume (or façade) this logging feature. This package is something like the Java Log4J1 or similar logging frameworks.

This package provides package-level logging functions. It also has a Logger interface with similar functions that any code can implement. It provides a formatted log prefix for any messages directed to some io.Writer, such as STDOUT. The details, such as date and time format, source file reference, etc., of the prefix string can be configured. Some log actions can raise a panic or exit the program.

In Java using Log4J, one would do this:
import ...Log4J.Logger;
static Logger log = Logger.getLogger(<myclass>.class);
:
log.trace("Program running...")
:
log.trace("Program done")
A basic Go log sequence is
import "log"
:
log.Print("Program running...")
:
log.Print("Program done")

Note this predefined logger logs to STDERR.

Similarly, if you have a Logger instance, you can configure it:
import "log"
var logger = log.New(<someWriter>, "<someLinePrefix>", <flags>)
:
logger.Print("Program running...")
:
logger.Print("Program done")
Either might output a line like the following depending on the configuration:
2021/01/01 00:00:00.123456 /x/y/x.go:100: Program running...

Note that there is no severity provided. Many third-party logging offerings add this and other statistics. See the following example.

Since logger creation takes a Writer as an argument, logging can be to many targets, including persistent ones like files. Clients (creators) of the Logger need to open and close such targets. For example, to log the output of a complete program:
var DefaultLogger log.Logger
var DefaultLoggerTarget os.File
var DefaultLoggerTargetPath = "main.log"
:
func main() {
      var f *os.File
      if f, err := os.Create(DefaultLoggerTargetPath); err != nil {
            log.Fatalf("Cannot create log file: %s ",
                  DefaultLoggerTargetPath)
      }
      defer f.Close()
      DefaultLoggerTarget = f
      DefaultLogger = log.New(f, "main ", log.LstdFlags)
      defer DefaultLogger.Flush()
      DefaultLogger.Println("main starting...")
      :
      DefaultLogger.Flush()
      :
      DefaultLogger.Println("main done")
}

Note the logger is made a public top-level value so it can be accessed from all functions in the program. This is easier than passing the logger instance around the programs as a function argument. This example recreates the log each time the program is run. One can use the Open function (vs. Create) to (say) append to an existing log.

The log output may not be written until the program exits. If one exposes the created file as a public top-level value (as done in the example), a coder could use the Flush function on it to force the data to be written at other times.

As an example of using Go logging, Listing 17-1 shows a simple extension/wrapper to it as the Go community might provide. It provides a Logger interface that outputs leveled logging messages that any logging engine needs to implement. This logger is not API equivalent to the standard logger and is thus not a drop-in replacement.

The example provides a default engine implementation called DefaultLoggerImpl. There are helper functions that access the current state, including getting the id of the calling goroutine; this is something that the Go runtime does not provide a function to directly access.
type Logger interface {
      Error(format string, args ...interface{})
      Warn(format string, args ...interface{})
      Info(format string, args ...interface{})
      Debug(format string, args ...interface{})
      Trace(format string, args ...interface{})
}
type DefaultLoggerImpl struct{
      logger log.Logger
}
func (l *DefaultLoggerImpl) output(level, format string, args ...interface{}) {
      l.logger.Printf(fmt.Sprintf("%s %s %s ",getCallerDetails(2, "-"),level, fmt.Sprintf(format, args...)))
}
func (l *DefaultLoggerImpl) Error(format string, args ...interface{}) {
      l.output("ERROR", format, args...)
}
func (l *DefaultLoggerImpl) Warn(format string, args ...interface{}) {
      l.output("WARN ", format, args...)
}
func (l *DefaultLoggerImpl) Info(format string, args ...interface{}) {
      l.output("INFO ", format, args...)
}
func (l *DefaultLoggerImpl) Debug(format string, args ...interface{}) {
      l.output("DEBUG", format, args...)
}
func (l *DefaultLoggerImpl) Trace(format string, args ...interface{}) {
      l.output("TRACE", format, args...)
}
var DefaultLogger  *DefaultLoggerImpl
func init(){
      DefaultLogger = &DefaultLoggerImpl{}
      DefaultLogger.logger = log.New(os.Stdout, "GoBook ", log.LstdFlags|log.Lmicroseconds|log.LUTC)
}
// get details about the caller.
func getCallerDetails(level int, lead string) string {
      level++
      if pc, file, line, ok := runtime.Caller(level); ok {
            file = getName(file)
            goId := getGID()
            xlineCount := atomic.AddUint64(&lineCount, 1)
            lead = fmt.Sprintf("%7d go%-5d %08X %-40v@%4v", xlineCount, goId, pc, file, line)
      }
      return lead
}
var lineCount uint64
// Get the current goroutine id.
func getGID() (n uint64) {
      b := make([]byte, 64)
      b = b[:runtime.Stack(b, false)]
      b = bytes.TrimPrefix(b, []byte("goroutine "))
      b = b[:bytes.IndexByte(b, ' ')]
      n, _ = strconv.ParseUint(string(b), 10, 64)
      return
}
//  Get the file name part.
func getName(file string) string {
      posn := strings.Index(file, src)
      if posn >= 0 {
            file = file[posn+len(src):]
            if strings.HasSuffix(file, goExtension) {
                  file = file[0 : len(file)-len(goExtension)]
            }
      }
      return file
}
const src = "/src/"
const goExtension = ".go"
Listing 17-1

Sample Logger Implementation

It can be used like this where logging is done in different goroutines, as shown in Listing 17-2 .
DefaultLogger.Trace("Hello %s!", "World")
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
      wg.Add(1)
      go func(id int) {
            defer wg.Done()
            DefaultLogger.Info("Hello from goroutine %d!", id)
            time.Sleep( time.Duration(rand.Intn(2000)) * time.Millisecond)
            DefaultLogger.Info("Goodbye from goroutine %d!", id)
      }(i)
}
wg.Wait()
DefaultLogger.Trace("Goodbye %s!", "World")
Listing 17-2

Sample Logger Implementation Client

This example produces an output like this:
GoBook 2020/12/18 15:21:57.365337       1 go1     004D6AE7 main/main                               @ 122 TRACE Hello World!
GoBook 2020/12/18 15:21:57.366333       3 go15    004D6D97 main/main                               @ 128 INFO  Hello from goroutine 9!
GoBook 2020/12/18 15:21:57.366333       5 go9     004D6D97 main/main                               @ 128 INFO  Hello from goroutine 3!
GoBook 2020/12/18 15:21:57.366333       4 go6     004D6D97 main/main                               @ 128 INFO  Hello from goroutine 0!
GoBook 2020/12/18 15:21:57.366333       2 go7     004D6D97 main/main                               @ 128 INFO  Hello from goroutine 1!
GoBook 2020/12/18 15:21:57.366333       7 go10    004D6D97 main/main                               @ 128 INFO  Hello from goroutine 4!
GoBook 2020/12/18 15:21:57.366333       9 go14    004D6D97 main/main                               @ 128 INFO  Hello from goroutine 8!
GoBook 2020/12/18 15:21:57.366333       8 go11    004D6D97 main/main                               @ 128 INFO  Hello from goroutine 5!
GoBook 2020/12/18 15:21:57.366333      10 go12    004D6D97 main/main                               @ 128 INFO  Hello from goroutine 6!
GoBook 2020/12/18 15:21:57.366333       6 go8     004D6D97 main/main                               @ 128 INFO  Hello from goroutine 2!
GoBook 2020/12/18 15:21:57.366333      11 go13    004D6D97 main/main                               @ 128 INFO  Hello from goroutine 7!
GoBook 2020/12/18 15:21:57.426070      12 go7     004D6E84 main/main                               @ 130 INFO  Goodbye from goroutine 1!
GoBook 2020/12/18 15:21:57.447973      13 go15    004D6E84 main/main                               @ 130 INFO  Goodbye from goroutine 9!
GoBook 2020/12/18 15:21:57.447973      14 go10    004D6E84 main/main                               @ 130 INFO  Goodbye from goroutine 4!
GoBook 2020/12/18 15:21:57.792721      15 go11    004D6E84 main/main                               @ 130 INFO  Goodbye from goroutine 5!
GoBook 2020/12/18 15:21:57.822589      16 go8     004D6E84 main/main                               @ 130 INFO  Goodbye from goroutine 2!
GoBook 2020/12/18 15:21:57.917368      17 go12    004D6E84 main/main                               @ 130 INFO  Goodbye from goroutine 6!
GoBook 2020/12/18 15:21:58.674824      18 go13    004D6E84 main/main                               @ 130 INFO  Goodbye from goroutine 7!
GoBook 2020/12/18 15:21:58.684779      19 go14    004D6E84 main/main                               @ 130 INFO  Goodbye from goroutine 8!
GoBook 2020/12/18 15:21:59.228337      20 go6     004D6E84 main/main                               @ 130 INFO  Goodbye from goroutine 0!
GoBook 2020/12/18 15:21:59.254222      21 go9     004D6E84 main/main                               @ 130 INFO  Goodbye from goroutine 3!
GoBook 2020/12/18 15:21:59.254222      22 go1     004D6C2E main/main                               @ 134 TRACE Goodbye World!

Note the different goroutine ids (go##). Having the goroutine id present in logging helps a lot when looking at traces of code that uses multiple goroutines. Otherwise, the logging can seem very jumbled and confusing.

The records are assigned line numbers when created, not when written to the console by the Go logger; thus, the line numbers do not always come out in sequential order because the goroutines do not run in any predictable order. Line numbers are generally sequential within a single goroutine.

The actual code location inside the executable is shown (in hexadecimal) of the logging caller. This can help in crashes. If the logging calls had come from different packages, that would be shown. In this case, all the logging called were done from the same package (main) and function (main). The calling line number is included.

The result of runtime.Stack(b, false) starts like the following text. This is how getGID() can access the id:
goroutine 1 [running]:
:

Note it is not guaranteed that the Stack method’s output will not change in the future, thus rendering this code obsolete.

Math Package

The math package provides functions similar to the java.math package. Combined with Go’s complex number type, that makes Go a stronger numeric processing language relative to Java. It provides useful constants and mathematical functions. The constants are E, Pi, Phi, Sqrt2, SqrtE, SqrtPi, SqrtPhi, Ln2, Log2E (1/Ln2), Ln10, Log10E (1/Ln10). Most are to at least 60 digits of precision.

The math package has several sub-packages:
  • big provides a big integer (Int, much like java.math.BigInteger), a big float (Float, similar to but not the same as java.math.BigDecimal), and a Rational (Rat, no Java equivalent) number type.

  • bits provide functions to count, access, and change bits in unsigned integer types.

  • cmplx (note the odd name, this is because complex is a reserved word) provides useful constants and mathematical functions for the complex type.

  • rand provides random number generation.

The math package provides these (self-explanatory) functions:
  • func Gamma(x float64) float64

  • func Hypot(p, q float64) float64

  • func Ilogb(x float64) int

  • func Inf(sign int) float64

  • func IsInf(f float64, sign int) bool

  • func IsNaN(f float64) (is bool)

  • func J0(x float64) float64

  • func J1(x float64) float64

  • func Jn(n int, x float64) float64

  • func Ldexp(frac float64, exp int) float64

  • func Lgamma(x float64) (lgamma float64, sign int)

  • func Log(x float64) float64

  • func Log10(x float64) float64

  • func Log1p(x float64) float64

  • func Log2(x float64) float64

  • func Logb(x float64) float64

  • func Max(x, y float64) float64

  • func Min(x, y float64) float64

  • func Mod(x, y float64) float64

  • func Modf(f float64) (int float64, frac float64)

  • func NaN() float64

  • func Nextafter(x, y float64) (r float64)

  • func Nextafter32(x, y float32) (r float32)

  • func Pow(x, y float64) float64

  • func Pow10(n int) float64

  • func Remainder(x, y float64) float64

  • func Round(x float64) float64

  • func RoundToEven(x float64) float64

  • func Signbit(x float64) bool

  • func Sin(x float64) float64

  • func Sincos(x float64) (sin, cos float64)

  • func Sinh(x float64) float64

  • func Sqrt(x float64) float64

  • func Tan(x float64) float64

  • func Tanh(x float64) float64

  • func Trunc(x float64) float64

  • func Y0(x float64) float64

  • func Y1(x float64) float64

  • func Yn(n int, x float64) float64

As an example of using functions in the math package, Listing 17-3 shows a simple function plotting example:
var ErrBadRange = errors.New("bad range")
type PlotFunc func(in float64) (out float64)
// Print (to STDOUT) the plots of one or more functions.
func PlotPrinter(xsteps, ysteps int, xmin, xmax, ymin, ymax float64,
      fs ...PlotFunc) (err error) {
      xdiff, ydiff := xmax-xmin, ymax-ymin
      if xdiff <= 0 || ydiff <= 0 {
            err = ErrBadRange
            return
      }
      xstep, ystep := xdiff/float64(xsteps), ydiff/float64(ysteps)
      plots := make([][]float64, len(fs))
      for index, xf := range fs {
            plot := make([]float64, xsteps)
            plots[index] = plot
            err = DoPlot(plot, xf, xsteps, ysteps, xmin, xmax, ymin, ymax, xstep)
            if err != nil {
                  return
            }
      }
      PrintPlot(xsteps, ysteps, ymin, ymax, ystep, plots)
      return
}
// Plot the values of the supplied function.
func DoPlot(plot []float64, f PlotFunc, xsteps, ysteps int,
          xmin, xmax, ymin, ymax, xstep float64) (err error) {
      xvalue := xmin
      for i := 0; i < xsteps; i++ {
            v := f(xvalue)
            if v < ymin || v > ymax {
                  err = ErrBadRange
                  return
            }
            xvalue += xstep
            plot[i] = v
      }
      return
}
// Print the plots of the supplied data.
func PrintPlot(xsteps, ysteps int, ymin float64, ymax float64, ystep float64,
          plots [][]float64) {
      if xsteps <= 0 || ysteps <= 0 {
            return
      }
      middle := ysteps / 2
      for yIndex := 0; yIndex < ysteps; yIndex++ {
            fmt.Printf("%8.2f: ", math.Round((ymax-float64(yIndex)*ystep)*100)/100)
            ytop, ybottom := ymax-float64(yIndex)*ystep, ymax-float64(yIndex+1)*ystep
            for xIndex := 0; xIndex < xsteps; xIndex++ {
                  pv := " "
                  if yIndex == middle {
                        pv = "-"
                  }
                  for plotIndex := 0; plotIndex < len(plots); plotIndex++ {
                        v := plots[plotIndex][xIndex]
                        if v <= ytop && v >= ybottom {
                              pv = string(markers[plotIndex%len(markers)])
                        }
                  }
                  fmt.Print(pv)
            }
            fmt.Println()
      }
      fmt.Printf("%8.2f: ", math.Round((ymax-float64(ysteps+1)*ystep)*100)/100)
}
const markers = "*.^~-=+"
Listing 17-3

Sample Plotting of a Math Function Client

It is driven by this test function:
func testPlotPrint() {
      err := PlotPrinter(100, 20, 0, 4*math.Pi, -1.5, 4,
            func(in float64) float64 {
                  return math.Sin(in)
            }, func(in float64) float64 {
                  return math.Cos(in)
            }, func(in float64) float64 {
                  if in == 0 {
                        return 0
                  }
                  return math.Sqrt(in) / in
            })
      if err != nil {
            fmt.Printf("plotting failed: %v", err)
      }
}

Note that three different function literals, which conform to the PlotFunc type, for different sample equations are passed.

This produces the output shown in Figure 17-1.
../images/516433_1_En_17_Chapter/516433_1_En_17_Fig1_HTML.jpg
Figure 17-1

Sample plotting of math functions output

Note that three plots, using “^”, “.”, and “*” as markers, are superimposed on the graph. The middle of the graph (not the zero point) is marked with a line of dashes.

The big package provides these types:
  • Float – An extended precision floating-point value

  • Int – An extended (large) precision integer value

  • Rat – A Rational number value composed of an int64 numerator and denominator

A Float type has these functions (most self-explanatory):
  • func NewFloat(x float64) *Float

  • func ParseFloat(s string, base int, prec uint, mode RoundingMode) (f *Float, b int, err error)

  • func (z *Float) Abs(x *Float) *Float

  • func (z *Float) Add(x, y *Float) *Float

  • func (x *Float) Append(buf []byte, fmt byte, prec int) []byte

  • func (x *Float) Cmp(y *Float) int – Compare

  • func (z *Float) Copy(x *Float) *Float

  • func (x *Float) Float32() (float32, Accuracy)

  • func (x *Float) Float64() (float64, Accuracy)

  • func (x *Float) Format(s fmt.State, format rune)

  • func (x *Float) Int(z *Int) (*Int, Accuracy)

  • func (x *Float) Int64() (int64, Accuracy)

  • func (x *Float) IsInf() bool

  • func (x *Float) IsInt() bool

  • func (x *Float) MantExp(mant *Float) (exp int)

  • func (x *Float) MinPrec() uint

  • func (x *Float) Mode() RoundingMode

  • func (z *Float) Mul(x, y *Float) *Float

  • func (z *Float) Neg(x *Float) *Float

  • func (z *Float) Parse(s string, base int) (f *Float, b int, err error)

  • func (x *Float) Prec() uint

  • func (z *Float) Quo(x, y *Float) *Float

  • func (x *Float) Rat(z *Rat) (*Rat, Accuracy)

  • func (z *Float) Scan(s fmt.ScanState, ch rune) error

  • func (z *Float) Set(x *Float) *Float

  • func (z *Float) SetFloat64(x float64) *Float

  • func (z *Float) SetInf(signbit bool) *Float

  • func (z *Float) SetInt(x *Int) *Float

  • func (z *Float) SetInt64(x int64) *Float

  • func (z *Float) SetMantExp(mant *Float, exp int) *Float

  • func (z *Float) SetMode(mode RoundingMode) *Float

  • func (z *Float) SetPrec(prec uint) *Float

  • func (z *Float) SetRat(x *Rat) *Float

  • func (z *Float) SetString(s string) (*Float, bool)

  • func (z *Float) SetUint64(x uint64) *Float

  • func (x *Float) Sign() int

  • func (x *Float) Signbit() bool

  • func (z *Float) Sqrt(x *Float) *Float

  • func (x *Float) String() string

  • func (z *Float) Sub(x, y *Float) *Float

  • func (x *Float) Text(format byte, prec int) string

  • func (x *Float) Uint64() (uint64, Accuracy)

An Int type has these functions (most self-explanatory):
  • func NewInt(x int64) *Int

  • func (z *Int) Abs(x *Int) *Int

  • func (z *Int) Add(x, y *Int) *Int

  • func (z *Int) And(x, y *Int) *Int

  • func (z *Int) AndNot(x, y *Int) *Int

  • func (x *Int) Append(buf []byte, base int) []byte

  • func (z *Int) Binomial(n, k int64) *Int

  • func (x *Int) Bit(i int) uint

  • func (x *Int) BitLen() int

  • func (x *Int) Bits() []Word

  • func (x *Int) Bytes() []byte

  • func (x *Int) Cmp(y *Int) (r int) – Compare

  • func (x *Int) CmpAbs(y *Int) int – Compare absolute

  • func (z *Int) Div(x, y *Int) *Int

  • func (z *Int) DivMod(x, y, m *Int) (*Int, *Int)

  • func (z *Int) Exp(x, y, m *Int) *Int

  • func (x *Int) FillBytes(buf []byte) []byte

  • func (x *Int) Format(s fmt.State, ch rune)

  • func (z *Int) GCD(x, y, a, b *Int) *Int

  • func (x *Int) Int64() int64

  • func (x *Int) IsInt64() bool

  • func (x *Int) IsUint64() bool

  • func (z *Int) Lsh(x *Int, n uint) *Int

  • func (z *Int) Mod(x, y *Int) *Int

  • func (z *Int) ModInverse(g, n *Int) *Int

  • func (z *Int) ModSqrt(x, p *Int) *Int

  • func (z *Int) Mul(x, y *Int) *Int

  • func (z *Int) MulRange(a, b int64) *Int

  • func (z *Int) Neg(x *Int) *Int

  • func (z *Int) Not(x *Int) *Int

  • func (z *Int) Or(x, y *Int) *Int

  • func (x *Int) ProbablyPrime(n int) bool

  • func (z *Int) Quo(x, y *Int) *Int

  • func (z *Int) QuoRem(x, y, r *Int) (*Int, *Int)

  • func (z *Int) Rand(rnd *rand.Rand, n *Int) *Int

  • func (z *Int) Rem(x, y *Int) *Int

  • func (z *Int) Rsh(x *Int, n uint) *Int

  • func (z *Int) Scan(s fmt.ScanState, ch rune) error

  • func (z *Int) Set(x *Int) *Int

  • func (z *Int) SetBit(x *Int, i int, b uint) *Int

  • func (z *Int) SetBits(abs []Word) *Int

  • func (z *Int) SetBytes(buf []byte) *Int

  • func (z *Int) SetInt64(x int64) *Int

  • func (z *Int) SetString(s string, base int) (*Int, bool)

  • func (z *Int) SetUint64(x uint64) *Int

  • func (x *Int) Sign() int

  • func (z *Int) Sqrt(x *Int) *Int

  • func (x *Int) String() string

  • func (z *Int) Sub(x, y *Int) *Int

  • func (x *Int) Text(base int) string

  • func (x *Int) TrailingZeroBits() uint

  • func (x *Int) Uint64() uint64

  • func (z *Int) Xor(x, y *Int) *Int

As an example of using the Int type, consider the N! (N factorial) function. N! is defined:
  • N < 0: undefined

  • N == 0: 1

  • N > 0: N * (N-1)!

Note that N!, as implemented in Listing 17-4, gets large very quickly as N increases. Even with small N (<< 100), the value exceeds what can be held in an uint64 (the biggest machine integer) type.
var ErrBadArgument = errors.New("invalid argument")
func factorial(n int) (res *big.Int, err error) {
      if n < 0 || n >= 1_000 {  // limit result and time
            err = ErrBadArgument
            return   // or raise panic
      }
      res = big.NewInt(1)
      for i := 2; i <= n; i++ {
            res = res.Mul(res, big.NewInt(int64(i)))
      }
      return
}
Listing 17-4

N! Function

The sequence:
fact, _ := factorial(100)
fmt.Println("Factorial(100):", fact)
produces this output:
Factorial(100): 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
A Rat type has these functions (most self-explanatory):
  • func NewRat(a, b int64) *Rat

  • func (z *Rat) Abs(x *Rat) *Rat

  • func (z *Rat) Add(x, y *Rat) *Rat

  • func (x *Rat) Cmp(y *Rat) int – Compare

  • func (x *Rat) Denom() *Int

  • func (x *Rat) Float32() (f float32, exact bool)

  • func (x *Rat) Float64() (f float64, exact bool)

  • func (x *Rat) FloatString(prec int) string

  • func (z *Rat) GobDecode(buf []byte) error

  • func (x *Rat) GobEncode() ([]byte, error)

  • func (z *Rat) Inv(x *Rat) *Rat

  • func (x *Rat) IsInt() bool

  • func (z *Rat) Mul(x, y *Rat) *Rat

  • func (z *Rat) Neg(x *Rat) *Rat

  • func (x *Rat) Num() *Int

  • func (z *Rat) Quo(x, y *Rat) *Rat

  • func (x *Rat) RatString() string

  • func (z *Rat) Scan(s fmt.ScanState, ch rune) error

  • func (z *Rat) Set(x *Rat) *Rat

  • func (z *Rat) SetFloat64(f float64) *Rat

  • func (z *Rat) SetFrac(a, b *Int) *Rat

  • func (z *Rat) SetFrac64(a, b int64) *Rat

  • func (z *Rat) SetInt(x *Int) *Rat

  • func (z *Rat) SetInt64(x int64) *Rat

  • func (z *Rat) SetString(s string) (*Rat, bool)

  • func (z *Rat) SetUint64(x uint64) *Rat

  • func (x *Rat) Sign() int

  • func (x *Rat) String() string

  • func (z *Rat) Sub(x, y *Rat) *Rat

The cmplx package provides these (self-explanatory) functions:
  • func Abs(x complex128) float64

  • func Acos(x complex128) complex128

  • func Acosh(x complex128) complex128

  • func Asin(x complex128) complex128

  • func Asinh(x complex128) complex128

  • func Atan(x complex128) complex128

  • func Atanh(x complex128) complex128

  • func Conj(x complex128) complex128

  • func Cos(x complex128) complex128

  • func Cosh(x complex128) complex128

  • func Cot(x complex128) complex128

  • func Exp(x complex128) complex128

  • func Inf() complex128

  • func IsInf(x complex128) bool

  • func IsNaN(x complex128) bool

  • func Log(x complex128) complex128

  • func Log10(x complex128) complex128

  • func NaN() complex128

  • func Phase(x complex128) float64

  • func Polar(x complex128) (r, θ float64)

  • func Pow(x, y complex128) complex128

  • func Rect(r, θ float64) complex128

  • func Sin(x complex128) complex128

  • func Sinh(x complex128) complex128

  • func Sqrt(x complex128) complex128

  • func Tan(x complex128) complex128

  • func Tanh(x complex128) complex128

The rand package provides these types:
  • Rand – A random number generator.

  • Source – A 63-bit seed source for random numbers; defaults to the same value on all executions, resulting in repeated “random” sequences; this differs from Java behavior.

The rand package provides these functions:
  • func ExpFloat64() float64 – Get exponentially distributed value

  • func Float32() float32 – Get [0.0, 1.0)

  • func Float64() float64 – Get [0.0, 1.0)

  • func Int() int

  • func Int31() int32

  • func Int31n(n int32) int32 – Get [0, n)

  • func Int63() int64

  • func Int63n(n int64) int64 – Get [0, n)

  • func Intn(n int) int – Get [0, n)

  • func NormFloat64() float64 – Get normally distributed value

  • func Perm(n int) []int – Get permutation of [0.0, 1.0)

  • func Read(p []byte) (n int, err error) – Read n bytes into p

  • func Seed(seed int64)

  • func Shuffle(n int, swap func(i, j int)) – Shuffle n items. Items access via swap closure

  • func Uint32() uint32

  • func Uint64() uint64

Note do not depend on the preceding generator functions to seed the generator randomly (say by process start time). You must do this yourself if needed. Without seeding, each execution of a Go program will repeat the same sequence of random values.

The Rand type provides these functions. See the previous list for explanations:
  • func New(src Source) *Rand

  • func (r *Rand) ExpFloat64() float64

  • func (r *Rand) Float32() float32

  • func (r *Rand) Float64() float64

  • func (r *Rand) Int() int

  • func (r *Rand) Int31() int32

  • func (r *Rand) Int31n(n int32) int32

  • func (r *Rand) Int63() int64

  • func (r *Rand) Int63n(n int64) int64

  • func (r *Rand) Intn(n int) int

  • func (r *Rand) NormFloat64() float64

  • func (r *Rand) Perm(n int) []int

  • func (r *Rand) Read(p []byte) (n int, err error)

  • func (r *Rand) Seed(seed int64)

  • func (r *Rand) Shuffle(n int, swap func(i, j int))

  • func (r *Rand) Uint32() uint32

  • func (r *Rand) Uint64() uint64

The Source type provides this function:
  • func NewSource(seed int64) Source

Operating System Support Packages

The os package provides access to operating system (OS) functionality in an OS-agnostic way. This is the preferred way to access these functions. This package has sub-packages:
  • exec provides the ability to launch external processes as command-line style programs.

  • signal provides the ability to watch for and capture operating system originated signals (aka interrupts).

  • user provides access to operating system user and group accounts.

The path package provides utility functions to process operating system file system paths. It has one sub-package:
  • filepath provides utility functions to parse and process operating system file paths.

The syscall package provides access to low-level operating system functions not provided by other packages. It is somewhat operating system type dependent, and thus its functions may not work the same on all OS types.

The os package has these types:
  • File – Represents access to a file

  • FileInfo – Represents metadata about a file

  • FileMode – File access modes (as bit flags)

  • Process – Represents an external process

  • ProcessState – Represents a process exit status

The os package has these useful constants:
  • PathSeparatorOS-specific file path separator

  • PathListSeparator – OS-specific shell path list separator

The os package has these useful values:
  • Stdin – File for/dev/stdin.

  • Stdout – File for standard out.

  • Stderr – File for standard err.

  • Args[]string of command-line arguments; unlike in Java, argument 0 is the command that launched the program.

The os package has these functions. These are based on the Unix functions of the same name. Some functions may not work (say return useful values) on all operating system types, especially Microsoft Windows:
  • func Chdir(dir string) error – Change the current directory

  • func Chmod(name string, mode FileMode) error – Change file mode

  • func Chown(name string, uid, gid int) error – Change the file owner

  • func Chtimes(name string, atime time.Time, mtime time.Time) error – Change file times

  • func Clearenv() – Clear the process environment

  • func Environ() []string – Get the process environment

  • func Executable() (string, error) – Get the active program path

  • func Exit(code int) – Force exit of this process

  • func Expand(s string, mapping func(string) string) string – Replace ${var},$vaR in string

  • func ExpandEnv(s string) string – Replace ${var},$vaR in string using the environment

  • func Getegid() int – Get the effective group id

  • func Getenv(key string) string – Get the environment value by key

  • func Geteuid() int – Get the effective user id

  • func Getgid() int – Get the group id of the user

  • func Getgroups() ([]int, error) – Get group ids the user belongs to

  • func Getpagesize() int – Get the virtual memory page size

  • func Getpid() int – Get the current process id

  • func Getppid() int – Get the current process’s parent id

  • func Getuid() int – Get the user id

  • func Getwd() (dir string, err error) – Get the working directory

  • func Hostname() (name string, err error) – Get the system’s hostname

  • func IsExist(err error) bool – Tests error for “exists”

  • func IsNotExist(err error) bool – Tests error for “not exists”

  • func IsPathSeparator(c uint8) bool – Is c a path separator

  • func IsPermission(err error) bool – Tests error for “permission issue”

  • func IsTimeout(err error) bool – Tests error for “timeout”

  • func Lchown(name string, uid, gid int) error – Change the owner of the file/link

  • func Link(oldname, newname string) error – Create a hard link between files

  • func LookupEnv(key string) (string, bool) – Get an environment value by key (name)

  • func Mkdir(name string, perm FileMode) error – Make a directory

  • func MkdirAll(path string, perm FileMode) error – Make all needed directories

  • func Pipe() (r *File, w *File, err error) – Create a pipe between files

  • func Readlink(name string) (string, error) – Reads a link

  • func Remove(name string) error – Removes (deletes) a file or empty directory

  • func RemoveAll(path string) error – Remove a directory tree

  • func Rename(oldpath, newpath string) error – Change a file/directory name

  • func SameFile(fi1, fi2 FileInfo) bool – Tests for the same file

  • func Setenv(key, value string) error – Set an environment value

  • func Symlink(oldname, newname string) error – Create a symbolic link between names

  • func TempDir() string – Get the current temporary directory

  • func Truncate(name string, size int64) error – Extend/shorten a file

  • func Unsetenv(key string) error – Remove an environment key

  • func UserCacheDir() (string, error) – Get the user cache directory

  • func UserConfigDir() (string, error) – Get the user configuration directory

  • func UserHomeDir() (string, error) – Get the user home directory

A File provides these functions:
  • func Create(name string) (*File, error) – Create/truncate a file

  • func Open(name string) (*File, error) – Open a file with default access

  • func OpenFile(name string, flag int, perm FileMode) (*File, error) – Open a file

  • func (f *File) Chdir() error – Make directory f the current directory

  • func (f *File) Chmod(mode FileMode) error – Change file mode

  • func (f *File) Chown(uid, gid int) error – Change the file owner

  • func (f *File) Close() error – Close an open file

  • func (f *File) Name() string – Get a file name

  • func (f *File) Read(b []byte) (n int, err error) – Read from the current location in a file

  • func (f *File) ReadAt(b []byte, off int64) (n int, err error) – Read from a location in a file

  • func (f *File) Readdir(n int) ([]FileInfo, error) – Read directory entries

  • func (f *File) Readdirnames(n int) (names []string, err error) – Read directory names

  • func (f *File) Seek(offset int64, whence int) (ret int64, err error) – Set the current location

  • func (f *File) Stat() (FileInfo, error) – Get file info

  • func (f *File) Sync() error – Flush pending changes

  • func (f *File) Truncate(size int64) error – Set the file length

  • func (f *File) Write(b []byte) (n int, err error) – Write bytes at the current location

  • func (f *File) WriteAt(b []byte, off int64) (n int, err error) – Write bytes at the location

  • func (f *File) WriteString(s string) (n int, err error) – Write a string

A FileInfo provides these functions:
  • func Lstat(name string) (FileInfo, error) – Get link/file info

  • func Stat(name string) (FileInfo, error) – Get file info

A FileMode provides these functions:
  • func (m FileMode) IsDir() bool – Test m represents a directory

  • func (m FileMode) IsRegular() bool – Test m represents a regular file

  • func (m FileMode) Perm() FileMode – Gets file mode

A Process provides these functions:
  • func FindProcess(pid int) (*Process, error) – Find by process id

  • func StartProcess(name string, argv []string, attr *ProcAttr) (*Process, error) – Create and start

  • func (p *Process) Kill() error – Kill a running process

  • func (p *Process) Release() error – Release resources if Wait not used

  • func (p *Process) Signal(sig Signal) error – Send a signal (interrupt) to a process

  • func (p *Process) Wait() (*ProcessState, error) – Wait for a process to end

A ProcessState provides these functions to access the state:
  • func (p *ProcessState) ExitCode() int

  • func (p *ProcessState) Exited() bool

  • func (p *ProcessState) Pid() int

  • func (p *ProcessState) Success() bool

  • func (p *ProcessState) SystemTime() time.Duration

  • func (p *ProcessState) UserTime() time.Duration

The exec package has these types and functions:
  • func LookPath(file string) (string, error) – Look for an executable in the OS path; return a path

  • func Command(name string, arg ...string) *Cmd – Make a command

  • func CommandContext(ctx context.Context, name string, arg ...string) *Cmd – Make with context

The Cmd type has these functions:
  • func (c *Cmd) CombinedOutput() ([]byte, error) – Run and get stdout and stderr

  • func (c *Cmd) Output() ([]byte, error) – Run and get stdout

  • func (c *Cmd) Run() error – Run

  • func (c *Cmd) Start() error – Start

  • func (c *Cmd) StderrPipe() (io.ReadCloser, error) – Connect a pipe to stderr

  • func (c *Cmd) StdinPipe() (io.WriteCloser, error) – Connect a pipe to stdin

  • func (c *Cmd) StdoutPipe() (io.ReadCloser, error) – Connect a pipe to stdout

  • func (c *Cmd) Wait() error – Wait for the started command to end

Operating systems can send “signals” (asynchronous event notifications) when asked to by a user or program. Some programs may want to detect/intercept these signals. Go supports this via channels which are sent a message whenever a signal occurs.

These signals are always supported. Others may also be:
var Interrupt Signal = syscall.SIGINT
var Kill      Signal = syscall.SIGKILL
The signal package has these functions:
  • func Ignore(sig ...os.Signal) – Ignore the signal

  • func Ignored(sig os.Signal) bool – Test if ignored

  • func Notify(c chan<- os.Signal, sig ...os.Signal) – Enable the signal to the channel

  • func Reset(sig ...os.Signal) – Undoes a notify action

  • func Stop(c chan<- os.Signal) – Similar to reset but by the channel

The user package allows access to users and user groups. Not all operating system types support these functions.

The group provides these functions:
  • func LookupGroup(name string) (*Group, error) – Find by group name

  • func LookupGroupId(gid string) (*Group, error) – Find by group id

The user provides these functions:
  • func Current() (*User, error) – Get the current user

  • func Lookup(username string) (*User, error) – Find by user name

  • func LookupId(uid string) (*User, error) – Find by user id

  • func (u *User) GroupIds() ([]string, error) – Get the groups a user belongs to

As an example of using the os package, here is a function to read the contents of a file and return it as a string:
func ReadFile(filePath string) (text string, err error) {
      var f *os.File
      if f, err = os.Open(filePath); err != nil {
            return
      }
      defer f.Close()  // ensure closed
      var xtext []byte // accumulate result
      buffer := make([]byte, 16*1024)
      for { // read file in chunks
            n, xerr := f.Read(buffer)
            if xerr != nil {
                  if xerr == io.EOF {
                        break // EOF is OK error
                  }
                  err = xerr
                  return
            }
            if n == 0 {
                  continue
            }
            xtext = append(xtext, buffer[0:n]...)
      }
      text = string(xtext) // want as string
      return
}
Invoked by
text, err := ReadFile(`.../words.txt`)
if err!=nil {
      fmt.Printf("got: %v" , err)
      return
}
fmt.Printf("testFile: %q" , text)
produces:testFile: "Now is the time to come to the aid of our countrymen! "

Reflection Package

The reflect package provides reflection (runtime type and data introspection and/or creation) capabilities that allows the processing of data of arbitrary type. It is similar in concept to the java.lang.reflect package.

Reflection is a complex subject (it could have its own book), and the detailed usage is beyond the scope of this text.

In Go, each discrete value (not necessarily each array, slice, or map element) has a runtime type associated with it. Go provides functions to query the type at runtime. It also allows values to be created and/or changed dynamically at runtime. In most cases, the value being queried is declared as an interface{} type and thus could be many different types at runtime:
var x interface{}
:
fmt.Printf("%v is of type %T ", x, x)

This prints the current value and runtime type of the value in x.

A common thing to do via reflection is to test the type of a value. Functions like fmt.Sprintf() do this. Given
var values = []interface{}{0, 0.0, 0i, "", []int{}, map[int]int{},
   func() {}, }
one can test the types as follows:
for _, v := range values {
      switch v := reflect.ValueOf(v); v.Kind() {
      case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
            fmt.Println(v.Int())
      case reflect.Float32, reflect.Float64:
            fmt.Println(v.Float())
      case reflect.String:
            fmt.Println("-", v.String() , "-")
      default:
            fmt.Printf("other type %v: %v ", v.Kind(), v)
      }
}
This produces the following:
0
0
other type complex128: (0+0i)
-  -
other type slice: []
other type map: map[]
other type func: 0x81baf0

The ValueOf() method is used to dereference a potential *T type into a T type (*T and T are considered different when testing types). A kind is an integer (enum) form of the type of the value.

The reflect package has two main types:
  1. 1.

    Type – Represents a type at runtime; returned by the reflect.TypeOf(interface{}) function

     
  2. 2.

    Value – Represents the value of an interface type at runtime; returned by the reflect.ValueOf(interface{}) function

     

To get the value of a Value, one must call one of the Value methods based on the value’s kind. Asking for a value of the wrong kind can result in a panic.

Regular Expression Package

The regexp package provides an implementation of a Regular Expression (RE) parser and matcher. It is assumed the reader understands the concept of an RE. The particulars of the RE syntax and function are explained in the Go package documentation. Note many languages support REs, but most have subtle differences in how they work. This is true for Java vs. Go.

There are several variants of RE matching, using this (RE-like) pattern:
Xxxx(All)?(String)?(Submatch)?(Index)?
where when present
  • All – Match all non-overlapping segments

  • String – Match a string (vs. []byte)

  • Submatch – Return capture groups per (…) in the pattern

  • Index – Augment Submatch with the position of the match in the input

It provides these functions and types:
  • func Match(pattern string, b []byte) (matched bool, err error)

  • func MatchReader(pattern string, r io.RuneReader) (matched bool, err error)

  • func MatchString(pattern string, s string) (matched bool, err error)

  • func QuoteMeta(s string) string – Quote RE meta characters (e.g., * . ? +) in s

The Regexp type provides a regular expression engine:
  • func Compile(expr string) (*Regexp, error) – Compile Go regex

  • func CompilePOSIX(expr string) (*Regexp, error) – Compile POSIX regex

  • func MustCompile(str string) *Regexp – Compile or panic

  • func MustCompilePOSIX(str string) *Regexp – Compile or panic

  • func (re *Regexp) Copy() *Regexp

  • func (re *Regexp) Expand(dst []byte, template []byte, src []byte, match []int) []byte

  • func (re *Regexp) ExpandString(dst []byte, template string, src string, match []int) []byte

  • func (re *Regexp) Find(b []byte) []byte

  • func (re *Regexp) FindAll(b []byte, n int) [][]byte

  • func (re *Regexp) FindAllIndex(b []byte, n int) [][]int

  • func (re *Regexp) FindAllString(s string, n int) []string

  • func (re *Regexp) FindAllStringIndex(s string, n int) [][]int

  • func (re *Regexp) FindAllStringSubmatch(s string, n int) [][]string

  • func (re *Regexp) FindAllStringSubmatchIndex(s string, n int) [][]int

  • func (re *Regexp) FindAllSubmatch(b []byte, n int) [][][]byte

  • func (re *Regexp) FindAllSubmatchIndex(b []byte, n int) [][]int

  • func (re *Regexp) FindIndex(b []byte) (loc []int)

  • func (re *Regexp) FindReaderIndex(r io.RuneReader) (loc []int)

  • func (re *Regexp) FindReaderSubmatchIndex(r io.RuneReader) []int

  • func (re *Regexp) FindString(s string) string

  • func (re *Regexp) FindStringIndex(s string) (loc []int)

  • func (re *Regexp) FindStringSubmatch(s string) []string

  • func (re *Regexp) FindStringSubmatchIndex(s string) []int

  • func (re *Regexp) FindSubmatch(b []byte) [][]byte

  • func (re *Regexp) FindSubmatchIndex(b []byte) []int

  • func (re *Regexp) LiteralPrefix() (prefix string, complete bool) – Is prefix all of RE

  • func (re *Regexp) Longest() – Modify RE to match longest

  • func (re *Regexp) Match(b []byte) bool

  • func (re *Regexp) MatchReader(r io.RuneReader) bool

  • func (re *Regexp) MatchString(s string) bool

  • func (re *Regexp) NumSubexp() int

  • func (re *Regexp) ReplaceAll(src, repl []byte) []byte

  • func (re *Regexp) ReplaceAllFunc(src []byte, repl func([]byte) []byte) []byte

  • func (re *Regexp) ReplaceAllLiteral(src, repl []byte) []byte

  • func (re *Regexp) ReplaceAllLiteralString(src, repl string) string

  • func (re *Regexp) ReplaceAllString(src, repl string) string

  • func (re *Regexp) ReplaceAllStringFunc(src string, repl func(string) string) string

  • func (re *Regexp) Split(s string, n int) []string

  • func (re *Regexp) SubexpIndex(name string) int

  • func (re *Regexp) SubexpNames() []string

Go Runtime Packages

The runtime package contains functions that expose the Go runtime system. It is similar in role to the java.lang.System and java.lang.Runtime types. The runtime package has several sub-packages not covered in this text.

The runtime package has these functions:
  • func Caller(skip int) (pc uintptr, file string, line int, ok bool) – Get caller info

  • func GC() – Run garbage collection

  • func GOMAXPROCS(n int) int – Set the number of goroutine processors

  • func GOROOT() string – Get the Go install root

  • func Goexit() – Exit the calling goroutine

  • func Gosched() – Run another ready goroutine

  • func NumCPU() int – Get the number of CPU cores

  • func NumGoroutine() int – Get the active goroutine count

  • func SetFinalizer(obj interface{}, finalizer interface{}) – Set the finalizer func for an object

  • func Version() string – Get the Go version

Unlike in Java, where each Object has a finalize method, most Go data has no associated finalizer. If you have a value that needs finalization (i.e., resource cleanup at the time of garbage collection), you should use the SetFinalizer function on it, perhaps in a constructor function for the data type.

String Processing Packages

The strconv provides conversion functions that convert from and to (i.e., parse and format) the string type. The fmt package is also often used to format values.

The strconv package has this key constant:

const IntSize – The size in bits of the int (and uint and pointers) type; this can vary based on HW architecture (32/64-bit).

The strconv has these functions:
  • func AppendBool(dst []byte, b bool) []byte – Append a Boolean to dst

  • func AppendFloat(dst []byte, f float64, fmt byte, prec, bitSize int) []byte – Append a float to dst

  • func AppendInt(dst []byte, i int64, base int) []byte – Append a signed integer to dst

  • func AppendQuote(dst []byte, s string) []byte – Append quoted s to dst

  • func AppendQuoteRune(dst []byte, r rune) []byte – Append quoted r to dst

  • func AppendUint(dst []byte, i uint64, base int) []byte – Append an unsigned integer to dst

  • func Atoi(s string) (int, error) – Parse a string to an integer

  • func FormatBool(b bool) string – Boolean to string

  • func FormatComplex(c complex128, fmt byte, prec, bitSize int) string – Complex to string

  • func FormatFloat(f float64, fmt byte, prec, bitSize int) string – Float to string

  • func FormatInt(i int64, base int) string – Signed integer to string in base

  • func FormatUint(i uint64, base int) string – Unsigned integer to string in base

  • func IsGraphic(r rune) bool – True if a Unicode graphic char

  • func IsPrint(r rune) bool – True if a printable character

  • func Itoa(i int) string – Integer to string in base 10

  • func ParseBool(str string) (bool, error) – Parse a string to a Boolean

  • func ParseComplex(s string, bitSize int) (complex128, error) – Parse a string to a complex

  • func ParseFloat(s string, bitSize int) (float64, error) – Parse a string to a float

  • func ParseInt(s string, base int, bitSize int) (i int64, err error) – Parse a string to a signed integer

  • func ParseUint(s string, base int, bitSize int) (uint64, error) – Parse a string to an unsigned integer

  • func Quote(s string) string – Wrap a string in quotes with escapes if needed

  • func QuoteRune(r rune) string – Wrap a rune in quotes with escapes if needed

  • func Unquote(s string) (string, error) – Remove quotes and escapes

The strings package provides functions and types to ease the processing of strings. Note that in Go, like in Java, strings are immutable, so all these functions return new, not modified, strings. In Java, most of these functions are methods on the String or StringBuilder/Buffer types :
  • func Compare(a, b string) int – Compares a and b, returns -1, 0, 1; alternative to <, <=, ==, !=, >, >=

  • func Contains(s, substr string) bool – True if a string in s

  • func ContainsAny(s, chars string) bool – True if any char in chars is in s

  • func ContainsRune(s string, r rune) bool – True if r in s

  • func Count(s, substr string) int – Count of substr in s

  • func EqualFold(s, t string) bool – True if s == t after Unicode folding

  • func Fields(s string) []string – s split at whitespace

  • func FieldsFunc(s string, f func(rune) bool) []string – s split at chars where f returns true

  • func HasPrefix(s, prefix string) bool – True if s starts with a prefix

  • func HasSuffix(s, suffix string) bool – True if s ends with a suffix

  • func Index(s, substr string) int – >= 0 if substr in s

  • func IndexAny(s, chars string) int – >=0 if any char in chars in s

  • func IndexByte(s string, c byte) int – >= 0 if c in s

  • func IndexFunc(s string, f func(rune) bool) int – >= 0 if f true on any char

  • func IndexRune(s string, r rune) int – >=0 if r in s

  • func Join(elems []string, sep string) string – Concatenate items in elems with sep between

  • func LastIndex(s, substr string) int – >= 0 if substr in s from end

  • func LastIndexAny(s, chars string) int – >= 0 if any char in chars in s from end

  • func LastIndexByte(s string, c byte) int – >0 if c in s from end

  • func LastIndexFunc(s string, f func(rune) bool) int – >= 0 if f true on any char in s from end

  • func Map(mapping func(rune) rune, s string) string – Chars of s replaced with the mapping result or removed if rune < 0

  • func Repeat(s string, count int) string – s repeated count times

  • func Replace(s, old, new string, n int) string – Occurrences of old in s replaced with new up to n times

  • func ReplaceAll(s, old, new string) string – All occurrences of old in s replaced with new

  • func Split(s, sep string) []string – s split at occurrences of sep

  • func SplitAfter(s, sep string) []string – s split after occurrences of sep

  • func SplitAfterN(s, sep string, n int) []string – s split after occurrences of sep up to n times

  • func SplitN(s, sep string, n int) []string – s split at occurrences of sep up to n times

  • func Title(s string) string – Convert each word initial letter to title case

  • func ToLower(s string) string – Convert to all lowercase

  • func ToTitle(s string) string – Convert to all title case

  • func ToUpper(s string) string – Convert to all uppercase

  • func Trim(s, cutset string) string – Remove s leading/trailing chars in cutset

  • func TrimFunc(s string, f func(rune) bool) string – Remove s leading/trailing chars where f is true

  • func TrimLeft(s, cutset string) string – Remove s leading chars in cutset

  • func TrimLeftFunc(s string, f func(rune) bool) string – Remove s leading chars where f is true

  • func TrimPrefix(s, prefix string) string – Remove from s any prefix

  • func TrimRight(s, cutset string) string – Remove s trailing chars in cutset

  • func TrimRightFunc(s string, f func(rune) bool) string – Remove s trailing chars where f is true

  • func TrimSpace(s string) string – Remove s leading/trailing whitespace

  • func TrimSuffix(s, suffix string) string – Remove from s any suffix

Type Builder – Used to build strings (like Java’s StringBuilder)

Type Reader – Used to read text from a string as a source

The Builder type has these methods:
  • func (b *Builder) Cap() int – Current builder capacity

  • func (b *Builder) Grow(n int) – Add to builder capacity

  • func (b *Builder) Len() int – Current content length

  • func (b *Builder) Reset() – Sets length to 0

  • func (b *Builder) Write(p []byte) (int, error) – Add bytes

  • func (b *Builder) WriteByte(c byte) error – Add a byte

  • func (b *Builder) WriteRune(r rune) (int, error) – Add a rune

  • func (b *Builder) WriteString(s string) (int, error) – Add a string

The Reader type has these methods:
  • func NewReader(s string) *Reader – Make a reader on a string

  • func (r *Reader) Len() int – Get unread count

  • func (r *Reader) Read(b []byte) (n int, err error) – Read up to n bytes into b

  • func (r *Reader) ReadAt(b []byte, off int64) (n int, err error) – Read at position

  • func (r *Reader) ReadByte() (byte, error) – Read a byte

  • func (r *Reader) ReadRune() (ch rune, size int, err error) – Read a rune

  • func (r *Reader) Reset(s string) – Set to start

  • func (r *Reader) Seek(offset int64, whence int) (int64, error) – Set to position

  • func (r *Reader) Size() int64 – Get original (total) length

  • func (r *Reader) UnreadByte() error – Reverse read

  • func (r *Reader) UnreadRune() error – Reverse read

  • func (r *Reader) WriteTo(w io.Writer) (n int64, err error) – Copy to writer

Concurrency and Goroutines

The sync package provides goroutine synchronization support such as mutual exclusion functions. This is often used as replacement for the synchronized statement and select methods in Java. It has a function similar to the java.util.concurrent.locks package. The atomic sub-package provides atomic access to certain data types. It is similar to the java.util.concurrent.atomic package. The Go community provides many more concurrent types and serialization functions.

The sync package provides these types:
  • Cond – Provides a conditional variable; like Java’s Object.wait/notify{All} pairs.

  • Map – Provides behavior like Java ConcurrentHashMap.

  • Mutex – Provides access control to shared values; see java.util.concurrent packages.

  • RWMutex – A Mutex with multiple concurrent readers; see java.util.concurrent packages.

  • Once – Does a code block only one time; useful for singleton creation.

  • Pool – Like a cache of the same type values; members can be removed spontaneously.

  • WaitGroup – Used to wait for multiple goroutines to exit (like Thread.join() in Java).

Note that a synchronized block can be reentered by the same thread but not by other threads. Go has no predefined library that offers this behavior; Go locks will block the same goroutine that owns them (a deadlock).

Many of the preceding types have no direct analogue in Java, but they can often be approximated. Often, the reverse is also true; many Java concurrency functions can be easily emulated in Go. For example, the Once type could be emulated in Java as
@FunctionalInterface
public interface Onceable {
   void doOnce(Runnable r);
}
public class Oncer implements Onceable {
   private AtomicBoolean once = new AtomicBoolean(false);
   public void doOnce(Runnable r) {
     if(!once.getAndSet(true)) {
       r.run();
     }
   }
}
This is used as
Onceable oncer = new Oncer();
  for(var i = 0; i < N; i++) {
    oncer.doOnce(()->System.out.println("Hello World!"));
 }
}
In Go, this is done:
var once sync.Once
for i := 0; i < N; i++ {
      once.Do(func(){
            fmt.Println("Hello World!");
      })
}
Given:
type Locker interface {
      Lock()
      Unlock()
}
The Cond type has these methods:
  • func NewCond(l Locker) *Cond – Make a Cond

  • func (c *Cond) Broadcast() – Like Object.notifyAll

  • func (c *Cond) Signal() – Like Object.notify

  • func (c *Cond) Wait() – Like Object.wait

The (concurrent) Map type has these methods (generally self-explanatory – load => get; store => put):
  • func (m *Map) Delete(key interface{})

  • func (m *Map) Load(key interface{}) (value interface{}, ok bool)

  • func (m *Map) LoadAndDelete(key interface{}) (value interface{}, loaded bool)

  • func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)

  • func (m *Map) Range(f func(key, value interface{}) bool) – For range over keys

  • func (m *Map) Store(key, value interface{})

The Mutex type has these methods (generally self-explanatory) and thus is a Locker:
  • func (m *Mutex) Lock()

  • func (m *Mutex) Unlock()

The RWMutex type has these methods (generally self-explanatory) and thus is a Locker:
  • func (rw *RWMutex) Lock()

  • func (rw *RWMutex) RLock()

  • func (rw *RWMutex) RLocker() Locker

  • func (rw *RWMutex) RUnlock()

  • func (rw *RWMutex) Unlock()

The Once type has these methods (generally self-explanatory):
  • func (o *Once) Do(f func()) – f called only one time

The Pool type has these methods (generally self-explanatory):
  • func (p *Pool) Get() interface{} – Get and remove an arbitrary instance (all should be of type returned from New)

  • func (p *Pool) Put(x interface{}) – (Re)add an instance

  • Pool has a member function value New used to create entries if none is found.

The WaitGroup type has these methods (generally self-explanatory):
  • func (wg *WaitGroup) Add(delta int)

  • func (wg *WaitGroup) Done() – Same as Add(-1)

  • func (wg *WaitGroup) Wait() – Wait for count to go to 0

Testing Package

This section is a condensation of Chapter 17, “Go Unit Tests and Benchmarks.”

Java itself has no built-in testing framework, but good community testing frameworks exist. Many authors use the main method of a class as a test case. Because creating a main function is more overhead (not per class as in Java) in Go, this method of creating tests is not often used in Go. Similarly, since a package can have many init() functions, they can be used to conveniently contain test cases, but they must be manually enabled/disabled in code.

The Go testing package provides support and a framework for doing JUnit2-like repeatable testing of Go code. This package is reflection driven and has many types used to run test suites and benchmarks and is not generally used directly by testers.

In Go, a test suite is any Go source file with a name of the form xxxx_test.go (where xxxx is the suite name) that contains one or more test functions (aka test cases). The test code is often placed in the same package as the code under test (CUT), so it can access non-public names. These types of tests are called “white-box”3 tests. By placing the test code in a different package from the CUT, “black-box”4 tests can be made.

A test function has this form:
func TestXxx(*testing.T) {
      :
}

where Xxx is the name of the test case. There can be any number of such test cases in a test suite. The “go test” command will run them and the CUT and report the results. Note that the test suite has no main function. The test case interacts with the test runner via the T argument.

The typical structure of a test is like
func TestSomething(t *testing.T) {
      got := <run some test>
            want := <expected value>
      if got != want {
            t.Errorf("unexpected result %v (vs. %v)", got, want)
      }
}
Like test cases, benchmarks have a consistent form:
func BenchmarkXxx(*testing.B) {
      :
}

where Xxx is the name of the benchmark. There can be any number of such benchmarks in a test suite. The “go test” command with the benchmark option (off by default as benchmarks often take significant elapsed time) will run them and report the results. The benchmark interacts with the benchmark runner via the B argument.

The typical structure of a benchmark is like
func BenchmarkSomething(b *testing.B) {
      : do some setup which may take time
      b.ResetTimer()
      for i := 0; i < b.N; i++ {
            : some code to time
      }
}

The benchmark runner will determine a good N value (often quite large) to use. Thus, running benchmarks can take a lot of time and probably should not be done on each build.

The key types are B and T:
  • B – Context/helpers for benchmark functions

  • BenchmarkResult – Struct with benchmark results as fields

  • PB – Supports running benchmarks in parallel

  • T – Context/helpers for test case functions

  • TB – Methods in both type T and type B

The testing package has these functions:
  • func AllocsPerRun(runs int, f func()) (avg float64) – Get average allocation per call of f

  • func Short() bool – Reports short options

  • func Verbose() bool – Reports verbose options

The B (benchmark) type has these functions:
  • func (c *B) Cleanup(f func()) – Call f to clean after benchmarks

  • func (c *B) Error(args ...interface{}) – Log and then fail

  • func (c *B) Errorf(format string, args ...interface{}) – Formatted log and then fail

  • func (c *B) Fail() – Mark failed

  • func (c *B) FailNow() – Fail and exit

  • func (c *B) Failed() bool – Test failed

  • func (c *B) Fatal(args ...interface{}) – Log and then fail now

  • func (c *B) Fatalf(format string, args ...interface{}) – Formatted log and then fail now

  • func (c *B) Helper() – Mark a caller as a helper (not traced)

  • func (c *B) Log(args ...interface{}) – Log values

  • func (c *B) Logf(format string, args ...interface{}) – Formatted log

  • func (c *B) Name() string – Get benchmark name

  • func (b *B) ReportAllocs() – Enable allocation tracking

  • func (b *B) ReportMetric(n float64, unit string) – Sets report scales

  • func (b *B) ResetTimer() – Reset benchmark timer and counts

  • func (b *B) Run(name string, f func(b *B)) bool – Run benchmarks sequentially

  • func (b *B) RunParallel(body func(*PB)) – Run benchmarks concurrently

  • func (c *B) Skip(args ...interface{}) – Skip benchmark and log

  • func (c *B) SkipNow() – Skip and stop now

  • func (c *B) Skipf(format string, args ...interface{}) – Skip benchmark and formatted log

  • func (c *B) Skipped() bool – Test skipped

  • func (b *B) StartTimer() – Start timing

  • func (b *B) StopTimer() – Stop timing

  • func (c *B) TempDir() string – Get temp directory

Type BenchmarkResult
  • func Benchmark(f func(b *B)) BenchmarkResult – Benchmark f

  • func (r BenchmarkResult) AllocedBytesPerOp() int64 – Get info

  • func (r BenchmarkResult) AllocsPerOp() int64 – Get info

  • func (r BenchmarkResult) MemString() string – Get info

  • func (r BenchmarkResult) NsPerOp() int64 – Get info

The T (test) type has these functions. Many are the same as for the B type and not redescribed:
  • func (c *T) Cleanup(f func())

  • func (t *T) Deadline() (deadline time.Time, ok bool) – Get test deadline

  • func (c *T) Error(args ...interface{})

  • func (c *T) Errorf(format string, args ...interface{})

  • func (c *T) Fail()

  • func (c *T) FailNow()

  • func (c *T) Failed() bool

  • func (c *T) Fatal(args ...interface{})

  • func (c *T) Fatalf(format string, args ...interface{})

  • func (c *T) Helper() – Marks a caller as a helper; it is not included in reports

  • func (c *T) Log(args ...interface{})

  • func (c *T) Logf(format string, args ...interface{})

  • func (c *T) Name() string

  • func (t *T) Parallel() – Set to run a test in parallel with other tests

  • func (t *T) Run(name string, f func(t *T)) bool

  • func (c *T) Skip(args ...interface{})

  • func (c *T) SkipNow()

  • func (c *T) Skipf(format string, args ...interface{})

  • func (c *T) Skipped() bool

  • func (c *T) TempDir() string

Time and Date Package

The time package provides functions to display and manipulate dates, times, and durations. It has one sub-package:
  • tzdata provides support for time zones without reliance on operating system support.

The time package has these time formats (really templates – actual values are formatted to look like the template) built-in:
  • ANSIC = "Mon Jan _2 15:04:05 2006"

  • UnixDate = "Mon Jan _2 15:04:05 MST 2006"

  • RubyDate = "Mon Jan 02 15:04:05 -0700 2006"

  • RFC822 = "02 Jan 06 15:04 MST"

  • RFC822Z = "02 Jan 06 15:04 -0700"

  • RFC850 = "Monday, 02-Jan-06 15:04:05 MST"

  • RFC1123 = "Mon, 02 Jan 2006 15:04:05 MST"

  • RFC1123Z = "Mon, 02 Jan 2006 15:04:05 -0700"

  • RFC3339 = "2006-01-02T15:04:05Z07:00"

  • RFC3339Nano = "2006-01-02T15:04:05.999999999Z07:00"

  • Kitchen = "3:04PM"

  • Stamp = "Jan _2 15:04:05"

  • StampMilli = "Jan _2 15:04:05.000"

  • StampMicro = "Jan _2 15:04:05.000000"

  • StampNano = "Jan _2 15:04:05.000000000"

Note the preceding patterns are not arbitrary. The values (like month name/abbreviation and day of month) are keywords. For example, one cannot use May instead of January or 03 instead of 02.

Time is measured in nanosecond precision, but the computer may not be able to measure time passing at this resolution, so steps in time may happen in multiple nanoseconds. The time package has these durations built-in:
  • Nanosecond Duration = 1

  • Microsecond = 1000 * Nanosecond

  • Millisecond = 1000 * Microsecond

  • Second = 1000 * Millisecond

  • Minute = 60 * Second

  • Hour = 60 * Minute

The time package has these functions and types:
  • func After(d Duration) <-chan Time – Returns a channel that fires after a duration

  • func Sleep(d Duration) – Sleeps/suspends caller goroutine for a duration

  • func Tick(d Duration) <-chan Time – Returns a channel that fires each duration

  • type Duration – Represents a span of time

  • type Location – Represents a time zone

  • type Month – Enum for month

  • type Ticker – Wraps a channel getting ticks; used to do actions repeatedly

  • type Time – Represents an instant in time with nanosecond resolution

  • type Timer – Fires a channel at periodic intervals

  • type Weekday – Enum for weekday

The Duration type has these methods (generally self-explanatory):
  • func ParseDuration(s string) (Duration, error)

  • func Since(t Time) Duration

  • func Until(t Time) Duration

  • func (d Duration) Hours() float64

  • func (d Duration) Microseconds() int64

  • func (d Duration) Milliseconds() int64

  • func (d Duration) Minutes() float64

  • func (d Duration) Nanoseconds() int64

  • func (d Duration) Round(m Duration) Duration

  • func (d Duration) Seconds() float64

  • func (d Duration) Truncate(m Duration) Duration

Type Location
  • func FixedZone(name string, offset int) *Location

  • func LoadLocation(name string) (*Location, error)

Type Ticker
  • func NewTicker(d Duration) *Ticker – Make and start a ticker

  • func (t *Ticker) Reset(d Duration) – Change interval

  • func (t *Ticker) Stop()

Type Time
  • func Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time

  • func Now() Time

  • func Parse(layout, value string) (Time, error)

  • func ParseInLocation(layout, value string, loc *Location) (Time, error)

  • func Unix(sec int64, nsec int64) Time – Time from Epoch

  • func (t Time) Add(d Duration) Time

  • func (t Time) AddDate(years int, months int, days int) Time

  • func (t Time) After(u Time) bool

  • func (t Time) Before(u Time) bool

  • func (t Time) Clock() (hour, min, sec int)

  • func (t Time) Date() (year int, month Month, day int)

  • func (t Time) Day() int

  • func (t Time) Equal(u Time) bool

  • func (t Time) Format(layout string) string

  • func (t Time) Hour() int

  • func (t Time) ISOWeek() (year, week int)

  • func (t Time) In(loc *Location) Time

  • func (t Time) IsZero() bool

  • func (t Time) Local() Time

  • func (t Time) Location() *Location

  • func (t Time) Minute() int

  • func (t Time) Month() Month

  • func (t Time) Nanosecond() int

  • func (t Time) Round(d Duration) Time

  • func (t Time) Second() int

  • func (t Time) Sub(u Time) Duration

  • func (t Time) Truncate(d Duration) Time

  • func (t Time) UTC() Time

  • func (t Time) Unix() int64

  • func (t Time) UnixNano() int64 – Time since Epoch

  • func (t Time) Weekday() Weekday

  • func (t Time) Year() int

  • func (t Time) YearDay() int

  • func (t Time) Zone() (name string, offset int)

Type Timer
  • func AfterFunc(d Duration, f func()) *Timer – Call func in goroutine after duration

  • func NewTimer(d Duration) *Timer – Make a timer

  • func (t *Timer) Reset(d Duration) bool

  • func (t *Timer) Stop() bool

On Ticker vs. Timer, Tickers supply multiple events at periodic intervals, while a Timer supplies just one event. Both work on time durations. Consider this simple version of a clock that outputs the time of day every minute:
func startClock(minutes int) {
      minuteTicker := time.NewTicker(time.Minute)
      defer minuteTicker.Stop() // cleanup ticker when done
      fmt.Println("Clock running...")
      complete := make(chan bool) // notifies end
      go func() { // trigger the clock to stop eventually
            time.Sleep(time.Duration(minutes) * time.Minute)
            complete <- true
      }()
      count := 0
loop:
      for {
            select { // blocks while waiting for an event
            // ticker has a channel that fires every minute
            case tod := <-minuteTicker.C:
                  fmt.Println(tod.Format(time.RFC850))
                  count++
            case <-complete:
                  break loop
            }
      }
      fmt.Println("Clock stopped; final count:", count)
}
This is run by use of startClock(5) in some main with this result:
Clock running...
Friday, 07-May-21 14:16:07 PDT
Friday, 07-May-21 14:17:07 PDT
Friday, 07-May-21 14:18:07 PDT
Friday, 07-May-21 14:19:07 PDT
Friday, 07-May-21 14:20:07 PDT
Clock stopped; final count: 5
Contrast this with a timer that triggers a function when it is done. The timer is created with the Tick() function . The timer() function returns before the timeout:
func timer(seconds int, f func(t time.Time)) {
      ticks := time.Tick(time.Duration(seconds) * time.Second)
      go func() {
            // only iterates once as only one value sent before closure
            for t := range ticks {
                  f(t)
            }
      }()
}
This is driven by the following code in main:
var wg sync.WaitGroup
wg.Add(1)
start := time.Now()
fmt.Println("Running...")
timer(5, func(t time.Time) {
      defer wg.Done()
      trigger := time.Now()
      fmt.Println("Trigger difference:", trigger.Sub(start))
})
wg.Wait()
fmt.Println("Done")
That produces this output:
Running...
Trigger difference: 5.0041177s
Done
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.236.174