https://github.com/teivah/100-go-mistakes#table-of-contents
nil Map
map记得要make初始化, slice可以不用初始化!
go
func main() {
//assignment to nil map
var course map[string]string //如果不初始化,就会为nil
course["name"] = "go体系课"
}
结构体空指针
空结构体和结构体空指针可不一样
go
type Course struct {
Name string
Desc string
}
func (c *Course) String() float64 {
return c.Name + c.Desc
}
func main() {
var c *Course //无效的内存地址或空指针解引用
fmt.Println(c.String())
}
//结构体空指针 指针类型一定要初始化,否则nil
//var c *Couser
c := &Couser{} // new(Couser)
使用对循环迭代器变量的引用 - 大坑!
在 Go 中,循环迭代器变量是一个单一的变量,在每个循环迭代中取不同的值。这如果使用不当,可能会导致非预期的行为。
go
func main() {
var out []*int
for i := 0; i < 3; i++ {
out = append(out, &i)
}
fmt.Println("Values:", *out[0], *out[1], *out[2])
fmt.Println("Addresses:", out[0], out[1], out[2])
}
go
func main() {
var out []*int
//for循环的临时变量会复用
for i := 0; i < 3; i++ {
out = append(out, &i)
}
fmt.Println(out) //[0xc00000a0c8 0xc00000a0c8 0xc00000a0c8]
for _, value := range out {
fmt.Println(*value) //3 3 3
}
}
解决办法
go
func main() {
var out []*int
for i := 0; i < 3; i++ {
tmpi := i
out = append(out, &tmpi)
}
fmt.Println("Values:", *out[0], *out[1], *out[2])
fmt.Println("Addresses:", out[0], out[1], out[2])
}
原因是:在每次迭代中,我们将 i 的地址追加到 out 切片中,但由于它是同一个变量,我们实际上追加的是相同的地址,该地址最终包含分配给 i 的最后一个值。所以只需要拷贝一份,让两者脱离关联就可以了。同样的,如果这里是for循环然后启动多个goroutine, 如下:
go
package main
import (
"fmt"
"strconv"
"time"
)
func main() {
goodsID := []uint64{1, 2, 3, 4, 5}
for _, id := range goodsID {
go func() {
fmt.Println("正在查询商品:" + strconv.Itoa(int(id)))
}()
}
time.Sleep(time.Second * 5)
}
注意:这种bug在goland中一般会提醒,
常用的解决办法:
go
import (
"fmt"
"strconv"
"time"
)
func main() {
goodsID := []uint64{1, 2, 3, 4, 5}
for _, id := range goodsID {
tmp := id
go func() {
fmt.Println("正在查询商品:" + strconv.Itoa(int(tmp)))
}()
}
time.Sleep(time.Second * 5)
}
go
package main
import (
"fmt"
"strconv"
"time"
)
func main() {
goodsID := []uint64{1, 2, 3, 4, 5}
for _, id := range goodsID {
//值传递
go func(id uint64) {
fmt.Println("正在查询商品:" + strconv.Itoa(int(id)))
}(id)
}
time.Sleep(time.Second * 5)
}
go如何使用泛型
go
package main
func Add[T int | int32 | float32 | float64 | uint64](a, b T) T {
return a + b
}
// IAdd 没有泛型之前
func IAdd(a, b interface{}) interface{} {
switch a.(type) {
case int:
return a.(int) + b.(int)
case int32:
return a.(int32) + b.(int32)
case float32:
return a.(float32) + b.(float32)
}
return nil
}
func main() {
//print(Add[float32](1.2, 2.2))
t := IAdd(1, 2).(int)
print(t)
}
泛型的常见用法
go
package main
type Mymap[KEY int | string, VALUE float32 | float64] map[KEY]VALUE
type Man struct {
}
type Woman struct {
}
type Company[T Man | Woman] struct {
Name string
CEO T
}
type MyChannel[T int | string] chan T
// WowStruct 类型嵌套
type WowStruct[T string | int, S []T] struct {
A T
B S
}
func main() {
/*m:=Mymap[int,float32]{
}*/
//company := Company[Man]{
// Name: "chengpeng",
// CEO: Man{},
//}
//
//company1 := Company[Woman]{
// Name: "chengpeng",
// CEO: Man{},
//}
var c MyChannel[string]
}
泛型的错误用法
go
//错误用法1 类型参数不能单独使用
//type CommonType[T int | string] T
type CommonType[T int | string] []T
//错误用法2 无效的数组绑定 'T *int | string',必须是一个常量表达式
//type CommonType1[T *int | string] []T
type CommonType1[T interface{ *int } | string] []T
匿名接口体不支持泛型--错误用法3
go
test:= struct[t int|string] {
Name string
Age int
}{}
匿名函数不支持泛型--错误用法3
go
fn := func[T int | float64](a, b T) {
}
泛型不支持switch断言,但是可以用反射去做做法不提倡---错误用法4
go
func Add1[T int | int32 | float32 | float64 | uint64](a, b T) T {
v := reflect.ValueOf(a)
switch v.Kind() {
case reflect.Int:
print("int type")
}
return a + b
}