go-basic
overview
Go is a statically typed, compiled
programming language designed at Google, Go is syntactically similar to C
, but with memory safety, garbage collection, structural typing, concurrency.
Go is influenced by C, but with an emphasis on greater simplicity and safety
.
A syntax and environment adopting patterns more common in dynamic languages:
- Optional concise variable declaration and initialization through type inference (x := 0 instead of int x = 0; or var x = 0;).
- Fast compilation.
- Remote package management (go get) and online package documentation.
Distinctive approaches to particular problems:
- Built-in concurrency primitives: light-weight processes (goroutines), channels, and the select statement.
- An interface system in place of virtual inheritance, and
type embedding
instead of non-virtual inheritance. - A toolchain that,
by default, produces statically linked native binaries without external dependencies
.
Syntax
Go’s syntax includes changes from C aimed at keeping code concise and readable
. A combined declaration/initialization operator
was introduced that allows the programmer to write i := 3 or s := “Hello, world!”, without specifying the types of variables used
. This contrasts with C's int i = 3; and const char *s = "Hello, world!"
; Semicolons(;) still terminate statements,but are implicit when the end of a line occurs. Methods may return multiple values, and returning a result, err pair is the conventional way a method indicates an error to its caller in Go. Go adds literal syntaxes for initializing struct parameters by name
and for initializing maps and slices. As an alternative to C’s three-statement for loop, Go’s range
expressions allow concise iteration over arrays, slices, strings, maps, and channels
.
Built-in Types
- bool
- string
- int int8 int16 int32 int64
- uint uint8 uint16 uint32 uint64 uintptr
- byte // alias for uint8, used as char like c,
'a' + 1 is valid it's 'b'
- rune // alias for int32 represents a Unicode code point
- float32 float64
- complex64 complex128
- pointer
The int, uint types are usually 32 bits wide on 32-bit systems and 64 bits wide on 64-bit systems, it depends on arch, different like C, int, uint are 32 bits(4 bytes) even on 64-bit machine
Custom type
1 | // status and bool are two different types. |
NOT Supported
- inheritance
- assertions
- pointer arithmetic
- implicit type conversions
- NO ~x but ^x in Go for integer.
Tools
The main Go distribution includes tools for building, testing, and analyzing code:
go build
, which builds Go binaries using only information in the source files themselves, no separate makefilesgo test
, for unit testing and microbenchmarksgo fmt
, for formatting codego get
, for retrieving and installing remote packagesgo vet
, a static analyzer looking for potential errors in codego run
, a shortcut for building and executing code, but not save binary to disk.godoc
, for displaying documentation or serving it via HTTP
An ecosystem of third-party tools adds to the standard distribution, such as gocode
, which enables code autocompletion in many text editors, goimports
, which automatically adds/removes package imports as needed, anderrcheck
, which detects code that might unintentionally ignore errors.
single quote vs double quote They are different
To declare either byte or rune we use single quote
. While declaring byte we have to specify the type
. If we do not specify the type, then the default type is meant as a rune for 'a'
. A single quote will allow only one character.
1 | func test() { |
NO -> for pointer type like what we did in C, but works as C like &a, *p, **p, *p=
1 | var a int = 12 |
string, slice, map behave like pointer, but when assigning and passing as parameter, array, struct are different.
1 | import "fmt" |
for/range
when loop array, slice, map, it’s copy of element, hence if change on that element, make sure use s[i]
if element is not pointer.
1 | type Person struct { |
naming convention
Go is to use MixedCaps or mixedCaps rather than underscores to write multiword names
.
Files
- Go follows a convention where source
files are all lower case with underscore separating multiple words
, client_log.go - Compound file names are separated with
_
- Files with the suffix
_test.go
are only compiled and run by the go test tool.
Functions and Methods
- Use camel case,
exported functions should start with uppercase
- If a name consists of multiple words, each word after the first should be capitalized like this: empName, EmpAddress, etc.
- function names are case-sensitive (car, Car and CAR are three different variables).
Constants
- Constant should be capitalized(camel case like Exported named). WorldStdEncoding
Variables
- shouldn’t include the name of your type in the name of your variable’s name,
tetMap
- Generally, use relatively simple (short) name(lower case), camel case(NOT _ underscore for multiple worlds) long var.
- user to u
- userID to uid
- serverListener
- lpcfg
- If variable type is bool, its name should start with Has, Is, Can or Allow, etc.
- Single letter represents index: i, j, k
struct interface
- Name of struct or interface
should be capitalized and camel case
type BJSchool struct{} method of interface and struct
should be capitalized(exported) and camel case type School interface{ Name() string }field of struct
should be low letter starts camel case if not exported, otherwise uppercase type School struct { regStudent int }
import package
- package name should be lowercase, no camel case. like import xxx/testhello
Non-exported struct fields can be accessed only in the same package, can not be accessed by other package.
printing
Package fmt
implements formatted I/O with functions analogous to C’s printf and scanf. The format ‘verbs’ are derived from C’s but are simpler.
In Golang we can use Printf with a special format code
. This determines how a string or integer is formatted. Println does not require a format string
.
- Printf: Must provide the format and
support explicit argument indexes, no auto newline
. - Println: No special format support, auto newline for each output, auto space between arguments, just use it’s default.
- Print: Print does not insert a newline after each call and no auto space between arguments, it just writes the data to the console with no trailing newline, except this, same as Println.
Above three prints to console while Sxx returns the formated result.
- Sprintf: Must provide the format and
support explicit argument indexes, No auto newline
. - Sprintln:
No special format support, auto newline
, just use it’s default. - Sprint: Print does not insert a newline after each call, it just writes the data to the console with no trailing newline, except this, same as Sprintln.
Above three prints returns the formated result, while Fxx
writes data to io.Writer
- Fprintf: Must provide the format and
support explicit argument indexes, No auto newline
. - Fprintln:
No special format support, auto newline
, just use it’s default. - Fprint: Print does not insert a newline after each call, it just writes the data to the file with no trailing newline, except this, same as Fprintln.
explicit argument index
1 | a := 10 |
default format of each type
1 | %c print for byte and rune only |
print in multiple lines
1 | s := `hello |
%q vs %c vs %s
1 | package main |
More details refer to fmt package, like C format, but more simpler to use.
1 | import "fmt" |
10
Print: no auto space added between arg 10hello
10
Println: each arg is separated by space automatically 10 hello
Println: not support special format: 10 not cool
Printf: support special format: hello 10
Sprintf: support special format: 20 10
[999 99 9]
[a b c]
{jason 1}
struct { 𒀸name string; 𒀸id int }{𒀸name:"jason", 𒀸id:1}
{𒀸name:jason 𒀸id:1}
{jason 1}
hello "hello"
65 'A'
builtin API
func append(slice []Type, elems ...Type) []Type
The append built-in function appends elements to the end of a slice. If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not sufficient, a new underlying array will be allocated
func cap(v Type) int
- Array: the number of elements in v (same as len(v)).
2.Pointer to array: the number of elements in *v (same as len(v)).
3.Slice: the maximum length the slice can reach when resliced, may different with len(slice)
4.if v is nil, cap(v) is zero.
5.Channel: the channel buffer capacity, in units of elements;
func close(c chan<- Type)
The close built-in function closes a channel not a file, which must be either bidirectional or send-only. It should be executed only by the sender, never the receiver, and has the effect of shutting down the channel after the last sent value is received. After the last value has been received from a closed channel c, any receive from c will succeed without blocking, returning the zero value for the channel element
func copy(dst, src []Type) int
shadow copy(only the top level is copied)
The copy built-in function copies elements from a source slice into a destination slice. (As a special case, it also will copy bytes from a string to a slice of bytes.) The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum of len(src) and len(dst).
func delete(m map[Type]Type1, key Type)
delete element specified by key from a map
func len(v Type) int
1.Array: the number of elements in v.
2.Pointer to array: the number of elements in *v (even if v is nil).
3.Slice, or map: the number of elements in v; if v is nil, len(v) is zero.
4.String: the number of bytes in v
.
5.Channel: the number of elements queued (unread) in the channel buffer
;
6.if v is nil, len(v) is zero.
func make(t Type, size ...IntegerType) Type
Can be used only for Slice, Map, Channel
Slice: The size specifies the length. The capacity of the slice is
equal to its length. A second integer argument may be provided to
specify a different capacity; it must be no smaller than the
length. For example, make([]int, 0, 10) allocates an underlying array
of size 10 and returns a slice of length 0 and capacity 10 that is
backed by this underlying array.
Map: An empty map is allocated with enough space to hold the
specified number of elements. The size may be omitted, in which case
a small starting size is allocated.
Channel: The channel's buffer is initialized with the specified
buffer capacity. If zero, or the size is omitted, the channel is
unbuffered.
# for slice, can pass two parameters
s1 = make([]int, 4) // len=4 and cap = 4
s1 = make([]int, 0, 4)// len=0 and cap = 4
# for map, no one needed
m1 = make(map[string]int)
# for channel, can pass one parameter
c1 = make(chan int) // buffer size 0(unbuffered)
c2 = make(chan int, 10) // buffer size 10
func new(Type) *Type
The new built-in function allocates memory. The first argument is a type, not a value, and the value returned is a pointer to a newly allocated zero value of that type.
most of time, we does not use it at all
.
1 | import "fmt" |
[1 2 3] [0 0 0 0]
[1 2 3] [1 2 3 0]
0xc0009190d0 12
constants
untyped value has default type
An untyped value means the type of the value has not been confirmed yet. For most untyped values, each of them has one default type, All literal constants (unnamed constants) are untyped values
. most untyped values are literal constants and named constants.
The default type of a literal constant is determined by its literal form.
- The default type of a string literal is string.
- The default type of a boolean literal is bool.
- The default type of an
integer literal is int.
- The default type of a
rune literal is rune (a.k.a., int32)
. - The default type of a floating-point literal is float64.
- If a literal contains an imaginary part, then its default type is complex128
constant type
unnamed constant(literal constant)
1 | 12 |
named constant with untyped value
1 | const MAX = 12 |
named constant with typed value
1 | const MAX int8 = 12 |
type deduction(type inference)
Go supports type deduction. In other words, in many circumstances, programmers don’t need to explicitly specify the types of some values in code. Go compilers will deduce the types for these values by context.
In Go code, if a place needs a value of a certain type and an untyped value (often a constant) is representable as a value of the certain type, then the untyped value can be used in the place
. Go compilers will view the untyped value as a typed value of the certain type. it can be viewed as implicit conversions
.
constant declaration way
= not := for constant declaration
untyped named constant
1 | package main |
typed named constant
1 | const X float32 = 3.14 |
Autocomplete in constant declarations
In a group-style constant declaration, except the first constant specification, other constant specifications can be incomplete. An incomplete constant specification doesn’t contain the = symbol. Compilers will autocomplete the incomplete lines for us by copying the missing part from the first preceding complete constant specification
.
1 | const ( |
iota is a special value controlled by compiler, its value is reset to 0 for each const keyword
the first constant line of group, and increased by 1 for each appearance before next const keyword
.
1 | package main |
1 | const ( |
NOTE
- Constants are declared like variables, but with the
const keyword
. - Constants can be
character, string, boolean, or numeric values
. - Constants can NOT be declared using the := syntax.
- This is no enum in GO, use const instead
- Constants can be declared both at package level and function bodies.
1 | import "fmt" |
day 0
Hello world
Happy 3.14 Day
Go rules? true
Char 97
Char a
variable
All variables are typed values. When declaring a variable, there must be sufficient information provided for compilers to deduce the type of the variable
There are two basic variable declaration forms, the standard one and the short one. The short form can only be used to declare local variables
NOTE
- var is not needed for declaration like in struct, function parameter, function return value
- var is a must when declare global variable, optional for local variable.
- All variables are addressable and all constants are unaddressable
- Go doesn’t support assignment chain, like this a = b = 123.
Suggestion
- constant, use
const statement not var statement
- global variable, use
var statement
- local variable, but
no need explicit initialization(default value), use var statement
- local variable,
needs initialization, use := statement
- with assignment at declaration, always use short way.
As Go is compiled language, hence we must know the type of each variable at declaration either by explicit or implicit(assigned value), the type of variable is determined at declaration, can’t be change during running!!!
When declaring a variable without specifying an explicit type (either by using the := syntax or var = expression syntax), the variable’s type is inferred from the value on the right hand side
standard way
1 | var lang, website string = "Go", "https://golang.org" |
short way
Short variable declarations can only be used to declare local variables.
There are several differences between short and standard variable declarations.
- In the short declaration form, the var keyword and variable types must be omitted.
- The assignment sign must be := instead of =.
- In the short variable declaration,
old variables and new variables can mix at the left of :=. But there must be at least one new variable at the left.
1 | package main |
NOTE := declare all new variables left, not part of it
1 | package main |
default value of each type(without explicit initialization)
Variables declared without an explicit initial value
are given their zero value
, you can access var with zero directly, one except is for map, you can NOT modify nil map
!!
1 | var m map[string]int // nil map |
zero
value for each type
- 0 for numeric types,
- false for the boolean type
- “” (the empty string) for string.
- nil for pointer
- nil for function type
- nil, but len(map) == 0
- nil, but len(slice) == 0
- zero value for all fields for struct instance
variable initialization order
When a variable depends on another variable b, b should be defined beforehand, else program won’t compile. Go follows this rule inside functions
. but it’s not true for global variable.
1 | package main |
pointer
The type *T
is a pointer to a T
type.
1 | var p *int |
scope
A variable or a named constant declared in an inner code block will shadow the variables and constants declared with the same name in outer code blocks.
1 | package main |
1 | import "fmt" |
1 global b 10 20 local a 30 local c
types
Get Max value of integer, use math
lib which provides Max of Int8, Int16, Int32, Int64, Int and unsigned version as well.
1 | //do it by yourself |
conversion
Identical types, no need for conversion
Two types are identical if their underlying type literals are structurally equivalent; that is, they have the same literal structure and corresponding components have identical types. In detail:
Two array types are identical if they have identical element types and the same array length.
Two slice types are identical if they have identical element types.
Two struct types are identical if they have the same sequence of fields, and if corresponding fields have the same names, and identical types, and identical tags. Non-exported field names from different packages are always different.
1
2
3
4
5
6
7
8
9
10type A_ID int
type A struct {
id A_ID
}
type B_ID int
type B struct {
id B_ID
}
// A and B are different type!!
1
2
3
4
5
6
7
8type A struct {
name string
}
type B struct {
name string
}
// A and B are same typeTwo pointer types are identical if they have identical base types.
Two function types are identical if they have the same number of parameters and result values, corresponding parameter and result types are identical, and either both functions are variadic or neither is. Parameter and result names are not required to match
.Two interface types are identical if they have the same set of methods with the same names and identical function types. Non-exported method names from different packages are always different. The order of the methods is irrelevant.
Two map types are identical if they have identical key and element types.
Two channel types are identical if they have identical element types and the same direction.
Different types
Unlike in C, in Go assignment between different types(if possible) requires an explicit conversion
, there are two ways to use explicit type conversion, other different types can NOT be converted.
- number: int() uint()
- number<—>string:
strconv.Atoi("12"), strconv.Itoa(12)
Orfmt.Sprintf("%v",12)
you can Convert int to string in this way, the result may be not what you want j := string(97), j is "a" not "97"
1 | import "fmt" |
12 12 10 20
20
20
1 | package main |
jack
1 | type A_ID int |
100
string
A string is a struct that has a length and a pointer to a byte array. When you pass a string to another function, it copies the length and the pointer. As a consequence, the new copied string points to the same underlying data.
each element of string is a byte
like s[0]
, string is immutable, you can NOT modify it in place
Create a string
- var s string
- var s = “hello”
- s := “hello”
- var s = strconv.Itoa(12): Int to string: “12”
- string(97): 97 is “a”, so “a” is printed
Ops
- s[0], s[0] is
byte
type!!! - last element
s[len(s)-1]
, s[-1] NOT supported - s[0:3]
- s += “extend it”
- string([]byte{56,57}) // convert byte slice to string, new memory is created!!!
- string(slice)
- support s1 == s2
for _, c:= range s {}
c is rune type!!!
- string itself does not have method like
Find, Match
whilestrings
library provides ops for it - Can NOT convert array to string but slice is allowed.
1
2
3ar :=[2]byte{56,57}
fmt.Println(string(ar)) //error
fmt.Println(string(ar[:])) // copy array to slice
1 | import "fmt" |
h hel 0xc0004df3e0 0xc0004df400
0xc0004df400 hello
hello
boy
hello
world
string equal
uint8, e, 101
5
array and slice
Like C an array has an unique type, initialize with {}, arrays cannot be resized, size is fixed at initialization
, index from 0 like C.
- Arrays are values.
Assigning one array to another copies all its elements.
- In particular, if you
pass an array to a function, it will receive a copy of the array
, not a pointer to it. - The size of an array is part of its type. The types
[10]int and [20]int are distinct type
.
slice and array conversion
1 | //---------------array to slice(no need to define slice firstly)-------------------- |
Create an array
- var arr [3]int: all zero
- arr := [3]int{1, 2, 3}
- arr := [3]int{}: all zero
- var arr [3]interface{} // array of any type
- arr := [3]interface{}{}
- arr := [3]interface{}{“a”, 2, 1} array of any type
array of maps
1 | // each element of the array is a map. |
Ops
- arr[0]
- last element: arr[len(arr) - 1]
- arr[0:3] not include arr[3]
- for i, v := range arr {fmt.Println(v)}
- arr = append(arr, ‘a’, ‘b’) //arr may point to new memory!!!
- arr = append(arr, another_arr…) // link two arrays
slice
An array has a fixed size must be provided at declaration
. A slice, on the other hand, is a dynamically-sized, flexible view into the elements of an array
. In practice, slices are much more common than arrays.
A slice is formed by specifying two indexes, a low and high bound(not included
), separated by a colon:a[low:high]
you may omit the high or low bounds to use their defaults instead. The default is zero for the low bound and the length of the slice for the high bound.
These slice expressions are equivalent:
1 | a[0:10] == a[:10] |
A slice does not store any data, it just describes a section of an underlying array, Slices are like references to arrays
Changing the elements of a slice modifies the corresponding elements of its underlying array.
The underlying array is dynamic and can be enlarged(or reduced to smaller one) to a new larger array(may larger than real elements) if append to a slice, hence a slice has both a length and a capacity.
The length of a slice is the number of elements it contains.
The capacity of a slice is the number of elements in the underlying array, counting from the first element in the slice.
The length and capacity of a slice s can be obtained using the expressions len(s) and cap(s)
.
Create a slice
- var sc []int
- sc := []int{1, 2}, nsc = sc[:] // nsc and sc point to same underlaying memory
- sc := []byte(“hello”) // byte slice from string
- sc := make([]int, 0, 5)
- arr := [10]int{}; sc := arr[1:5]; sc := arr[:], sc and arr points to same memory
- sc := []interface{}{}
- sc := []interface{}{“a”, 1} slice of any type.
func test(sp *[] int)
pointer to slice!!!
Create a slice of map, each slice element is a map
1 | // slice with 0 element |
Ops
- sc[0]
- last element: sc[len(sc) - 1]
- sc[0:3]
- for i, v := range sc {fmt.Println(v)}
- sc = append(sc, 12) // sc may point to new memory!!!
- sc = append(sc, 12, 13) // sc may point to new memory!!!
- sc = append(sc, another_sc…) // sc may point to new memory!!!
- inset element at index
sc = append(sc[:index+1], orig[sc:]...) orig[index] = value
- remove element at index
sc = append(sc[:index], sc[index+1:]...)
Note
- For append(),
If the backing array of s is too small to fit all the given values a bigger array will be allocated
. Thereturned slice
will point to the newly allocated array. - New element is put at the end of len, may overwrite underlaying array if it’s part of it
- empty slice is nil with len == 0 but len(s)==0, s may be not nil
1
2s2 := make([]int, 0, 4)
len(s2) == 0 // but s2 is not nil!!
1 | func test() { |
how slice cap change
every slice has an underlying array, an array may be shared among several slices. If the new slice’s length will exceed the array’s capacity, a new array will be created for the new slice. Usually new capacity will be two times old capacity
cap(s), count elements from the beginning of slice to the end of underlay memory.
make([]byte, 5)
s = s[2:4], cap(s) == 3
Go only supports move start of underlaying array, but the end, the first two is dropped, memory is recycled!!!
s = s[:3], cap(s) == 5!!!
1 | package main |
NOTE
- It’s ok to loop a nil slice, same thing for map as well
1
2
3
4var n []int
for _, i := range n {
fmt.Printf("%d\n", i)// nothing print as n is nil
}
1 | import "fmt" |
[1 2 3 4 5] 5
[1 2 3 4]
[10 2 3 4 5] [10 2 3 4] 5 4
[10 20 30 40 50] 5 5
[10 20 30 40 50 60 70] 10 7
[0 0 0 0 0] 5 5
[] 5 0
[{1 2} {3 4}]
[{1 2} {3 4}]
0 {1 2}
1 {3 4}
{1 2}
{3 4}
{1 2}
{3 4}
[6 2 3 4 5] [1 2 3 4 5]
[100 2]
[1 hi] [2 two]
[1 2] [1 2 3 4]
1 | import "fmt" |
len=6 cap=6 [2 3 5 7 11 13]
len=0 cap=6 []
len=4 cap=6 [2 3 5 7]
demo
len=3 cap=5 [3 5 7]
len=2 cap=5 [3 5]
len=1 cap=4 [5]
map(dict)
Key of map can be of any type for which the equality operator is defined
, such as integers, floating point and complex numbers, strings, pointers, interfaces (as long as the dynamic type supports equality), structs and arrays
. Slices cannot be used as map keys, because equality is not defined on them, value can by any type like int, string, slice, function etc
Create a map
- var m map[string]int:
map[string]int sits at right side when assigning values
- m := map[string]int{} // empty map
- m := map[string]int{“a”: 1, “b”: 2}
- m := make(map[string]int)
- m := map[string]interface{}{}
- m := map[string]interface{}{“a”: 1, “b”: “b”} key must be quoted when it’s string literal
- m := map[string]func(i string){} map of function object.
create map whose value is a slice
1 | var m = map[string][]int{} // empty map |
create map whose value is func object
1 | package main |
Ops
- m[“c”]= 3
- elem, ok = m[key]
- delete(m, “c”): It’s safe to do even if the key is absent from the map
you CAN NOT assign value for nil map, you must create it first!!!
1 | var mt map[string]int // nil map |
But it’s ok to loop a nil map
1 | var n map[string]int |
Note
- Access map by map[key] NOT map.key
- The key of map must be same type, but the value can be any type when use
interface{}
as value type.
1 | import "fmt" |
map[a:1 b:2 e:15 f:100] map[a:1 b:2 e:15 f:100] map[c:3 d:4]
The value: 42
The value: 48
The value: 0
The value: 0 Present? false
map[a:1 b:2]
a 1
b 2
map[a:12 b:hi] 12 hi
struct
Struct fields can be accessed by struct instance or through a struct pointer which uses . NOT ->
like what did in C
1 | type Vertex struct { |
Create a struct instance
- var st Vertex
- st := Vertex{1, 2} // unamed assignment, must provide all values!!!
- st := &Vertex{x: 1} // named assignment, can provide part of values!!!
- st := Vertex{x: 1, y: 2} NOT “x” or “y” when use named index!!!
- st := Vertex{}
Ops
- st.x = 10
- p := &st
- p.x = 10
not p->x
Note
- Access field of struct by st.field_name not st[“field_name”] like what we do for map
- pointer still uses p.field_name to access filed which is converted to
(*p).field
by Go automatically
1 | import "fmt" |
{1 2 3} &{1 2 3} {0 1 0} {0 0 0} {1 3 4}
{1 2 3} &{1 2 3} {0 1 0} {0 0 0} {1 3 4} {4 5 6}
function
Always remember Go is compiled language, hence, each parameter and return value must have a type, NO default value supported for parameter func test(x=12, y)
, Unsupported named parameter call like test(y=12, x=13)
.
function can return any number of results
1 | func add(x int, y int) int { |
defer
A defer statement defers the execution of a function until the surrounding function returns.
The deferred call’s arguments are evaluated immediately, but the function call is not executed until the surrounding function returns. deferred function calls are pushed onto a stack. When a function returns, its deferred calls are executed in last-in-first-out order
.
1 |
|
Note
- defer GetPerson().GetName() only the last call
GetName(
) is deferred, GetPerson() is called immediately!!! - deferred call’s
arguments are evaluated immediately
deferred function should no return
, if wants return value, use channel, if deferred function has return value, it’s not captured!!!- deferred function executes after return statement!!!
- As go is compile, hence deferred may not be pushed to stack, if code not reach it!!!.
1 | package main |
1 + 2 = 3
1 | package main |
defer false
function object
Function is an object, so it can be used as argument or return value
1 | import "fmt" |
function closure
function closure is a function that returns another function, but you can NOT define a function in another function like this
1 | func test() { |
closure Return unnamed function
1 | func adder() func(int) int { // return value is a function |
1 | package main |
wrapper: hello tom
variadic function(dynamic parameters)
In Go, a function that can accept a dynamic number of arguments is called a Variadic function. Below is the syntax for variadic function. Three dots are used as a prefix before type.
dynamic parameters with same type
1 | // same type, ... is closer to type, it's a new type ...int |
dynamic parameters for different types
1 | func test() { |
1 | import "fmt" |
hello
3
1
2
1
girl
boy
function type
Think function signature(without name) as a type
, you can declare variable, parameter, new type based on function signature.
1 | package main |
hello jason
hello jason
function parameter
Parameter passing is same like C except for array, for array it’s copy of the whole array, not array pointer is passed!!!
For slice even pointer
is passed in function, if you append new element in that slice, the caller does not know either, see below explanations
the underlying array reached its capacity, a new slice created to replace the origin one, obviously the origin slice will not be modified.
the underlying array has not reached its capacity, and was modified. BUT the field len of the slice was not overwritten because the slice was passed by value
. As a result, the origin slice will not aware its len was modified, which result in the slice not modified.
1 | package main |
[a b] 2
[a b c] 3
[a b] 2
[x c] 2
[x] 1
flow control
for
Go has only one looping construct, the for
loop, NO while, until
etc.
The basic for loop has three components separated by semicolons
:
- the init statement: executed before the first iteration
- the condition expression: evaluated before every iteration
- the post statement: executed at the end of every iteration
*Note
- Unlike other languages like C, Java, or JavaScript. For Go there are no parentheses surrounding the three components of the for statement but the braces { } are always required.
1 | import "fmt" |
use for as while
as init and post statements are optional
1 | sum := 0 |
infinite loop
1 | for { |
multiple assignments
1 | a := []int{1, 2, 3, 4, 5, 6} |
range with for
1 | package main |
NOTE: when reach the loop end, the index is different!!!
1 | package main |
if
Go’s if
statements is like its for
loops; the expression may not be surrounded by parentheses ( ) but the braces { } are required.
The if
statement can start with a short statement to execute before the condition
.
Variables declared by the statement are only in scope until the end of the if
.
it’s also available inside any of the else
blocks.
1 | import "fmt" |
NOTE
- non-boolean type can NOT be used as if condition!!!
switch
The expressions need not to be constants or even integers
, the cases are evaluated top to bottom until a match is found, and if the switch has no expression it switches on true.
Go’s switch is like the one in C, C++, Java, JavaScript, and PHP, except that Go only runs the selected case (implicit break at the end)
, not all the cases that follow. In effect, the break statement that is needed at the end of each case in those languages is provided automatically in Go but if you want to break in the middle of this case, break is required
. Another important difference is that Go’s switch cases need not be constants, and the values involved need not be integers.
Switch cases evaluate cases from top to bottom, stopping when a case succeeds, auto break if matched
1 | import "fmt" |
Switch without condition
Switch without a condition is the same as switch true
.
This construct can be a clean way to write long if-then-else chains
.
1 | t := 15 |
goto
1 | func myfunc() { |
break/continue
By default, break, continue
work for inner loop, but if you want to take effect of outer loop, use label
for break, continue
.
1 | import "fmt" |
0123467
-------------------------
hello
-------------------------
i= 0
j= 1
i= 1
j= 2
-------------------------
2
system env
1 | import ( |
PATH=/home/data/Anaconda3/envs/py3.9/bin:/opt/llvm/bin:/home/data/Anaconda3/envs/py3.9/bin:/home/data/Anaconda3/condabin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/home/go:/home/go/bin:/root/.yarn_pkg/bin:/usr/lib64:/usr/local/go/bin:/home/data/Anaconda3/envs/py3.9/libexec/git-core:/root/bin:/root/.yarn_pkg/bin:/home/go/bin:/home/go:/usr/local/go/bin
PWD=/
LANG=en_US.UTF-8
SHLVL=1
_=/usr/bin/env
GO111MODULE=on
GOMODCACHE=/home/go/pkg/mod
GOCACHE=/root/.cache/go-build
GOPATH=/home/go
PYDEVD_USE_FRAME_EVAL=NO
JPY_PARENT_PID=1797
GO=/tmp/go
/tmp/go
small tips
return local var from stack is safe in GO
Returning an address of a local variable is also safe.
1 | import "fmt" |
string vs [] byte
string is the set of byte
, conventionally but not necessarily representing UTF-8-encoded text
. A string may be empty, but not nil.
Values of string type are immutable
Values of []byte are mutable
conversion
1 | s1 := "hello" |
1 | import "fmt" |
6
[20320 22909] 2 你好
what does empty mean for each type
let’s focus on these types, string, integer(int, uint etc), pointer, array, slice, map)
1 | import "fmt" |
string default value: ""
int default value: 0
pointer default value: nil
slice defautl value: nil ([], cap=0, len=0)
map defautl value: nil ({} len=0)
get the size of memory for each type
1 | package main |
when should I use new()
new(T) allocates zeroed storage
for a new item of type T and returns its address, a value of type *T.
Suggestion, use it as less as possible, as new(T) and &T{} can do the same thing. Both allocate a zero T and return a pointer to this allocated memory. The only difference is, that &T{} doesn’t work for builtin types like int; you can only do new(int).
1 | // without new only form is different!! |
when should I use make()
It creates slices, maps, and channels only, and it returns an initialized (not zeroed) value of type T (not *T)
. The reason for the distinction is that these three types represent, under the covers, references to data structures that must be initialized before use. A slice, for example, is a three-item descriptor containing a pointer to the data (inside an array), the length, and the capacity
, and until those items are initialized, the slice is nil. For slices, maps, and channels, make initializes the internal data structure and prepares the value for use
The make built-in function allocates and initializes
an object of type slice, map, or chan (only), can be used only for Slice, Map, Channel
Slice: The size specifies the length. The capacity of the slice is equal to its length. A second integer argument may be provided to specify a different capacity; it must be no smaller than the
length. For example,make([]int, 0, 10) allocates an underlying array of size 10 and returns a slice of length 0 and capacity 10 that is backed by this underlying array
.Map: An empty map(not equal nil) is allocated with enough space to hold the specified number of elements. The size may be omitted, in which case a small starting size is allocated.
Channel: The channel’s buffer is initialized with the specified buffer capacity. If zero, or
the size is omitted, the channel is unbuffered
.
Suggestion
- If you know the estimated size of slice or map, use make() to preallocate enough memory
- Always use
make() for channel
1 | s := []int{} |
pointer to array and array of pointers
1 | // bad way never use this, use slice instead |
1 | package main |
tom jack hak
check type of variable
1 | var1 := 12 |
variable has same name with package
In such case, error happens.
1 | package main |