入门 参考: Go英文文档
Hello World 1 2 3 4 5 6 7 8 9 package mainimport "fmt" func main () { fmt.Print("hello world" ) }
1 2 3 4 5 6 7 8 9 C:\Users\anthony\go\src\awesomeProject\demo\main>go build demo.go C:\Users\anthony\go\src\awesomeProject\demo\main>demo.exe hello world
标准输入 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package mainimport "fmt" func main () { var name string var age int fmt.Scan(&name, &age) fmt.Println(name, age) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package mainimport "fmt" func main () { var name string var age int fmt.Scanln(&name, &age) fmt.Println(name, age) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package mainimport "fmt" func main () { var name string var age int fmt.Scanf("%s%d" , &name, &age) fmt.Println(name, age) }
1 2 3 4 5 func method_if () { reader := bufio.NewReader(os.Stdin) str, _ := reader.ReadString('\n' ) fmt.Printf("输入的值是:%s\n" , str) }
声明变量 基本类型,声明变量,不赋值,就有默认值
1 2 3 4 5 6 7 8 9 10 11 func main () { var age int = 0 fmt.Println(age) var code int fmt.Println(code) var sex bool fmt.Println(sex) }
一次性声明不同类型的变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 func main () { var age, name = 10 , "anthony" println ("可以声明不同类型,只能自动推导,不能定义类型:" , age, name) } func method6 () { var ( age int = 23 name string = "anthony" sex bool ) println ("使用集合的方式声明变量,可以声明不同类型,并且可以赋值" , age, name, sex) }
先声明,后赋值
1 2 3 4 5 6 7 8 func main () { var i int i=10 fmt.Print("i=" , i) }
类型推导
1 2 3 4 func method2 () { var name = "anthony" print ("类型推导:" , name) }
常量 常量是一个简单值的标识符,在程序运行时,不会被修改的量
出于习惯,常量用大写,不过小写也没有问题
1 2 3 4 5 6 7 8 9 10 const identifier [type ] = valueconst A string = "abc" const B = "abc" const C,D = 1 ,2 const E, F, G = 1 , false , "str"
常量用作枚举 常量可以作为枚举,常量组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func main () { const ( RED = 1 BLACK = 2 WHITE = 3 ) const ( x int = 16 y s = "abc" z ) fmt.Println(RED, BLACK, WHITE) fmt.Println("常量组中如不指定类型和初始化值,则与上一行非空常量右值相同:" , x, y, s, z) }
特殊常量:iota iota,特殊常量,可以认为是一个可以被编译器修改的常量
如果中断iota自增,则必须显式恢复。且后续自增值按行序递增
自增默认是int类型,可以自行进行显示指定类型
数字常量不会分配存储空间,无须像变量那样通过内存寻址来取值,因此无法获取地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func demo() { // 报错 //a = iota const ( a = iota //0 b //1 c //2 d = "ha" //独立值,iota += 1 e //"ha" iota += 1 f = 100 //iota +=1 g //100 iota +=1 h = iota //7,恢复计数 i //8 ) fmt.Println(a, b, c, d, e, f, g, h, i) }
字符串 1 2 3 4 5 6 7 8 func main () { name := "Hello World" for index := range name { fmt.Printf("遍历字符串:%v,%v\n" ,index,name[index]) fmt.Println(len (name[index])) } }
字符串切片 1 2 3 4 5 var response = "var r = [[\"000001\",\"HXCZHH\",\"华夏成长混合\",\"混合型-偏股\",\"HUAXIACHENGZHANGHUNHE\"]];" fmt.Println(response[8:len(response)-1]) // 打印 [["000001","HXCZHH","华夏成长混合","混合型-偏股","HUAXIACHENGZHANGHUNHE"]]
字符串数组转一维数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 func main () { response := "[1,2,3]" var arr []int if err := json.Unmarshal([]byte (response), &arr); err != nil { fmt.Println("Error:" , err) } for i:=0 ;i< len (arr);i++ { fmt.Printf("第%v行;数据是:%v\n" ,i+1 ,arr[i]) } }
字符串数组转二维数组 1 2 3 4 5 6 7 8 9 10 11 12 13 func main () { response := "[[1,2,3],[2,3,4]]" var arr [][]int if err := json.Unmarshal([]byte (response), &arr); err != nil { fmt.Println("Error:" , err) } for i:=0 ;i< len (arr);i++ { fmt.Printf("第%v行;数据是:%v\n" ,i+1 ,arr[i]) } }
字符串和数字互转 1 2 3 4 5 6 7 string 转成int :int , err := strconv.Atoi(string )string 转成int64 :int64 , err := strconv.ParseInt(string , 10 , 64 )int 转成string :string := strconv.Itoa(int )int64 转成string :string := strconv.FormatInt(int64 ,10 )
type关键字 1 2 3 4 5 6 7 8 9 10 func main () { var one int = 17 mean := float32 (one) fmt.Println(mean) }
流程控制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 func method_if () { reader := bufio.NewReader(os.Stdin) str, _ := reader.ReadString('\n' ) if str == "" { fmt.Println("空的" ) } fmt.Printf("输入的值是:%s\n" , str) } func main () { var data = make (map [string ]int ) data["a" ] = 0 data["b" ] = 1 data["c" ] = 2 for k, v := range data { fmt.Println(k, v) } if _, ok := data["a" ]; ok { fmt.Println("存在" ) } } func method_if2 () { if num := 10 ; num%2 == 0 { fmt.Println(num, "is even" ) } else { fmt.Println(num, "is odd" ) } } func method_switch () { num := 1 result := -1 switch num { case 1 : result = 1 case 2 , 3 , 4 : result = 2 default : result = -2 } fmt.Println(result) } func demo () { fmt.Print("开始" ) for i := 0 ; i <= 10 ; i++ { fmt.Print(i) } fmt.Print("结束" ) } func demo_break () { fmt.Print("开始" ) for i := 0 ; i <= 10 ; i++ { if i == 5 { break } fmt.Print(i) } fmt.Print("结束" ) } func demo_continue () { for i := 1 ; i <= 10 ; i++ { if i%2 == 0 { continue } fmt.Printf("%d " , i) } } func demo_goto () { var a = 0 LOOP: fmt.Println("===" ) for a < 10 { if a == 5 { a = a + 1 goto LOOP } fmt.Printf("a的值为 : %d\n" , a) a++ } }
值传递和引用传递 程序中使用的是值传递, 所以两个值并没有实现交互
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 func main () { var a int = 100 var b int = 200 fmt.Printf("交换前 a 的值为 : %d\n" , a ) fmt.Printf("交换前 b 的值为 : %d\n" , b ) swap(a, b) fmt.Printf("交换后 a 的值 : %d\n" , a ) fmt.Printf("交换后 b 的值 : %d\n" , b ) } func swap (x, y int ) int { var temp int temp = x x = y y = temp return temp; }
引用传递
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 func main () { var a int = 100 var b int = 200 fmt.Printf("交换前,a 的值 : %d\n" , a ) fmt.Printf("交换前,b 的值 : %d\n" , b ) swap(&a, &b) fmt.Printf("交换后,a 的值 : %d\n" , a ) fmt.Printf("交换后,b 的值 : %d\n" , b ) } func swap (x *int , y *int ) { var temp int temp = *x *x = *y *y = temp }
变量作用域 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var global int = 32 func main () { var a,b int =1 , 2 fmt.Printf("打印全局变变量:%v\n" , global) global = a+ b fmt.Printf("打印全局变变量:%v\n" , global) var global int = 3 fmt.Printf("打印全局变变量:%v\n" , global) }
匿名代码块
匿名代码块被用来定义一个局部变量 message
,该变量仅在代码块内部可见。一旦离开代码块,变量 message
就不再可见,任何尝试在代码块外部访问它都会导致编译错误。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package mainimport ( "fmt" ) func main () { fmt.Println("在main函数外部" ) { fmt.Println("在匿名代码块内部" ) message := "这是一个局部变量" fmt.Println(message) } fmt.Println("在main函数外部" ) }
数组和切片 数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 func get () { list := [5 ]int {1 ,2 ,3 ,4 ,5 } for i:=0 ;i< len (list);i++ { fmt.Println(i, "==" , list[i]) } for k, v := range my { fmt.Println(k, v) } } func create () { var numbers []int fmt.Println("新建个空数组:" ,numbers) var defaultcount [4 ]int fmt.Println("新建个指定长度的数组:" ,defaultcount) var balance = []int {1 ,2 ,3 ,4 ,5 } fmt.Println("新建个不指定长度的数组:" ,balance) d := [...] int {1 ,2 ,3 ,4 ,5 } fmt.Println("新建个指定位置的数组:" ,d) e := [5 ] int {4 : 100 } fmt.Println("新建个指定位置的数组:" ,e) f := [...] int {0 : 1 , 4 : 1 , 9 : 1 } fmt.Println("新建个指定位置的数组:" ,f) }
切片
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 func define () { var identifier []int fmt.Println("空数组" , identifier) var slice1 []int = make ([]int , 10 ) fmt.Println("切片" , slice1) i0 := slice1[0 ] i1 := slice1[1 ] i2 := slice1[2 ] i3 := slice1[3 ] fmt.Println("通过索引,获取值" , i0, i1, i2, i3) for i := 0 ; i < len (slice1); i++ { slice1[i] = i } fmt.Println("修改完切片::" , slice1) fmt.Println("获取切片区间1:" , slice1[0 :2 ]) fmt.Println("获取切片区间2:" , slice1[5 :]) fmt.Println("获取切片区间3:" , slice1[:5 ]) slice1 = append (slice1, 10 , 11 , 12 ) fmt.Println("追加完切片::" , slice1) slice2 := make ([]int , len (slice1), cap (slice1)*2 ) fmt.Println("创建个容量是原来容量两位的数组:" , slice2) number := copy (slice2, slice1) fmt.Printf("slice:%v,slice2:%v,number:%v:" , slice1, slice2, number) }
Map 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 func main () { keyvale := make (map [string ]string ) keyvale["k1" ] = "v1" keyvale["k2" ] = "v2" keyvale["k3" ] = "v3" keyvale["k4" ] = "v4" for key := range keyvale { fmt.Println("循环遍历:" , key, keyvale[key]) } delete (keyvale, "k1" ) for key := range keyvale { fmt.Println("删除值之后,循环遍历:" , key, keyvale[key]) } _, ok := keyvale["United States" ] if ok { fmt.Printf("存在\n" ) } else { fmt.Printf("不存在\n" ) } m := make (map [string ]int ) m["a" ] = 1 x, ok := m["b" ] fmt.Println("key不存在时,会有默认值" , x, ok) fmt.Println("map长度" , len (m)) mymap := map [string ]int { "steven" : 12000 , "anthony" : 15000 , } mymap["mike" ] = 9000 fmt.Printf("原来的数据:%v\n" , mymap) newmymap := mymap newmymap["anthony" ] = 50000 fmt.Printf("改变后,原来的数据:%v\n" , mymap) fmt.Printf("改变后,新的的数据:%v\n" , newmymap) }
函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 func max (num1, num2 int ) int { var result int if (num1 > num2) { result = num1 } else { result = num2 } return result } func main () { var a int = 100 var b int = 200 var ret int ret = max(a, b) fmt.Printf( "最大值是 : %d\n" , ret ) }
函数返回多个值 1 2 3 4 5 6 7 8 9 10 func swap (x, y string ) (string , string ) { return y, x } func main () { a, b := swap("Google" , "Runoob" ) fmt.Println(a, b) }
函数返回值的命名 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 type studen struct { name string age int } func test4 () { stu := test4_return() fmt.Println(stu.name) } func test4_return () (stu *studen) { stu = &studen{ "anthony" , 13 , } return }
函数作为实参 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 type fb func (x int ) int func main () { myfunc := func (x int ) int { return x } fmt.Println(myfunc(3 )) demo(2 , myfunc) } func demo (x int ,myfb fb) { myfb(x) }
函数作为实参-回调 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func main () { test2() } func test2 () { group("/system1" , myfunc) group("/system2" , func (path2 string ) { fmt.Printf("回调Path:%v \n" , path2) }) } func group (path string , group func (path2 string ) ) { fmt.Printf("手动实现 \n" + path) group(path) } func myfunc (path2 string ) { fmt.Printf("回调Path:%v \n" , path2) }
函数作为参数-interface{} 1 2 3 4 5 6 7 8 func test3 () { test3_object(myfunc) } func test3_object (object interface {}) { a := object fmt.Printf("什么鬼:%v" , a) }
指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 func main () { var a int = 20 var ip *int ip = &a fmt.Printf("a 变量的地址是: %x\n" , &a ) fmt.Printf("ip 变量储存的指针地址: %x\n" , ip ) fmt.Printf("*ip 变量的值: %d\n" , *ip ) } func main () { var a int = 100 var b int = 200 swap(&a, &b); fmt.Printf("交换后 a 的值 : %d\n" , a ) fmt.Printf("交换后 b 的值 : %d\n" , b ) } func swap (x *int , y *int ) { *x, *y = *y, *x }
结构体 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 func main () { var s people s.age = 1 fmt.Println(s) ming := new (people) ming.name = "xiao ming" ming.age = 18 fmt.Println(ming) ming2 := &people{"xiao ming" , 18 } fmt.Println(ming2) }
第二第三种返回指针的声明形式,在我们需要修改他的值的时候,其实应该使用的方式是:
1 (*ming).name = "xiao wang"
也就是说,对于指针类型的数值,应该要先用*
取值,然后再修改。
但是,在Golang中,可以省略这一步骤,直接使用ming.name = "xiao wang"
。
方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 type Vertex struct { X, Y float64 } func (v Vertex) test1() { v.X++ v.Y++ } func (v *Vertex) test2() { v.X++ v.Y++ } func main () { v1 := Vertex{1 , 1 } v2 := &Vertex{1 , 1 } v1.test1() v2.test2() fmt.Println(v1) fmt.Println(v2) }
test1
和test2
,他们唯一的区别就是方法名前面的接收者不同,一个是指针类型的,一个是值类型的。
测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 type Vertex struct { X, Y float64 } func (v Vertex) test1(){ fmt.Printf("在方法中的v的地址为:%p\n" , &v) v.X++; v.Y++; } func main () { v1 := Vertex{1 , 1 } fmt.Printf("自己定义的v1内存地址为:%p\n" , &v1) v1.test1() }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 type Vertex struct { X, Y float64 } func (v *Vertex) test2(){ fmt.Printf("在方法中的v的地址为:%p\n" , v) v.X++; v.Y++; } func main () { v1 := &Vertex{1 , 1 } fmt.Printf("自己定义的v1内存地址为:%p\n" , v1) v1.test2() }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 type Books struct { title string author string subject string book_id int } type Library struct { Books address string } func main () { var book Books book.title = "Go 语言" book.author = "www.runoob.com" book.subject = "Go 语言教程" book.book_id = 6495407 mark := Library{Books{"Go 语言" ,"www.runoob.com" ,"Go 语言教程" ,6495407 },"广东" } fmt.Println("His name is " , mark.title) fmt.Println("His age is " , mark.author) fmt.Println("His weight is " , mark.subject) fmt.Println("His speciality is " , mark.address) mark.title = "AI" fmt.Println("Mark changed his speciality" ) fmt.Println("His speciality is " , mark.title) }
面向对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 type PersonMethod interface { show() } type Person struct { name string age int } func (person *Person) setAge(age int ) { person.age = age } func (person Person) show() { fmt.Printf("Person类打印SHOW:%v\n" , person) } func show2 (person PersonMethod) { person.show() } type Student struct { Person level string } func (student *Student) show() { fmt.Printf("Student类打印SHOW:%v\n" , student) } type Teacher struct { Person price int } func (teacher Teacher) show() { fmt.Printf("Teacher类打印SHOW:%v\n" , teacher) } func main () { anthony := Person{"anthony" , 25 } fmt.Printf("anthony信息:%v\n" , anthony) anthony.setAge(12 ) fmt.Printf("anthony信息:%v\n" , anthony) anthony2 := Person{} fmt.Printf("anthony2信息:%v\n" , anthony2) anthony2.age = 26 anthony2.name = "anthony2" fmt.Printf("anthony2信息:%v\n" , anthony2) student := Student{} student.level = "小学生" student.name = "anthony" student.age = 23 fmt.Printf("学生继承类:%v\n" , student) teacher := Teacher{price: 12 , Person: Person{name: "li teacher" , age: 56 }} fmt.Printf("老师继承类:%v\n" , teacher) show2(&student) show2(teacher) show2(anthony) }
创建对象的方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 c := Car{} c1 := new (Car) c2 := &Car{} c3 := &Car{"红色" , "1.2L" } c4 := &Car{color: "红色" } c5 := Car{color: "红色" } func NewCar (color,size string ) *Car { return &Car{color,size} }
接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 type Phone interface { call() } type NokiaPhone struct {} func (nokiaPhone NokiaPhone) call() { fmt.Println("I am Nokia, I can call you!" ) } type IPhone struct {} func (iPhone IPhone) call() { fmt.Println("I am iPhone, I can call you!" ) } func main () { var phone Phone phone = new (NokiaPhone) phone.call() phone = new (IPhone) phone.call() }
错误处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 type DivErr struct { etype int v1 int v2 int } func (divErr DivErr) Error() string { if 0 == divErr.etype { return "除零错误" }else { return "其他未知错误" } } func div (a int , b int ) (int ,*DivErr) { if b == 0 { return 0 ,&DivErr{0 ,a,b} } else { return a / b, nil } } func main () { v,r :=div(100 ,2 ) if nil !=r{ fmt.Println("(1)fail:" ,r) }else { fmt.Println("(1)succeed:" ,v) } v,r =div(100 ,0 ) if nil !=r{ fmt.Println("(2)fail:" ,r) }else { fmt.Println("(2)succeed:" ,v) } }
GoRoutine 1 2 3 4 5 6 7 8 9 10 11 func main () { go say("world" ) say("hello" ) } func say (s string ) { for i := 0 ; i < 5 ; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) } }
Channel 1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport "fmt" func main () { messages := make (chan string ) go func () { messages <- "ping" }() msg := <-messages fmt.Println(msg) }
包管理器 modules go mod常用命令
命令
说明
download
download modules to local cache(下载依赖包)
edit
edit go.mod from tools or scripts(编辑go.mod
graph
print module requirement graph (打印模块依赖图)
init
initialize new module in current directory(在当前目录初始化mod)
tidy
add missing and remove unused modules(拉取缺少的模块,移除不用的模块)
vendor
make vendored copy of dependencies(将依赖复制到vendor下)
verify
verify dependencies have expected content (验证依赖是否正确)
why
explain why packages or modules are needed(解释为什么需要依赖)
示例一:创建一个新项目
在GOPATH 目录之外
新建一个目录,并使用go mod init
初始化生成go.mod
文件
go.mod文件一旦创建后,它的内容将会被go toolchain全面掌控。go toolchain会在各类命令执行时,比如go get、go build、go mod等修改和维护go.mod文件。
go.mod 提供了module
, require
、replace
和exclude
四个命令
module
语句指定包的名字(路径)
require
语句指定的依赖项模块
replace
语句可以替换依赖项模块
exclude
语句可以忽略依赖项模块
添加依赖
新建一个 server.go 文件,写入以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package mainimport ( "net/http" "github.com/labstack/echo" ) func main () { e := echo.New() e.GET("/" , func (c echo.Context) error { return c.String(http.StatusOK, "Hello, World!" ) }) e.Logger.Fatal(e.Start(":1323" )) }
执行 go run server.go
运行代码会发现 go mod 会自动查找依赖自动下载:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 $ go run server.go go : finding github.com/labstack/echo v3.3 .10 +incompatiblego : downloading github.com/labstack/echo v3.3 .10 +incompatiblego : extracting github.com/labstack/echo v3.3 .10 +incompatiblego : finding github.com/labstack/gommon/color latestgo : finding github.com/labstack/gommon/log latestgo : finding github.com/labstack/gommon v0.2 .8 # 此处省略很多行 ... ____ __ / __/___/ / ___ / _ /___/\__/_ High performance, minimalist Go web framework https: ____________________________________O/_______ O\ ⇨ http server started on [::]:1323 复制代码
现在查看go.mod 内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $ cat go .mod module hello go 1.12 require ( github.com/labstack/echo v3.3 .10 +incompatible github.com/labstack/gommon v0.2 .8 github.com/mattn/go -colorable v0.1 .1 github.com/mattn/go -isatty v0.0 .7 github.com/valyala/fasttemplate v1.0 .0 golang.org/x/crypto v0.0 .0 -20190313024323 -a1f597ede03a ) 复制代码
go module 安装 package 的原則是先拉最新的 release tag,若无tag则拉最新的commit,详见 Modules官方介绍 。 go 会自动生成一个 go.sum 文件来记录 dependency tree:
1 2 3 4 5 6 7 8 9 $ cat go .sum github.com/labstack/echo v3.3 .10 +incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= github.com/labstack/echo v3.3 .10 +incompatible/go .mod h1:0 INS7j/VjnFxD4E2wkz67b8cVwCLbBmJyDaka6Cmk1s= github.com/labstack/gommon v0.2 .8 h1:JvRqmeZcfrHC5u6uVleB4NxxNbzx6gpbJiQknDbKQu0= github.com/labstack/gommon v0.2 .8 /go .mod h1:/tj9csK2iPSBvn+3 NLM9e52usepMtrd5ilFYA+wQNJ4= github.com/mattn/go -colorable v0.1 .1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg= github.com/mattn/go -colorable v0.1 .1 /go .mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= ... 省略很多行 复制代码
再次执行脚本 go run server.go
发现跳过了检查并安装依赖的步骤。
可以使用命令 go list -m -u all
来检查可以升级的package,使用go get -u need-upgrade-package
升级后会将新的依赖版本更新到go.mod * 也可以使用 go get -u
升级所有依赖
go get 升级
运行 go get -u 将会升级到最新的次要版本或者修订版本(x.y.z, z是修订版本号, y是次要版本号)
运行 go get -u=patch 将会升级到最新的修订版本
运行 go get package@version 将会升级到指定的版本号version
运行go get如果有版本的更改,那么go.mod文件也会更改
示例二:改造现有项目(helloword) 项目目录为:
1 2 3 4 5 6 7 $ tree . ├── api │ └── apis.go └── server.go 1 directory, 2 files
server.go 源码为:
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport ( api "./api" "github.com/labstack/echo" ) func main () { e := echo.New() e.GET("/" , api.HelloWorld) e.Logger.Fatal(e.Start(":1323" )) } 复制代码
api/apis.go 源码为:
1 2 3 4 5 6 7 8 9 10 11 12 package apiimport ( "net/http" "github.com/labstack/echo" ) func HelloWorld (c echo.Context) error { return c.JSON(http.StatusOK, "hello world" ) } 复制代码
使用 go mod init ***
初始化go.mod
1 2 3 $ go mod init helloworld go: creating new go.mod: module helloworld 复制代码
运行 go run server.go
1 2 3 4 5 6 go: finding github.com/labstack/gommon/color latest go: finding github.com/labstack/gommon/log latest go: finding golang.org/x/crypto/acme/autocert latest go: finding golang.org/x/crypto/acme latest go: finding golang.org/x/crypto latest build command-line-arguments: cannot find module for path _/home/gs/helloworld/api
首先还是会查找并下载安装依赖,然后运行脚本 server.go
,这里会抛出一个错误:
1 build command-line-arguments: cannot find module for path _/home/gs/helloworld/api
但是go.mod
已经更新:
1 2 3 4 5 6 7 8 9 10 11 12 13 $ cat go.mod module helloworld go 1.12 require ( github.com/labstack/echo v3.3.10+incompatible // indirect github.com/labstack/gommon v0.2.8 // indirect github.com/mattn/go-colorable v0.1.1 // indirect github.com/mattn/go-isatty v0.0.7 // indirect github.com/valyala/fasttemplate v1.0.0 // indirect golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a // indirect )
那为什么会抛出这个错误呢? 这是因为 server.go 中使用 internal package 的方法跟以前已经不同了,由于 go.mod会扫描同工作目录下所有 package 并且变更引入方法
,必须将 helloworld当成路径的前缀,也就是需要写成 import helloworld/api,以往 GOPATH/dep 模式允许的 import ./api 已经失效,详情可以查看这个 issue 。
更新旧的package import 方式
所以server.go 需要改写成:
1 2 3 4 5 6 7 8 9 10 11 12 13 package main import ( api "helloworld/api" // 这是更新后的引入方法 "github.com/labstack/echo" ) func main() { e := echo.New() e.GET("/", api.HelloWorld) e.Logger.Fatal(e.Start(":1323")) } 复制代码
一个小坑
:开始在golang1.11 下使用go mod 遇到过 go build github.com/valyala/fasttemplate: module requires go 1.12
这种错误 ,遇到类似这种需要升级到1.12 的问题,直接升级golang1.12 就好了。幸亏是在1.12 发布后才尝试的go mod
🤷♂️
网络编程 最简单的网络编程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport ( "io" "log" "net/http" ) func HelloServer (w http.ResponseWriter, req *http.Request) { io.WriteString(w, "hello, world!\n" ) } func main () { http.HandleFunc("/hello" , HelloServer) err := http.ListenAndServeTLS(":8080" , "cert.pem" , "key.pem" , nil ) if err != nil { log.Fatal("ListenAndServe: " , err) } }
TLS服务端和客户端 serve.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package mainimport ( "bufio" "crypto/tls" "log" "net" ) func main () { log.SetFlags(log.Lshortfile) key, err := tls.LoadX509KeyPair("cert.pem" , "key.pem" ) if err != nil { log.Println(err) return } config := &tls.Config{Certificates: []tls.Certificate{key}} ln, err := tls.Listen("tcp" , ":8000" , config) if err != nil { log.Println(err) return } defer ln.Close() for { conn, err := ln.Accept() if err != nil { log.Println(err) continue } go handleConnection(conn) } } func handleConnection (conn net.Conn) { defer conn.Close() r := bufio.NewReader(conn) for { msg, err := r.ReadString('\n' ) if err != nil { log.Println(err) return } println (msg) n, err := conn.Write([]byte ("world\n" )) if err != nil { log.Println(n, err) return } } }
clinet.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package mainimport ( "crypto/tls" "log" ) func main () { log.SetFlags(log.Lshortfile) conf := &tls.Config{ InsecureSkipVerify: true , } conn, err := tls.Dial("tcp" , "127.0.0.1:8000" , conf) if err != nil { log.Println(err) return } defer conn.Close() n, err := conn.Write([]byte ("hello\n" )) if err != nil { log.Println(n, err) return } buf := make ([]byte , 100 ) n, err = conn.Read(buf) if err != nil { log.Println(n, err) return } println (string (buf[:n])) }
标准库 flag 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 import ( "errors" "flag" "fmt" "strings" "time" ) func main () { demo2() } func demo1 () { var nFlag = flag.String("name" , "anthony" , "help message for flag n" ) flag.Parse() fmt.Println(*nFlag) } func demo2 () { var name string flag.StringVar(&name, "name" , "anthonyyang" , "help message for flag n" ) flag.Parse() fmt.Println(name) } func demo3 () { var intervalFlag mystring flag.Var(&intervalFlag, "anthonyyang" , "name" ) fmt.Println(intervalFlag) } type mystring []time.Durationfunc (i *mystring) String() string { return fmt.Sprint() } func (i *mystring) Set(value string ) error { if len (*i) > 0 { return errors.New("interval flag already set" ) } for _, dt := range strings.Split(value, "," ) { duration, err := time.ParseDuration(dt) if err != nil { return err } *i = append (*i, duration) } return nil }
参考
log io io.Reader 对于要用作读取器的类型,它必须实现 io.Reader
接口的唯一一个方法 Read(p []byte)
。换句话说,只要实现了 Read(p []byte)
,那它就是一个读取器。
1 2 3 type Reader interface { Read(p []byte ) (n int , err error ) }
Read()
方法有两个返回值,一个是读取到的字节数,一个是发生错误时的错误。同时,如果资源内容已全部读取完毕,应该返回 io.EOF
错误。
io.Copy sync sync.WaitGroup 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import ( "fmt" ) func main () { go func () { fmt.Println("Goroutine 1" ) }() go func () { fmt.Println("Goroutine 2" ) }() }
解决办法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import ( "fmt" "time" ) func main () { go func () { fmt.Println("Goroutine 1" ) }() go func () { fmt.Println("Goroutine 2" ) }() time.Sleep(time.Second * 1 ) }
问题来了,不确定协程到底要用运行多长时间,比如协程需要用到2s,这里main
等待1s,就不够用了
解决办法:用管道
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import ( "fmt" ) func main () { ch := make (chan struct {}) count := 2 go func () { fmt.Println("Goroutine 1" ) ch <- struct {}{} }() go func () { fmt.Println("Goroutine 2" ) ch <- struct {}{} }() for range ch { count-- if count == 0 { close (ch) } } }
虽然比较完美的方案,但是Go提供了更简单的方法——使用sync.WaitGroup
WaitGroup顾名思义,就是用来等待一组操作完成的。
WaitGroup内部实现了一个计数器,用来记录未完成的操作个数,它提供了三个方法,Add()用来添加计数。
Done()用来在操作结束时调用,使计数减一。Wait()用来等待所有的操作结束,即计数变为0,该函数会在计数不为0时等待,在计数为0时立即返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import ( "fmt" "sync" ) func main () { var wg sync.WaitGroup wg.Add(2 ) go func () { fmt.Println("Goroutine 1" ) wg.Done() }() go func () { fmt.Println("Goroutine 2" ) wg.Done() }() wg.Wait() }
net http Get 不带参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func main () { resp, err := http.Get("http://fund.eastmoney.com/js/fundcode_search.js" ) if err != nil { fmt.Println(err) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) fmt.Println(string (body)) fmt.Println(resp.StatusCode) if resp.StatusCode == 200 { fmt.Println("ok" ) parse(string (body)) } }
net.Listen Listen通知本地网络地址。
网络必须是“tcp”,“tcp4”,“tcp6”,“unix”或“unixpacket”。
对于TCP网络,如果地址参数中的主机为空或文字未指定的IP地址,则Listen会监听本地系统的所有可用单播和任播IP地址。
要仅使用IPv4,请使用网络“tcp4”。
该地址可以使用主机名称,但不建议这样做,因为它将为主机的至多一个IP地址创建一个监听器。
如果地址参数中的端口为空或“0”,如“127.0.0.1:”或“:: 1:0”中所示,则会自动选择一个端口号。Listener的Addr方法可用于发现所选端口。
1 2 3 4 5 6 7 8 9 10 for retry := 0; retry < 16; retry++ { l, err := net.Listen("tcp", host+":0") if err != nil { continue } defer l.Close() _, port, err := net.SplitHostPort(l.Addr().String()) p, err := strconv.ParseInt(port, 10, 32) return int(p) }
net.Conn.SetReadDeadline 设置超时时间
net.Conn
为Deadline提供了多个方法Set[Read|Write]Deadline(time.Time)
。
Deadline是一个绝对时间值,当到达这个时间的时候,所有的 I/O 操作都会失败,返回超时(timeout)错误。
OS Stat 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package mainimport ( "fmt" "os" ) func main () { fileinfo, err := os.Stat(`C:\Users\Administrator\Desktop\UninstallTool.zip` ) if err != nil { panic (err) } fmt.Println(fileinfo.Name()) fmt.Println(fileinfo.IsDir()) fmt.Println(fileinfo.ModTime()) fmt.Println(fileinfo.Mode()) fmt.Println(fileinfo.Size()) fmt.Println(fileinfo.Sys()) } type FileInfo interface { Name() string Size() int64 Mode() FileMode ModTime() time.Time IsDir() bool Sys() interface {} }
IsExist 返回一个布尔值说明该错误是否表示一个文件或目录已经存在。ErrExist和一些系统调用错误会使它返回真。
1 2 3 4 5 6 7 8 9 10 func check () bool { stat, err := os.Stat("/Users/anthony/Desktop/go-testt/go.mod" ) if err != nil { if os.IsExist(err) { return true } return false } return true }
Text/template html模板 https://pkg.go.dev/text/template
Gin Demo 1 2 3 4 5 6 7 8 9 10 11 package mainimport "github.com/gin-gonic/gin" func main () { r := gin.Default() r.GET("/" , func (c *gin.Context) { c.String(200 , "Hello, Geektutu" ) }) r.Run() }
路由 基本使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 router := gin.Default() router.GET("/Get" , getting) router.POST("/Post" , posting) router.PUT("/Put" , putting) router.DELETE("/Delete" , deleting) router.Run() r.GET("/" , func (c *gin.Context) { c.String(http.StatusOK, "Who are you?" ) }) r.POST("createApi" , CreateApi) func CreateApi (c *gin.Context) { }
路由分组
1 2 3 4 5 6 7 8 9 10 11 12 13 v1 := r.Group("/v1" ) { v1.GET("/hello" , sayHello) v1.GET("/world" , sayWorld) } v2 := r.Group("/v2" ) { v2.GET("/hello" , sayHello) v2.GET("/world" , sayWorld) } r.Run(":8080" )
大量路由实现
建立routers
包,将不同模块拆分到多个go文件
每个文件提供一个方法
,该方法注册实现所有的路由
之后main方法在调用文件的方法实现注册
第一种实现方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 func LoadRouter (e *gin.Engine) { e.Group("v1" ) { v1.GET("/post" , postHandler) v1.GET("/get" , getHandler) } ... } func main () { r := gin.Default() routers.LoadRouter(r) routers.LoadRouterXXX(r) r.Run() }
第二种方式
1 像vue一样注册路由,没看懂项目 为啥要注册很多次
获取参数 获取body的数据,并绑定实体类
1 2 3 4 5 6 7 8 9 10 func (b *BaseApi) Login(c *gin.Context) { var l systemReq.Login err := c.ShouldBindJSON(&l) key := c.ClientIP() }
响应处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 r.GET("/someJSON" , func (c *gin.Context) { c.JSON(200 , gin.H{ "message" : "Json" , "status" : 200 , }) }) r.GET("/someXML" , func (c *gin.Context) { c.XML(200 , gin.H{"message" : "abc" }) }) r.GET("/someYAML" , func (c *gin.Context) { c.YAML(200 , gin.H{"name" : "zhangsan" }) }) r.GET("/someProtoBuf" , func (c *gin.Context) { reps := []int64 {1 , 2 } data := &protoexample.Test{ Reps: reps, } c.ProtoBuf(200 , data) })
中间件 中间件分为:全局中间件 和 路由中间件 ,区别在于前者会作用于所有路由。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func BasicAuth (accounts Accounts) HandlerFunc func BasicAuthForRealm (accounts Accounts, realm string ) HandlerFuncfunc Bind (val interface {}) HandlerFunc func ErrorLogger () HandlerFunc func ErrorLoggerT (typ ErrorType) HandlerFunc func Logger () HandlerFunc func LoggerWithConfig (conf LoggerConfig) HandlerFuncfunc LoggerWithFormatter (f LogFormatter) HandlerFuncfunc LoggerWithWriter (out io.Writer, notlogged ...string ) HandlerFuncfunc Recovery () HandlerFuncfunc RecoveryWithWriter (out io.Writer) HandlerFuncfunc WrapF (f http.HandlerFunc) HandlerFunc func WrapH (h http.Handler) HandlerFunc
作用范围
1 2 3 4 5 6 7 8 9 10 11 12 13 14 r.Use(gin.Logger()) r.Use(gin.Recovery()) r.GET("/benchmark" , MyBenchLogger(), benchEndpoint) authorized := r.Group("/" ) authorized.Use(AuthRequired()) { authorized.POST("/login" , loginEndpoint) authorized.POST("/submit" , submitEndpoint) }
自定义中间件
1 2 3 4 5 6 7 8 9 10 11 12 func Logger () gin.HandlerFunc { return func (c *gin.Context) { t := time.Now() c.Set("geektutu" , "1111" ) c.Next() latency := time.Since(t) log.Print(latency) } }
控制中间件
中间件控制的方法 gin提供了两个函数Abort()
和Next()
,二者区别在于:
next()函数会跳过当前中间件中next()后的逻辑,当下一个中间件执行完成后 再执行剩余的逻辑
abort()函数执行终止当前中间件以后的中间件执行,但是会执行当前中间件的后续逻辑
举例子更好理解:
我们注册中间件顺序为m1
、m2
、m3
,如果采用next()
:
执行顺序就是
m1的next()前面
、m2的next()前面
、m3的next()前面
、
业务逻辑
m3的next()后续
、m2的next()后续
、m1的next()后续
。
那如果m2
中间调用了Abort()
,则m3
和业务逻辑
不会执行,只会执行m2的next()后续
、m1的next()后续
。
Gorm CRUD 接口 创建 创建记录 1 2 3 4 5 6 7 8 user := User{Name: "Jinzhu" , Age: 18 , Birthday: time.Now()} result := db.Create(&user) user.ID result.Error result.RowsAffected
用指定的字段创建记录 创建记录并更新给出的字段。
1 2 3 db.Select("Name" , "Age" , "CreatedAt" ).Create(&user)
创建一个记录且一同忽略传递给略去的字段值
1 2 db.Omit("Name" , "Age" , "CreatedAt" ).Create(&user)
批量插入 1 2 3 4 5 6 7 8 9 10 11 var users = []User{{Name: "jinzhu1" }, {Name: "jinzhu2" }, {Name: "jinzhu3" }}db.Create(&users) for _, user := range users { user.ID } var users = []User{{name: "jinzhu_1" }, ...., {Name: "jinzhu_10000" }}db.CreateInBatches(users, 100 )
创建钩子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 type SysMenu struct { gorm.Model Title string ParentId int Path string AlwaysShow int Component string Hidden int Icon string NoCache string Name string Redirect string IsButton int Ajax string } func (s *SysMenu) BeforeSave(tx *gorm.DB) (err error ) { fmt.Println("BeforeSave" ) return } func (s *SysMenu) BeforeCreate(tx *gorm.DB) (err error ) { fmt.Println("BeforeCreate" ) return } func (s *SysMenu) AfterCreate(tx *gorm.DB) (err error ) { fmt.Println("AfterCreate" ) return } func (s *SysMenu) AfterSave(tx *gorm.DB) (err error ) { fmt.Println("AfterSave" ) return }
根据 Map 创建 1 2 3 4 5 6 7 8 9 db.Model(&User{}).Create(map [string ]interface {}{ "Name" : "jinzhu" , "Age" : 18 , }) db.Model(&User{}).Create([]map [string ]interface {}{ {"Name" : "jinzhu_1" , "Age" : 18 }, {"Name" : "jinzhu_2" , "Age" : 20 }, })
默认值 1 2 3 4 5 type User struct { ID int64 Name string `gorm:"default:galeone"` Age int64 `gorm:"default:18"` }
Cobra 简介 cobra 是一个命令行程序库,可以用来编写命令行程序。
快速入门 安装包
1 $ go get github.com/spf13/cobra/cobra
文件目录
1 2 3 4 5 - my-go-test - cmd - version - root main.go
main.go
1 2 3 4 5 import "go-test/cmd" func main () { cmd.Execute() }
version.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package cmdimport ( "fmt" "github.com/spf13/cobra" ) var versionCmd = &cobra.Command{ Use: "version" , Short: "显示计算器版本" , Long: "显示计算器版本" , Run: func (cmd *cobra.Command, args []string ) { fmt.Println("anthony's jsq version 0.0.1" ) }, } func init () { rootCmd.AddCommand(versionCmd) }
root.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import ( "fmt" "github.com/spf13/cobra" ) var rootCmd = &cobra.Command{ Use: "jsq" , Short: "计算器" , Long: `anthony写的计算器` , Run: func (cmd *cobra.Command, args []string ) { fmt.Println("anthony写的计算器,请使用 ./jsq -h" ) }, } func Execute () { rootCmd.Execute() }
打包运行 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 ➜ go-testt go build -o jsq ➜ go-testt ./jsq anthony写的计算器,请使用 ./jsq -h ➜ go-testt ./jsq -help Error: unknown shorthand flag: 'e' in -elp Usage: jsq [flags] jsq [command ] Available Commands: completion Generate the autocompletion script for the specified shell help Help about any command version 显示计算器版本 Flags: -h, --help help for jsq Use "jsq [command] --help" for more information about a command . ➜ go-testt ./jsq version -h 显示计算器版本 Usage: jsq version [flags] Flags: -h, --help help for version
包成window包
库 Yaml解析库
测试文件yaml
1 2 3 4 5 6 7 mysql: url: 127.0 .0 .1 port: 3306 redis: host: 127.0 .0 .1 port: 6379
将 yaml
文件的数据转成自定义的结构体或 Map
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import ( "fmt" "gopkg.in/yaml.v3" "os" ) type Config struct { Mysql Mysql `json:"mysql"` Redis Redis `json:"redis"` } type Mysql struct { Url string Port int } type Redis struct { Host string Port int } func main () { dataBytes, err := os.ReadFile("test.yaml" ) if err != nil { fmt.Println("读取文件失败:" , err) return } fmt.Println("yaml 文件的内容: \n" , string (dataBytes)) config := Config{} err = yaml.Unmarshal(dataBytes, &config) if err != nil { fmt.Println("解析 yaml 文件失败:" , err) return } fmt.Printf("config → %+v\n" , config) mp := make (map [string ]any, 2 ) err = yaml.Unmarshal(dataBytes, mp) if err != nil { fmt.Println("解析 yaml 文件失败:" , err) return } fmt.Printf("map → %+v" , config) }
yaml解析库 1 go get github.com/spf13/viper
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import ( "fmt" "github.com/spf13/viper" ) func main () { viper.SetConfigName("test" ) viper.SetConfigType("yaml" ) viper.AddConfigPath("./config" ) err := viper.ReadInConfig() if err != nil { panic (fmt.Errorf("fatal error config file: %w" , err)) } fmt.Println(viper.Get("mysql" )) fmt.Println(viper.Get("mysql.url" )) }
笔记 字符串互转byte 1 2 3 4 s1 := "hello" b := []byte (s1) s2 := string (b)
创建对象的方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 type Car struct { color string size string } c := Car{} c1 := new (Car) c2 := &Car{} c3 := &Car{"红色" , "1.2L" } c4 := &Car{color: "红色" } c5 := Car{color: "红色" } func NewCar (color,size string ) *Car { return &Car{color,size} }
报错 1.debug call has arguments but no formatting directives
打印/日志 格式解析错误,错误的原因就是没加%v
2.取消代理
1 2 3 4 5 6 go env GOPROXY='https://goproxy.cn,direct' # 这个是国内的代理,在国外不容易下载依赖 ➜ server git:(main) ✗ go env -u GOPROXY # 取消代理,使用默认的配置 ➜ server git:(main) ✗ go env GOPROXY='https://proxy.golang.org,direct'