- Workspace structure
- Run & Build
- Dependencies
- Control statements
- Operators
- Variables
- Types
- Pointer
- Function
- Error
- Array, Slice
- Map
- String
- Math
- Async
- Date, Time
- OS, IO
- Testing
Further reading:
========================================================================================================================
cmd/ // Entry point(s), i.e., main.go importing and running internal/ and pkg/
configs/
bin/ // Generated executables
pkg/ // Packages for external use
api/ // OpenAPI/Swagger, JSON schema, etc
internal/ // Internal packages
github.com/user/somemodule/
main.go
somepackage/.../
...
.../
...
https://go.dev/doc/tutorial/workspaces
https://pkg.go.dev/cmd/go#hdr-Workspace_maintenance
Package
is a directory of .go files.
It's a basic unit of code organization and reuse.
Module
is a collection of packages.
Modules are defined by a go.mod
file in the root directory, which specifies the module path and its dependencies.
Create module:
go mod init <prefix>/<module_name>
will create go.mod file for tracking dependencies.
Any source file can contain init
function (both executable and library):
func init() {
}
It can be used to set up whatever state is required.
init
is called after all the variable declarations in the package have evaluated their initializers, and those are evaluated only after all the imported packages have been initialized.
Function main
in the "main" package is an entry point for executable.
Library doesn't have main function.
package main
func main() {
}
========================================================================================================================
Run:
go run file.go
or
go run .
https://pkg.go.dev/cmd/go#hdr-Compile_and_run_Go_program
Make executable binary:
go build
Build and install binary into GOBIN dir, so it can be launched by name from anywhere:
go install
========================================================================================================================
https://go.dev/doc/modules/managing-dependencies
https://go.dev/doc/modules/developing
import "module/path"
// or with alias:
import p "module/path"
Import by full path, but invoke by declared package name.
By convention package name is the last element of import path.
import
or const
statements can be grouped by parentheses:
import (
"one"
aliasname "two"
)
Package member which name starts with a lowercase letter is not available beyond the package.
Member is public (exported) if name is Uppercase.
Public members accessible from outside by package name:
import "somepackage"
var m = somepackage.Member
somepackage.SomeFunc()
but inside the same package they are accessible directly:
package somepackage
var m == Member
SomeFunc()
To install third party library:
go get some_lib_path
# Specific version:
go get some_lib_path@v1.2.3
including a remote, e.g. "github.com/user/my_library_"
To use local module instead of remote:
go mod edit -replace example.com/my_module=../my_module
https://go.dev/doc/modules/gomod-ref#replace
List dependencies and available versions:
go list -m -u all
or specific module:
go list -m -u example.com/theirmodule
go mod tidy
will update go.mod
file - add missing and remove unused deps.
https://pkg.go.dev/cmd/go#hdr-Add_missing_and_remove_unused_modules
To enable auto importing, use GoImports
instead of GoFmt
.
========================================================================================================================
for i := 0; i < n; i++ {
// ...
}
Variables from init statement are only available in the scope of this for
block.
Since v1.22 each iteration has its own copies of init variables.
E.g.:
var printN func()
for n := 0; n < 9; n++ {
if n == 2 {
printN = func() {
fmt.Println(n)
}
}
}
printN() // will print 2, not 9
Init and post statements are optional:
for i < n {
// as `while` cycle
}
for {
// infinite loop
}
for i, v := range someArray {
// iterate over array or slice
}
for k, v := range someMap {
// iterate over map
}
To break loop:
break
To break current iteration of loop:
contunue
if b < c {
// ...
} else if b < d {
// ...
} else {
// ...
}
Like for loop
, can have short init statement:
if i := b; i < c {
// ...
}
Variables from init statement are only accessible in the scope of current if-else
block.
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X")
case "freebsd", "openbsd":
fmt.Println("BSD")
default:
// plan9, windows, ...
fmt.Printf("%s\n", os)
}
Only the first matched case runs. break
is not needed.
Case conditions can be a dynamic expressions.
========================================================================================================================
In the order of precedence from hight to low:
Pistfix: ()
[]
->
.
++
--
Unary: +
-
!
~
++
--
(type)*
&
sizeof
Multiplicative: *
/
%
Additive: +
-
Shift: <<
>>
Relational: <
<=
>
>=
Equality: ==
!=
Bitwise AND: &
Bitwise XOR: ^
Bitwise OR: |
Logical AND: &&
Logical OR: ||
Assignment: =
+=
-=
*=
/=
%=
>>=
<<=
&=
^=
|=
Comma: ,
Expressions can be grouped by parenthesis to change order of evaluation.
Go uses short circuit evaluation:
if true && f() { // `f` will never be called
- Pointers: are equal when they both point to the same address.
- Channels: both created by the same call to
make
. - Structs: all corresponding fields are equal.
- Arrays: all corresponding elements are equal.
- Strings: all corresponding bytes are equal.
Functions, maps and slices are not comparable by ==
.
Use bytes.EqualFord
or reflect.DeepEqual
to compare them.
Diff
function from github.com/google/go-cmp/cmp
package can be used to get diff of two objects.
========================================================================================================================
var name Type
var name Type = value // declare and assign a value
var name = value // type inference
name := value // shorthand for declare and assign (available only inside function)
const name Type = value
Constants can be rune
, string
, bool
or numeric values.
Multiple declaration:
var a, b Type
Multiple assignment:
var zero, one = 1, "str"
var zero, one = SomeFunc(...)
To avoid variable declared and not used
error use blank identifier _
:
_ = unusedVariable
Raw string literal:
var noNeedToQuote = `'"\`
Variables declared without an explicit initial value are given their zero value:
0
for numeric typesfalse
for the boolean type""
(the empty string) for stringsnil
for other types
- Block - members declared within a code block, such as
if
statement or function.
Accessible whithin that block.
Including short declarations from init statements ofif
,for
,switch
. - Package - declared at top level of .go file.
Accessible by all files within that package. - Public/Global - package-scoped members, which names starts with an uppercase letter.
Exported. Can be imported and used by other packages.
Inner-scoped variable shadows the outer variable with the same name.
========================================================================================================================
bool
string
int, int8, int16, int32, int64
uint, uint8, uint16, uint32, uint64, uintptr
// When you need an integer value you should use `int` unless you have a specific reason to use a sized or unsigned integer.
float64, float32
// package "math" operates float64. Prefer `float64` unless you have a specific reason to use float32.
rune // alias for int32, represents character code
byte // alias for uint8
complex64, complex128
nil // zero value for slice, map, channel, interface, func, pointer
any // alias for empty interface `interface{}`
Assignment between items of different type requires an explicit conversion.
Type(value) // Try to convert a value to type `Type`
e.g.:
var floatval = float32(123)
A type assertion provides access to an interface value's underlying concrete type.
interfaceValue.(ConcreteType) // for interface variables
e.g.:
var v interface{} = ...
var intval = v.(int) // panic when `v` is not int
var intval, ok = v.(int) // without error but `intval` will have nil value and `ok == false` when `v` is not int
Type switch permits several type assertions in series:
switch v := value.(type) { // `type` is keyword here
case int:
// here `v` is of type `int`
default:
// here `v` has same type as `value`
}
An interface type is defined as a set of method signatures.
A value of interface type can hold any value that implements those methods.
Define:
type IType interface {
Method()
IAnother // Embedded interface
// ...
}
Type implements an interface by implementing its methods,
so an empty interface may hold values of any type, e.g.:
func Func(anyType interface{}) {
// this function accepts any type
}
Interface values can be thought of as a tuple of a value and a concrete type.
Calling a method on an interface value executes the method of the same name on its underlying type.
If the concrete value inside the interface itself is nil
, the method will be called with a nil receiver.
Interface value that holds a nil
concrete value is itself non-nil:
type I interface {
M()
}
type T struct {
}
func (t *T) M() {
}
var i I // nil interface
var t *T
i = t // non-nil interface but nil concrete type
fmt.Printf("(%v, %T)\n", i, i)
// (<nil>, *main.T)
Is a collection of fields.
Define:
type Type struct {
f1 int
f2 int
*Another // Embedded struct
// ...
}
Instantiate:
var st = Type {
f1: 123
}
Can be created with a subset of fields.
Anonymous struct can be defined and instantiated by single expression, e.g.:
var unnamedStruct = struct{
f1 int
}{
f1: 123
}
Access fields via dot:
st.Another = &another
Instantiate and get pointer:
var stPtr = &Type { f2: value, Another: &another }
Instantiate by new
:
var stPtr = new(Type)
It allocates zeroed storage for a new item of type Type
and returns its address, a value of type *Type
.
Struct embedding creates a form of inheritance or composition.
It gives direct access to embedded struct's fields and methods. Own fields and methods takes precedence.
E.g.:
type A struct {
F string
a string
}
func (a *A) M() {}
type B struct {
A
F int
}
func (b *B) M() {}
var b = B{
F: 1,
A: A{
F: "AF",
a: "a",
},
}
b.M() // B's method
b.F // "1", because A.F is hidden by B.F
b.a // "a", direct access to A's field
b.A.F // "AF", indirect to A's hidden field
Type parameters allows function to accept arguments of multiple types, e.g.:
func GenericF[V int | float64](val V) {
switch v := any(val).(type) {
case int:
fmt.Println("int", v)
case float64:
fmt.Println("float", v)
}
}
GenericF[int](123)
// type argument can be omitted when compiler can infer the type:
GenericF(123)
Type parameters also applicable to type definitions, e.g.:
type List[T any] struct {
// Singly-linked list that holds values of any type
next *List[T]
val T
}
var head = List[string]{ val: "A" }
head.next = &List[string]{ val: "B" }
Type constraints can be declared as interface, e.g.:
type Number64 interface {
int64 | float64
}
Package "reflect"
implements runtime reflection.
Typical use is to take a value with static type interface{}
and extract its dynamic type information by calling TypeOf
, which returns a reflect.Type
.
TypeOf(v any) Type
ValueOf(v any) Value
DeepEqual(a, b any) bool
// etc.
Type // represents type info: name, size, fields, etc.
.In(i int) Type // i`th func parameter
.MethodByName(name string) Method
.AssignabeTo(t Type) bool
// etc.
Value // represents value of variable
.Set(v Value)
.Equal(v Value) bool
.Len() int
.Cap() int
.IsNil() bool
// etc.
========================================================================================================================
A pointer holds the memory address of a value.
*Type
e.g.:
var p *int
Create pointer:
p = &value
Access value by pointer:
*p = newvalue
// now p value equals newvalue
It is a dereferencing or indirecting.
Indirection for accessing fields and methods of named type is transparent:
var sp = &Struct{}
sp.field = value
// explicit dereference `(*sp)` is not needed
Everything in Go is passed by value.
Even pointers assigned the value of the memory address. So they are values too.
E.g., passing a struct into a function will create a local copy.
However, there are six types that actually hold pointer values, and using a pointer to these types (i.e., a pointer to a pointer) is not efficient: pointer, slice, map, channel, interface, function.
========================================================================================================================
func Some(param Type, ...) ReturnType {
// ...
}
Function can return multiple values:
func Some(param Type, ...) (RetType, RetType1) {
// ...
return zero, one
}
Returning values can be named:
func Some(param Type, ...) (named RetType) {
// ...
return named
}
Function is also a value and can be returned, assigned to variable or passed as argument.
var f = func() {
// ...
}
f()
Closure:
func parent() func() int {
var closured = 1;
return func() int {
closured++;
return closured;
}
}
defer
statement inside of a function defers a call until a surrounding function returns.
func() string {
defer anotherFunc() // will be invoked after some_code
// even if some_code will panic
// some_code...
return res
}
Arguments of the deferred call are evaluated immediately.
Deferred calls are pushed onto stack and executes in LIFO order.
Useful for some clean-up actions.
Go does not have classes. However, you can define methods on types.
Method is a function with a special receiver
argument.
func (v Type) Abs() ReturnType {
// v is always a local copy here
}
Method can be declared with a receiver whose type is defined in the same package only.
Method also can be declared with a pointer receiver:
var s = SomeStruct{}
func (s *SomeStruct) setV(v string) {
s.v = v // changes the original `s` struct, not its local copy
}
s.setV("")
// Don't need to create pointer explicitly because of indirection.
// It interprets as `(&s).setV("")`.
Function with value parameter must take a value argument.
Method with value receiver can take either value or pointer receiver which interprets as *p
implicitly.
========================================================================================================================
Error
is an interface which value are functions often returns as a second value.
nil
indicates success.
e.g.:
var i, err = strconv.Atoi("42")
if err != nil {
fmt.Printf("couldn't convert number: %v\n", err)
return
}
fmt.Println("Converted integer:", i)
To create own error type need to implement Error
interface:
type MyError float32
func (e MyError) Error() string {
return fmt.Sprint("incorrect value: ", float32(e))
}
Or instantiate Error object:
errors.New(err any) errors.Error
e.g:
import (
"errors"
"fmt"
"log"
)
func Hello(name string) (string, error) {
if name == "" {
err := errors.New("empty name")
log.Fatal(err)
return "", err
}
message := fmt.Sprintf("Hi, %v. Welcome!", name)
return message, nil
}
panic(error any)
But all defer
ed functions will be invoked before return.
recover() any
Calling it in defer
ed function will capture the value given to panic
and resume normal execution.
https://go.dev/doc/effective_go#recover
========================================================================================================================
https://go.dev/blog/slices-intro
[n]Type // array of n elements of type `Type`
[]Type // slice (reference to array range)
Array (fixed length):
var array [n]Type
Slice (dynamic size):
var slice = []Type{ val1, ... } // allocate array of specified values and associated slice
var slice = make([]Type, len) []Type // allocate a nil-filled array of specified length and return it's slice
In fact, slice does not store any data, it just describes a section of an underlying array.
Slice of an array:
var slice = array[indexFrom:indexTo] // points to same array in range of [from, to-1]
from
or to
bound can be omitted to get from start or to end of an array.
E.g.:
var tail = []arr{1, 2, 3}[1:] // 2, 3
By index:
array[index] = val
Size:
len(arr []Type) int // length
cap(arr []Type) int // capacity - length of underlying array
nil
slice has both length and capacity of 0.
append(slice []Type, values ...Type) []Type // append values and return a new slice
append()
does not change the original slice but may change the original underlying array.
When capacity is not enough, it allocates a new array using make()
under the hood.
Therefore, the new slice may refer to a new underlying array after append
.
To append a slice to slice (concat) use v...
syntax to interpret slice as list of args:
slice1 = append(slice1, slice2...)
From slice to slice:
copy(dst []Type, src []Type) int
The source and destination may overlap.
It copies only the smaller number of elements when len(src) != len(dst).
Array cannot be released by GC while any slice refers it.
When only a small piece of underlying array is needed, allocate a new array and copy slice to prevent memory leaks:
c := make([]byte, len(slice))
copy(c, slice)
========================================================================================================================
map[KeyType]ValueType
Key can be an integer, float, string, pointer, interface, struct or array.
var m = map[KeyType]ValueType { key: value, ... } // allocate map of specified key-values
var m = make(map[KeyType]ValueType) // allocate empty map
m[key] = value
var value = m[key]
var value, isExist = m[key]
delete(m map, key Type)
========================================================================================================================
String is a readonly slice of runes.
Strings can be concatenated with +
and +=
operators.
Comparable by <
, >=
, ==
, !=
(the underlying bytes will be compared).
Can be resliced or iterated like a normal slice.
E.g.:
str := "test"
firstByte := str[0] // 116
substr := str[1:] // "est"
conc := "R" + substr // "Rest"
Convert from/to bytes:
var cl = []byte(str)
var str = string(bytes)
Default string representation:
fmt.Sprintf("%v", obj)
For a specific representation implement Stringer
interface:
type Type ...
func (st *Type) String() string {
return "some custom representation"
}
Contains(str, substr string) bool
Count(str, substr string) int
Index(str, substr string) int
LastIndex(str, substr string) int
HasPrefix(str, prefix string) bool
Compare(a, b string) int
Join(sl []string, separ string) string
Split(str, separator string) string
Replace(str, old, new string, limit int) string
ToUpper(str string) string
Trim(str, cutset string) string
NewReader(str string) *strings.Reader
Sprintf(format string, values ...any) string // Format and return
Printf(format string, values ...any) int, error // Format and print to stdout.
// Returns number of bytes written.
Println(values ...any) int, error // With newline
Format string examples:
"pointer: %p"
"value: %v"
"in full syntax: %#v"
"type: %T"
"decimal: %d"
"hex: %x"
"string: %s"
"quoted: %q"
Read stdin:
Scan(a ...any) int, error // Read stdin and put into `a...`.
// Returns count of space-separated tokens readen.
Scanln(a ...any) int, error // same but stops at newline
ParseInt(s string, base int, bitSize int) int64, error
// bitSize: 0, 8, 16, 32, 64 for int, int8, int16, int32, int64 appropriately
ParseFloat(s string, bitSize int) float64, error
ParseBool(s string) bool, error
FormatInt(n int64, base int) string
FormatFloat(n float64, fmt byte, prec int, bitSize int) string
// fmt: 'f', 'e', 'E'
// e.g. `strconv.FormatFloat(v, 'f', -1, 32)`
FormatBool(b bool) string
Atoi(s string) int, error // shorthand for `ParseInt(s, 10, 0)`
Itoa(n int) string // shorthand for `FormatInt(int64(n), 10)`
========================================================================================================================
IsNaN(x float64) bool
NaN() float64
Abs(x float64) float64
Ceil(x float64) float64
Floor(x float64) float64
Max(a, b float64) float64
Min(a, b float64) float64
Mod(a, b float64) float64
Pow(a, b float64) float64
Exp(x float64) float64
Log(x float64) float64
Log2(x float64) float64
Cos(rad float64) float64
Sin(rad float64) float64
Tan(rad float64) float64
Sqrt(x float64) float64
Cbrt(x float64) float64
NaN can not be represented as integer, so:
int(math.NaN()) // 0
float64(math.NaN()) // NaN
Min
/Max
with NaN
always returns NaN.
Comparing NaN
to a number always results in false.
Octal number can be defined by leading 0o
:
var perm = 0o644 // int 420
Float32() float32 // [0.0, 1.0)
Float64() float64 // [0.0, 1.0)
Int() int
Intn(n int) int // [0, n)
========================================================================================================================
https://go.dev/doc/effective_go#concurrency
Goroutine is a lightweight thread.
Goroutines run in the same address space, so access to shared memory must be synchronized.
Run function in a new goroutine:
go someFunc(arg, ...)
Arguments will be evaluated in the current thread, not in the new goroutine.
Channel is a typed conduit through which values can be sent between goroutines.
By default, sends and receives block until other side is ready.
Buffered channel blocks only when empty (blocks receiver) or full (blocks sender).
chan Type // read|write
<-chan Type // read only
chan<- Type // write only
Create:
var ch = make(chan Type)
var ch = make(chan Type, len) // Buffered
Send:
ch<- value
Receive:
var v = <-ch
var v, ok = <-ch // `ok` will be false if channel closed by sender
Sender (only sender) can close the channel to signalize that no data will be send anymore:
close(ch)
For example, to terminate a for...range
loop.
To receive/send values from/to multiple channels use select/case
.
E.g.:
tick := time.Tick(100 * time.Millisecond)
boom := time.After(500 * time.Millisecond)
for {
select {
case <-tick:
fmt.Println("tick.")
case <-boom:
fmt.Println("BOOM!")
return
default:
fmt.Println(".")
time.Sleep(50 * time.Millisecond)
}
}
If default
not specified, it blocks until one of the channels can read/write.
It chooses randomly if multiple are ready.
To repeatedly receive available values from the channel use for...range
.
for v := range ch {
// ...
}
It will iterate until channel is closed.
Provides basic synchronization primitives such as mutual exclusion locks.
Other than the Once and WaitGroup types, most are intended for use by low-level library routines. Higher-level synchronization is better done via channels and communication.
Values containing the types defined in this package should not be copied.
https://pkg.go.dev/sync
To safely invoke a function only once, even if called concurrently multiple times.
OnceFunc(f func()) func() // returns a function that invokes f only once
OnceValue[T any](f func() T) func() T // the same with returning value
OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2)
Mutual exclusion locker.
To make a code block to be executed in mutual exclusion,
surround it with calls to Lock
and Unlock
methods of sync.Mutex
.
var m sync.Mutex
// no need to initialize, zero value is ready to use
Methods:
Lock()
will block current goroutine if mutex already locked.
Unlock()
will unblock, also from any other goroutine. Throws error if not locked.
WaitGroup waits for a collection of goroutines to finish.
When the counter becomes zero, all goroutines blocked on Wait
are released.
var wg sync.WaitGroup
Methods:
Add(delta int) // increase/decrease counter
Done() // decrease counter by 1
Wait() // blocks until the counter is zero
E.g.:
import (
"sync"
)
type Container struct {
mu sync.Mutex
counters map[string]int
}
func (c *Container) inc(name string) {
c.mu.Lock()
defer c.mu.Unlock()
c.counters[name]++
}
func main() {
c := Container{
counters: map[string]int{"a": 0, "b": 0},
}
var wg sync.WaitGroup
doIncrement := func(name string, n int) {
for i := 0; i < n; i++ {
c.inc(name)
}
wg.Done()
}
wg.Add(3)
go doIncrement("a", 10000)
go doIncrement("a", 10000)
go doIncrement("b", 10000)
wg.Wait()
fmt.Println(c.counters)
}
Context allows to execute goroutines in some context.
For carrying deadlines, cancelation signals, and other task-scoped values.
https://pkg.go.dev/context
Functions:
Background() Context // create an empty context
// Should be used at top level
TODO() Context // empty context too
// Should be used when it's unclear which Context to use or it is not yet available
WithValue(parent Context, key, val any) Context // create a copy of parent in which the value associated with `key` is `val`
// The `key` must be comparable and should not be of built-in type to avoid collisions between packages
WithCancel(parent Context) Context, CancelFunc // copy of parent with a new Done channel,
// which is closed when the CancelFunc is called or when the parent context's Done channel is closed (which first)
WithCancelCause(parent Context) Context, CancelCauseFunc // same but cancelation func accepts an error,
// which can be retrieved by `Cause(ctx)`
WithDeadline(parent Context, d time.Time) Context, CancelFunc // same with a timeout
WithTimeout(parent Context, d time.Duration) Context, CancelFunc // same with a duration
Cause(ctx Context) // returns a cause of the ctx cancelation,
// nil if not yet canceled
Methods:
Value(key any) any // returns the value associated with this context for `key`
// nil if no value is assotiated
Done() <-chan // returns the channel which is closed when context or its parent is closed
Err() error // error explaining why this context is closed:
// `Canceled` if the context was canceled, `DeadlineExceeded` if the context's deadline passed,
// `nil` if not yet closed
Deadline() time.Time, ok bool // time of deadline
// ok == false if no deadline is set
E.g., timeout:
import (
"context"
"time"
"math/rand"
)
func main () {
var result = make(chan string)
var ctx, cancel = context.WithTimeout(context.Background(), 5 * time.Second)
defer cancel()
go doLongOperation(result)
select {
case <- ctx.Done():
fmt.Println("Timeout reached")
case r := <- result:
fmt.Println(r)
}
}
func doLongOperation(result chan string) {
time.Sleep(time.Duration(rand.Intn(10)) * time.Second)
result <- "Completed"
}
E.g., cancelable request:
import (
"context"
"net/http"
)
func main() {
var ctx, cancel = context.WithCancelCause(context.Background())
go doRequest(ctx, url, response)
// ...
cancel(errors.New("operation is canceled"))
}
func doRequest(ctx context.Context, url string, ch chan Response) {
var req, err = http.NewRequestWithContext(
ctx,
"GET",
url,
nil,
)
if err != nil {
fmt.Println("Error creating request:", err)
ch <- Response{ Url: url, Status: "Failed", Err: err }
}
res, err := http.DefaultClient.Do(req)
if err != nil || res.StatusCode != 200 {
var status = "Failed"
if ctx.Err() == context.Canceled {
status = "Canceled"
}
ch <- Response{ Url: url, Status: status, Err: err }
} else {
ch <- Response{ Url: url, Status: "OK" }
}
}
E.g., nesting a value:
type CtxKey string
ctxRu := context.WithValue(ctx, CtxKey("lang"), "ru")
ctxCounter := context.WithValue(ctxRu, CtxKey("counter"), 2)
ctxRu.Value(CtxKey("lang")) // ru
ctxRu.Value(CtxKey("counter")) // nil
ctxCounter.Value(CtxKey("lang")) // ru
ctxCounter.Value(CtxKey("counter")) // 2
========================================================================================================================
Constants:
Nanosecond
Hour
Wednesday
April
RFC3339 // layout for ISO 8601 datetime format "2006-01-02T15:04:05Z07:00"
// etc.
Functions:
Now() Time
Date(year int, month Month, day, hour, min, sec, nsec int, loc *Location) Time
Since(t Time) Duration
Unix(sec int64) Time
Parse(layout string, value string) Time, error
ParseDuration(str string) Duration, error // e.g. "300ms", "-1.5h", "2h45m"
LoadLocation(name string) *Location, error // e.g. "", "UTC", "Europe/Moscow"
Sleep(delay Duration) // pause current goroutine for delay
After(delay Duration) <-chan Time // message to channel will be sent after delay
AfterFunc(delay Duration, f func()) *Timer // run func after delay
// func will be invoked in a new goroutine.
NewTicker(interval Duration) *Ticker
NewTimer(d Duration) *Timer
Represents an instant in time with nanosecond precision.
Methods:
Year() int
Clock() int, int, int // hours, minues, seconds
Unix() int64
UnixMilli() int64
Add(d Duration) Time
// etc.
Format(layout string) string
// `layout` is a representation of the time stamp
// "Jan 2 15:04:05 2006 MST"
// An easy way to remember this value is that it holds, when presented in this order, the values:
// 1 2 3 4 5 6 -7
Built-in type for setting the time amount.
Methods:
Abs() Duration // unsigned duration
Truncate(m Duration) Duration // round toward zero to a multiple of m
String() string // e.g. "72h3m0.5s"
Hours() float64
Nanoseconds() float64
// etc.
Provides a channel that delivers Time
ticks.
C <-chan Time
Methods:
Reset(d Duration)
Stop()
Provides a channel that deliver Time
once after dalay.
C <-chan Time
Methods:
Reset(d Duration) bool
Stop() bool
// returns false if already stopped or expired
========================================================================================================================
Stop program execution:
Exit(code int)
Std in/out:
Stdin *File
Stdout *File
Getenv(key string) string, error
Setenv(key, value string) error
Unsetenv(key string) error
Getwd() path string, e error // get current working directory
Chdir(path string) error
Mkdir(path string, perm FileMode) error
MkdirAll(path string, mode FileMode) error
Rename(oldpath, newpath string) error
Remove(path string) error
RemoveAll(path string) error
Create(path string) *File, error // if the file already exists, it is truncated
// Assigns `644` permissions.
Open(path string) *File, error // readonly
OpenFile(path string, flag int, perm FileMode) *File, error
ReadDir(path string) []os.DirInfo, error
ReadFile(path string) []byte, error
WriteFile(path string, data []byte, perm os.FileMode) error
// where `flag` is a bitmask of:
O_CREATE
O_APPEND
O_TRUNC
O_RDONLY
O_WRONLY
O_RDWR
E.g.:
var _, err = os.OpenFile("name", os.O_RDWR|os.O_CREATE, 0o644)
// Will create file if does not exist and
// open for reading or writing.
Close() error
Chmod(mode FileMode) error
Name() string // full path
Stat() FileInfo, error
Readdir(limit int) []FileInfo, error
Readdirnames(limit int) []string, error
Read(buf []byte) int, error
ReadAt(buf []byte, offset int64) int, error
Write(data []byte) int, error
WriteAt(data []byte, offset int64) int, error
WriteString(str string) int, error
// they are returns n readen/written bytes
Name() string // basename
Size() int64
IsDir() bool
Mode() FileMode
ModeTime() time.Time
User, Group, Others
U - number 0-7 - permissions for owner
G - number 0-7 - permissions for group
O - number 0-7 - permissions for others
where:
0 - forbidden
4 - read
5 - read and execute
6 - read and write
7 - read (4) + write (2) + execute (1)
E.g.:
var mode = os.FileMode(0o755)
// if directory: owner can list and create files/subdirs, anyone can list;
// if file: owner can read, write and execute, anyone can read and execute;
Reader:
type Reader interface {
Read(buf []byte) (n int, e error)
// Populates data slice to `buf` and returns number of readen bytes
// with error (`io.EOF` on end of file).
}
Writer:
type Writer interface {
Write(data []byte) (n int, e error)
}
Standart library contains many implementations of Reader
and Writer
for network connections, files, ciphers, etc.
Copy(dst Writer, src Reader) int64, error
CopyBuffer(dst Writer, src Reader, buf []byte) int64, error // copy through buffer
CopyN(dst Writer, src Reader, n int64) int64, error // copy n bytes
ReadFull(r Reader, buf []byte) int, error // read exactly len(buf) bytes
ReadAll(r io.Reader) []byte, error // read until EOF
WriteString(w Writer, s string) int, error
// returns n readen/written bytes
For paths separated by forward slashes, such as the paths in URLs.
Base(path string) string // get last element of path
Clean(path string) string // get shorter equivalent
Dir(path string) string
Ext(path string) string
IsAbs(path string) bool
Join(patrs ...string) string
Split(path string) dir, filename string
For filename paths in a way compatible with the target operating system-defined file paths.
Base(path string) string // get last element of path
Clean(path string) string // shorter equivalent
Abs(path string) string, error // absolute path
IsAbs(path string) bool
Rel(basepath, targpath string) string, error
Dir(path string) string
Ext(path string) string
Join(parts ...string) string
Split(path string) dir, filename string
EvalSymlinks(path string) string, error // resolve symlinks into real path
Walk(path string, func(path string, fi FileInfo, e error) error) // recursively walk on dir contents
For parsing command line flags.
Int(name string, def int, descr string) *int
Bool(name string, def bool, descr string) *bool
Float64(name string, def float64, descr string) *float64
String(name string, def string, descr string) *string
Set(name, value string) error
Parse()
Parsed() bool
E.g.:
var num = flag.Int("num", 0, "Some num")
flag.Parse()
fmt.Println("-num value:", *num)
========================================================================================================================
To test function SomeFunc(...)
from file.go
need to create function TestSomeFunc(t *testing.T)
in file_test.go
.
When test failed need to invoke t.Error
, t.Fatal
, tFatalf
, etc. from this test function to indicate failure.
t.Fail() // Mark test failed but continues execution
t.FailNow() // Mark failed and call GoExit
t.Fatal(args ...any) // Equivalent to Log and FailNow
t.Fatalf(format string, args ...any) // same but with format
t.Error(args ...any) // Equivalent to Log and Fail
Example:
package abc
import (
"regexp"
"testing"
)
func TestAbc(t *testing.T) {
v := GetAbc()
r, err := regexp.Compile(`\babc\b`)
if !r.MatchString(v) || err != nil {
t.Fatal(v, err)
}
}
Run tests:
go test
will execute Test* functions in *_test.go files.
-v
- flag for verbose output.
https://github.com/uber-go/mock
Install:
go install go.uber.org/mock/mockgen@latest
go get go.uber.org/mock/mockgen/model
Add generate instruction to the source file:
//go:generate mockgen -source=./<file>.go -destination=./mocks/<file>.go -package=<package>_mocks
// source mode: generate mocks from all interfaces in file.
// or:
//go:generate mockgen -destination=./mocks/<file>.go -package=<package>_mocks <path/to/package> <Interface>,...
// package mode: generate mocks from specified interfaces only.
Then run:
go generate -x ./...
it recursively executes //go:generate ...
instructions in project.
Then use, e.g.:
package gym
import (
context "context"
"errors"
"testing"
"github.com/stretchr/testify/assert"
auth_mocks "gitlab.ozon.ru/klopyrev/onboarding-klopyrev/internal/auth/mocks"
s3_mocks "gitlab.ozon.ru/klopyrev/onboarding-klopyrev/internal/s3/mocks"
"gitlab.ozon.ru/klopyrev/onboarding-klopyrev/internal/pb/gitlab.ozon.ru/klopyrev/onboarding-klopyrev/api/gym"
"go.uber.org/mock/gomock"
)
func Test_BossOfThisGym(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
defer ctrl.Finish()
t.Run("Responds with `Unauthenticated` when token is invalid", func(t *testing.T) {
t.Parallel()
ctx := context.Background()
authMock := auth_mocks.NewMockIAuthProvider(ctrl)
s3Mock := s3_mocks.NewMockIS3Provider(ctrl)
service := New(authMock, s3Mock)[0]
i, _ := service.(*Implementation)
authMock.EXPECT().GetUserFromToken(gomock.Any()).Return(nil, errors.New("has no token"))
res, err := i.WhoIsTheBossOfThisGym(ctx, &gym.WhoIsTheBossOfThisGymRequest{
Action: "kick",
})
assert.Nil(t, res)
assert.NotNil(t, err)
assert.ErrorContains(t, err, "code = Unauthenticated")
})
}