title: Go
#+STARTUP: overview
Go Features
Note
Array
- Array & Slice: Array must with specialized length for [x]
- function:
- must given specialized arguments(if point) when execution
- method:
- Must have a receiver
- if receiver is pointer of instance, the value of instance can be modified
- for execution: the instance of receiver can be pointer or not, will automatically convered
- if receiver is not a pointer, the receiver can still be a pointer
interface
- def interface with its contains
- def receiver
- receiver implementing the contains with methods(just like method above)
- instancing of interface
- instancing of receiver
- passing receiver to interface(if the implementation is kind of pointer for receiver, here also pass pointer)
- executing methods of interface
Slice
As you've noticed, when you slice from the end, it's the length that shrinks. The pointer to the first element and the capacity remain unchanged.
When you slice from the beginning of the slice, though, what happens instead is the pointer to the first element is changed to be a pointer to the nth element that you're slicing from (i.e. sl = arr[2:] means to set the pointer to point to &arr[2]). Because the head of the slice has moved forward 2, in this case, the length and capacity have to decrease by 2.
Generics
package main
import "fmt"
func Sum[V int64 | float64](m ...V) V {
var s V
for _,v := range m {
s += v
}
return s
}
func main() {
fmt.Println(Sum([]int64{1,2,3,4,5}...))
fmt.Println(Sum(int64(1),int64(2),int64(3),int64(4)))
fmt.Println(Sum(1.1, 2.2, 3.3, 4.4))
}
package main
import (
"fmt"
"golang.org/x/exp/constraints"
)
func Sum[V constraints.Float | constraints.Integer](m ...V) V {
var s V
for _,v := range m {
s += v
}
return s
}
func main() {
fmt.Println(Sum([]int64{1,2,3,4,5}...))
fmt.Println(Sum(1,2,3,4))
fmt.Println(Sum(1.1, 2.2, 3.3, 4.4))
fmt.Println(Sum(uint32(2), uint32(4)))
}
cd babel
go mod init go-generics-constraints
go mod tidy
go build go_generics_constraints.go
rm main.go
rm go.mod
rm go.sum
./go_generics_constraints
15
10
11
6
Strings
package main
import (
"fmt"
"strings"
)
func main() {
cut := func(sep string) {
s:= "hello|world"
before, after, found := strings.Cut(s, sep)
fmt.Printf("Cut(%q, %q): %q, %q, %v\n", s, sep, before, after, found)
}
cut("|")
cut("hello")
cut("nothing")
}
Cut("hello|world", "|"): "hello", "world", true
Cut("hello|world", "hello"): "", "|world", true
Cut("hello|world", "nothing"): "hello|world", "", false
Array
package main
import (
"fmt"
)
func test(array []int64) {
for i:= 0; i< 3; i++{
array[i] = 10
}
}
func main() {
start := make([]int64, 3)
test(start)
fmt.Println(start)
}
[10 10 10]
Map
three way to create a map
package main
import (
"fmt"
)
func main() {
myMap1 := make(map[string]string, 10)
fmt.Println(myMap1)
myMap1["one"] = "python"
fmt.Println(myMap1)
myMap2 := make(map[string]string)
myMap2["one"] = "python"
fmt.Println(myMap2)
myMap3 := map[string]string{
"one": "python",
}
fmt.Println(myMap3)
}
map[]
map[one:python]
map[one:python]
map[one:python]
Methode
function(copy or pointer) methode(copy or pointer)
interface
package main
import (
"fmt"
)
type AnimalIF interface{
Sleep()
GetColor() string
GetType() string
}
type Cat struct{
color string
}
func (this *Cat) Sleep(){
fmt.Println("Cat is sleeping")
}
func (this *Cat) GetColor() string {
return this.color
}
func (this *Cat) GetType() string {
return "Cat"
}
type Dog struct{
color string
}
func (this *Dog) Sleep(){
fmt.Println("Dog is sleeping")
}
func (this *Dog) GetColor() string {
return this.color
}
func (this *Dog) GetType() string {
return "Dog"
}
func ShowInfo(animal AnimalIF){
animal.Sleep()
fmt.Println("color = ", animal.GetColor())
fmt.Println("type = ", animal.GetType())
}
func main() {
cat := Cat{"Green"}
dog := Dog{"Yellow"}
ShowInfo(&cat)
ShowInfo(&dog)
}
Cat is sleeping
color = Green
type = Cat
Dog is sleeping
color = Yellow
type = Dog
interface {} assert: arg.(string), if arg is not {}, it will be forced to convert to string can refer to every kind of value
polymorphism
OpenFile already implemented Wirter and Reader
package main
import (
"fmt"
"io"
"os"
)
func main() {
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
fmt.Println("Open file with error", err)
return
}
var r io.Reader
r = tty
var w io.Writer
w = r.(io.Writer)
w.Write([]byte("HELLO from meb\n"))
}
Open file with error open /dev/tty: no such device or address
reflect
package main
import (
"fmt"
"reflect"
)
func reflectNum(arg interface{}){
fmt.Println("type: ", reflect.TypeOf(arg))
fmt.Println("value: ", reflect.ValueOf(arg))
}
func main(){
var num float64 = 3.1415926
reflectNum(num)
}
type: float64
value: 3.1415926
package main
import (
"fmt"
"reflect"
)
type User struct {
Id int
Name string
Age int
}
func (this User) Call(){
fmt.Println("user is calling")
fmt.Printf("%v\n", this)
}
func (this User) End_Call(id int){
fmt.Println(id)
fmt.Printf("%v\n", this)
}
func main(){
user := User{10, "Alice", 23}
reflectExample(user)
}
func reflectExample(input interface{}){
//get tpye
inputType := reflect.TypeOf(input)
fmt.Println("input Type is :", inputType.Name())
//get value
inputValue := reflect.ValueOf(input)
fmt.Println("value Type is :", inputValue)
//get fields in details
for i := 0; i< inputType.NumField(); i++ {
field := inputType.Field(i)
value := inputValue.Field(i).Interface()
fmt.Printf("%s: %v = %v\n",field.Name, field.Type, value)
}
//get methods in details
for i := 0; i< inputType.NumMethod(); i++ {
method := inputType.Method(i)
fmt.Printf("%s: %v\n", method.Name, method.Type)
}
}
input Type is : User
value Type is : {10 Alice 23}
Id: int = 10
Name: string = Alice
Age: int = 23
Call: func(main.User)
End_Call: func(main.User, int)
time
package main
import (
"fmt"
"time"
)
func test() {
start := time.Now()
sum := 0
for i := 0; i < 100000; i++ {
sum++
}
elapsed := time.Since(start)
fmt.Println("该函数执行完成耗时:", elapsed)
}
func main() {
test()
}
该函数执行完成耗时: 33.536µs
sync
package main
import "fmt"
func produce(c chan<- int) {
for i := 0; i < 10; i++ {
fmt.Printf(" Produced : %d\n", i)
c <- i // synchronization
}
}
func consume(c <-chan int) {
for true {
i := <-c // synchronization
fmt.Printf(" Consumed : %d\n", i)
}
}
func main() {
c := make(chan int)
go consume(c)
produce(c)
}
Produced : 0
Consumed : 0
Produced : 1
Produced : 2
Consumed : 1
Consumed : 2
Produced : 3
Produced : 4
Consumed : 3
Consumed : 4
Produced : 5
Produced : 6
Consumed : 5
Consumed : 6
Produced : 7
Produced : 8
Consumed : 7
Consumed : 8
Produced : 9
package main
import (
"fmt"
"time"
"sync"
)
func main() {
var wg sync.WaitGroup
wg.Add(1)
go func(){
count("Sleep")
wg.Done()
}()
wg.Wait()
}
func count(thing string) {
for i := 1; i <= 5; i++ {
fmt.Println(i, thing)
time.Sleep(time.Millisecond*500)
}
}
1 Sleep
2 Sleep
3 Sleep
4 Sleep
5 Sleep
go
package main
import (
"fmt"
"time"
)
func main() {
var times int
go func() {
for {
}
}()
go func() {
for {
}
}()
go func() {
for {
}
}()
go func() {
for {
}
}()
for times = 0; times <= 10; times++ {
fmt.Println("tick", times)
time.Sleep(time.Second)
}
}
tick 0
tick 1
tick 2
tick 3
tick 4
tick 5
tick 6
tick 7
tick 8
tick 9
tick 10
chan
example
package main
import "fmt"
func main() {
// 创建一个整型带两个缓冲的通道
ch := make(chan int, 2)
// 给通道放入两个数据
ch <- 0
ch <- 1
// 关闭缓冲
close(ch)
// 遍历缓冲所有数据, 且多遍历1个
for i := 0; i < cap(ch)+1; i++ {
// 从通道中取出数据
v, ok := <-ch
// 打印取出数据的状态
fmt.Println(v, ok)
}
}
#+RESULTS:
dead lock unbuffer channel
package main
import "fmt"
func main() {
// 创建一个整型带两个缓冲的通道
ch := make(chan int)
// 给通道放入两个数据
ch <- 0
id := <- ch
fmt.Println(id)
close(ch)
}
*but can be saved with goroutine*
package main
import "fmt"
func main() {
ch := make(chan int)
go func(){
ch <- 0
}()
id := <- ch
fmt.Println(id)
close(ch)
}
0
deal lock after buffer channel is full
buffer can be filled
package main
import "fmt"
func main() {
ch := make(chan int, 2)
ch <- 0
ch <- 1
id := <- ch
fmt.Println(id)
close(ch)
}
0
but can not be exceeded
package main
import "fmt"
func main() {
ch := make(chan int, 2)
ch <- 0
ch <- 1
ch <- 2
id := <- ch
fmt.Println(id)
close(ch)
}
0
context
package main
import (
"fmt"
"context"
"time"
)
func enrichContext(ctx context.Context) context.Context {
return context.WithValue(ctx, "request-id", "11212")
}
func doSomething(ctx context.Context){
rID := ctx.Value("request-id")
fmt.Println(rID)
for {
select {
case <-ctx.Done():
fmt.Println("times out")
return
default:
fmt.Println("doing something cool")
}
time.Sleep(500*time.Millisecond)
}
}
func main (){
fmt.Println("Go Context example")
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
fmt.Println(ctx.Err())
ctx = enrichContext(ctx)
go doSomething(ctx)
select {
case <- ctx.Done():
fmt.Println("Oh, no, Time is execeed the deadline")
fmt.Println(ctx.Err())
}
time.Sleep(2*time.Second)
}
Go Context example
<nil>
11212
doing something cool
doing something cool
doing something cool
doing something cool
Oh, no, Time is execeed the deadline
context deadline exceeded
times out
package main
import (
"context"
"fmt"
"log"
"time"
)
func main() {
start := time.Now
ctx := context.Background()
userId := 10
val, err := fetchUserData(ctx, userId)
if err != nil {
log.Fatal(err)
}
fmt.Println("result : ", val)
fmt.Println("took ", time.Since(start()))
}
type Response struct {
value int
err error
}
func fetchUserData(ctx context.Context, userid int) (int, error) {
ctx, cancel := context.WithTimeout(ctx, time.Millisecond*200)
defer cancel()
respch := make(chan Response)
go func() {
val, err := fetchThirdPartyStuffWhichCanbeSlow()
respch <- Response{
value: val,
err: err,
}
}()
for {
select {
case <-ctx.Done():
return 0, fmt.Errorf("Fetch data is time out")
case resp := <-respch:
return resp.value, resp.err
}
}
}
func fetchThirdPartyStuffWhichCanbeSlow() (int, error) {
time.Sleep(time.Millisecond * 150)
return 666, nil
}
gonum
Test example
package main
import (
"fmt"
"gonum.org/v1/gonum/blas/blas64"
"gonum.org/v1/gonum/mat"
)
func main() {
r := row{1, 2, 3, 4}
c := column{1, 2, 3}
var m mat.Dense
m.Mul(c, r)
fmt.Println(mat.Formatted(&m))
n := c.RawVector().N
inc := c.RawVector().Inc
d := c.RawVector().Data
fmt.Println(n)
fmt.Println(inc)
fmt.Println(d)
u := mat.NewVecDense(3, []float64{1, 2, 3})
v := mat.NewVecDense(3, []float64{4, 5, 6})
fmt.Println("u :", u)
fmt.Println("v :", v)
}
// row is a user-defined row vector.
type row []float64
// Dims, At and T minimally satisfy the mat.Matrix interface.
func (v row) Dims() (r, c int) { return 1, len(v) }
func (v row) At(_, j int) float64 { return v[j] }
func (v row) T() mat.Matrix { return column(v) }
// RawVector allows fast path computation with the vector.
func (v row) RawVector() blas64.Vector {
return blas64.Vector{N: len(v), Data: v, Inc: 1}
}
// column is a user-defined column vector.
type column []float64
// Dims, At and T minimally satisfy the mat.Matrix interface.
func (v column) Dims() (r, c int) { return len(v), 1 }
func (v column) At(i, _ int) float64 { return v[i] }
func (v column) T() mat.Matrix { return row(v) }
// RawVector allows fast path computation with the vector.
func (v column) RawVector() blas64.Vector {
return blas64.Vector{N: len(v), Data: v, Inc: 1}
}
NewDense
package main
import (
"fmt"
"gonum.org/v1/gonum/mat"
)
func main() {
zeros := mat.NewDense(3,5,nil)
fmt.Println(zeros)
}
make
package main
import (
"fmt"
"math/rand"
"gonum.org/v1/gonum/mat"
)
func main() {
data := make([]float64, 36)
for i := range data {
data[i] = rand.NormFloat64()
}
a := mat.NewDense(6, 6, data)
zeros := mat.NewDense(3,5,nil)
// fmt.Println(mat.Formatted(zeros, mat.Prefix(""), mat.Squeeze()))
// fmt.Println(mat.Formatted(a, mat.Prefix(""), mat.Squeeze()))
eq := mat.Equal(a, zeros)
fmt.Println(eq)
}
gonum execute
cd babel
go mod init babel-go
go mod tidy
go build main.go
rm main.go
rm go.mod
rm go.sum
./main
false
PatternDesign
Observer
package main
import (
"fmt"
"sync"
"time"
)
type (
eventObserver struct {
id int
time time.Time
}
eventSubject struct {
observers sync.Map
}
Event struct {
data int
}
Observer interface {
NotifyCallback(Event)
}
Subject interface {
AddListener(Observer)
RemoveListener(Observer)
Notify(Event)
}
)
// NotifyCallback ...
func (e *eventObserver) NotifyCallback(event Event) {
fmt.Printf("Observer: %d Recieved: %d after %v\n", e.id, event.data, time.Since(e.time))
}
// AddListener ...
func (s *eventSubject) AddListener(obs Observer) {
s.observers.Store(obs, struct{}{})
}
// RemoveListener ...
func (s *eventSubject) RemoveListener(obs Observer) {
s.observers.Delete(obs)
}
// Notify ...
func (s *eventSubject) Notify(event Event) {
s.observers.Range(func(key interface{}, value interface{}) bool {
fmt.Printf("%T", key)
if key == nil || value == nil {
return false
}
key.(Observer).NotifyCallback(event)
return true
})
}
func fib(n int) chan int {
out := make(chan int)
go // name ...
func() {
defer close(out)
for i, j := 0, 1; i < n; i, j = i+j, i {
out <- i
}
}()
return out
}
func main() {
n := eventSubject{
observers: sync.Map{},
}
var obs1 = eventObserver{id: 1, time: time.Now()}
var obs2 = eventObserver{id: 2, time: time.Now()}
n.AddListener(&obs1)
n.AddListener(&obs2)
n.AddListener(&eventObserver{id: 3, time: time.Now()})
go func() {
select {
case <-time.After(time.Millisecond):
n.RemoveListener(&obs2)
}
}()
for x := range fib(1000000000000000000) {
n.Notify(Event{data: x})
}
}
Factory
package main
import (
"fmt"
"reflect"
)
type (
mongoDB struct {
database map[string]string
}
sqlite struct {
database map[string]string
}
Database interface {
GetData(string) string
PutData(string, string)
}
file struct {
name string
content string
}
ntfs struct {
files map[string]file
}
ext4 struct {
files map[string]file
}
FileSystem interface {
CreateFile(string)
FindFile(string) file
}
Factory func(string) interface{}
)
func (mdb mongoDB) GetData(query string) string {
if _, ok := mdb.database[query]; !ok {
return ""
}
fmt.Println("MongoDB")
return mdb.database[query]
}
func (mdb mongoDB) PutData(query string, data string) {
mdb.database[query] = data
}
func (sql sqlite) GetData(query string) string {
if _, ok := sql.database[query]; !ok {
return ""
}
fmt.Println("Sqlite")
return sql.database[query]
}
func (sql sqlite) PutData(query string, data string) {
sql.database[query] = data
}
func (ntfs ntfs) CreateFile(path string) {
file := file{content: "NTFS file", name: path}
ntfs.files[path] = file
fmt.Println("NTFS")
}
func (ext ext4) CreateFile(path string) {
file := file{content: "EXT4 file", name: path}
ext.files[path] = file
fmt.Println("EXT4")
}
func (ntfs ntfs) FindFile(path string) file {
if _, ok := ntfs.files[path]; !ok {
return file{}
}
return ntfs.files[path]
}
func (ext ext4) FindFile(path string) file {
if _, ok := ext.files[path]; !ok {
return file{}
}
return ext.files[path]
}
// DatabaseFactory ...
func DatabaseFactory(env string) interface{} {
switch env {
case "production":
return mongoDB{
database: make(map[string]string),
}
case "development":
return sqlite{
database: make(map[string]string),
}
default:
return nil
}
}
func FilesystemFactory(env string) interface{} {
switch env {
case "production":
return ntfs{
files: make(map[string]file),
}
case "development":
return ext4{
files: make(map[string]file),
}
default:
return nil
}
}
// AbstractFactoryy ...
func AbstractFactory(fact string) func(string) interface{} {
switch fact {
case "database":
return DatabaseFactory
case "filesystem":
return FilesystemFactory
default:
return nil
}
}
func SetupConstructors(env string) (Database, FileSystem) {
fs := AbstractFactory("filesystem")
db := AbstractFactory("database")
return db(env).(Database), fs(env).(FileSystem)
}
func main() {
env1 := "production"
env2 := "development"
// db1 := DatabaseFactory(env1)
// db2 := DatabaseFactory(env2)
db1, fs1 := SetupConstructors(env1)
db2, fs2 := SetupConstructors(env2)
db1.PutData("test", "for test")
fmt.Println(db1.GetData("test"))
db2.PutData("test2", "for test2")
fmt.Println(db2.GetData("test2"))
fs1.CreateFile("../example/fts.txt")
fmt.Println(fs1.FindFile("../example/fts.txt"))
fs2.CreateFile("../example/et4.txt")
fmt.Println(fs2.FindFile("../example/et4.txt"))
fmt.Println(reflect.TypeOf(db1).Name())
fmt.Println(reflect.TypeOf(&db1).Elem())
fmt.Println(reflect.TypeOf(db2).Name())
fmt.Println(reflect.TypeOf(&db2).Elem())
fmt.Println(reflect.TypeOf(fs1).Name())
fmt.Println(reflect.TypeOf(&fs1).Elem())
fmt.Println(reflect.TypeOf(fs2).Name())
fmt.Println(reflect.TypeOf(&fs2).Elem())
}
MongoDB
for test
Sqlite
for test2
NTFS
{../example/fts.txt NTFS file}
EXT4
{../example/et4.txt EXT4 file}
mongoDB
main.Database
sqlite
main.Database
ntfs
main.FileSystem
ext4
main.FileSystem
Decorator
package main
import (
"fmt"
"log"
"math"
"os"
"sync"
"time"
)
func Pi(n int) float64 {
ch := make(chan float64)
for k := 0; k <= n; k++ {
go func(ch chan float64, k float64) {
ch <- 4 * math.Pow(-1, k) / (2*k + 1)
}(ch, float64(k))
}
result := 0.0
for k := 0; k <= n; k++ {
result += <-ch
}
return result
}
type piFunc func(int) float64
func wraplogger(fun piFunc, logger *log.Logger) piFunc {
return func(n int) float64 {
fn := func(n int) (result float64) {
defer func(t time.Time) {
logger.Printf("took=%v, v=%v, result=%v", time.Since(t), n, result)
}(time.Now())
return fun(n)
}
return fn(n)
}
}
func wrapcache(fun piFunc, cache *sync.Map) piFunc {
return func(n int) float64 {
fn := func(n int) float64 {
key := fmt.Sprintf("n=%d", n)
val, ok := cache.Load(key)
if ok {
return val.(float64)
}
result := fun(n)
cache.Store(key, result)
return result
}
return fn(n)
}
}
func divide(n int) float64 {
return float64(n / 2)
}
func main() {
// 01
// fmt.Println(Pi(1000))
// fmt.Println(Pi(50000))
// 02
//f := wraplogger(Pi, log.New(os.Stdout, "test", 1))
// f(10000)
//03
f := wrapcache(Pi, &sync.Map{})
g := wraplogger(f, log.New(os.Stdout, "test", 1))
g(100000)
g(200000)
g(500000)
g(500000)
// 04
f = wrapcache(divide, &sync.Map{})
g = wraplogger(f, log.New(os.Stdout, "divide", 1))
g(100000)
g(200000)
g(500000)
g(500000)
}
test2022/07/12 took=197.241951ms, v=100000, result=3.1416026534897195
test2022/07/12 took=326.435601ms, v=200000, result=3.1415976535647596
test2022/07/12 took=678.005443ms, v=500000, result=3.1415946535856927
test2022/07/12 took=2.673µs, v=500000, result=3.1415946535856927
divide2022/07/12 took=15.404µs, v=100000, result=50000
divide2022/07/12 took=3.475µs, v=200000, result=100000
divide2022/07/12 took=1.724µs, v=500000, result=250000
divide2022/07/12 took=922ns, v=500000, result=250000
单例模式
package main
import (
"fmt"
)
type User struct {
Name string
Occupation string
}
type Moli interface {
Changename()
ChangeOccupation()
}
func CreateUser() (Moli, *User){
var moli Moli
u := new(User)
moli = u
return moli, u
}
func (u *User) Changename (){
u.Name = "Joke"
}
func (u *User) ChangeOccupation (){
u.Occupation = "driver"
}
func changeName(u User) {
u.Name = "Newi"
}
func changeOccupation(u User) {
u.Occupation = "driver"
}
func main() {
M, user := CreateUser()
fmt.Println("should be empty", user)
user.Name = "kji"
fmt.Println("Directly can change the name", *user)
user.Occupation = "Nothing"
fmt.Println("Directly can change the occupation", *user)
changeName(*user)
fmt.Println("In function, indieictly can not be changed", *user)
changeOccupation (*user)
fmt.Println("In function, idieictly can not be changed", *user)
M.Changename()
fmt.Println("In methode, indieictly can be changed its name", *user)
M.ChangeOccupation()
fmt.Println("In methode, indieictly can be changed its occupation", *user)
}
should be empty &{ }
Directly can change the name {kji }
Directly can change the occupation {kji Nothing}
In function, indieictly can not be changed {kji Nothing}
In function, idieictly can not be changed {kji Nothing}
In methode, indieictly can be changed its name {Joke Nothing}
In methode, indieictly can be changed its occupation {Joke driver}
Architecture
package
each folder can only has one named package, but it can be shared for many files, also for the test file
module
it is the same designed as project, but only without main function and main package. it can be used by local package, module or project, but has to be replaced
go mod edit -replace modulename=folder
- modul name is defined in the go.mod file, and also at last one package should named as it
- floder can be relative path, or absolute path in the reference package, module or project.
- can be called from gore, but first :import
project
- the same with module, with main package and main func, can be call as go run xxx.go
- can not be called from gore for now (29.07.2022)
here is also1 here is also2 here is also3 here is also4
pprof, allow cpu and mem profile
import (
"os"
"time"
"github.com/pkg/profile"
"runtime/pprof"
)
cpuFile, _ := os.Create("cpu.pprof")
pprof.StartCPUProfile(cpuFile)
defer pprof.StopCPUProfile()
defer profile.Start(profile.MemProfile, profile.ProfilePath(".")).Stop()