• 初学者学习golang可能遇到的坑


    我也是个golang初学者,刚入门的话,有些“坑”还是不好发现的。如map只是定义了然后就拿来使用,变量的值覆盖等。
    本来打算写一篇的,后面发现有人写的挺不错的,我就把里面的有些坑都记录了下,每个都试验了下,也可以防止自已遇到这些问题的时候,不知从何下手。
    参考自博客:https://www.cnblogs.com/276815076/p/8583589.html ,在此对作者表示感谢。但我没有完全照搬代码,每个代码都自已敲的。只是概念有参考。如有侵权,请告知

    1 和其它语言不太一样 ," { "不能单独成一行,每个语句也不必加上分号

    
    package main
    
    import "fmt"
    
    //错误
    func main()
    {
    	fmt.Println("Hello,World");
    }
    
    会报错:
    # command-line-arguments
    ./firstlearn.go:5:6: missing function body
    ./firstlearn.go:6:1: syntax error: unexpected semicolon or newline before {
    
     //正确
    func main(){
    	fmt.Println("Hello,World")
    }
    

    2 定义了变量,但未使用

    如果定义了变量,又没有使用则是编译不通过的
    全局变量例外
    仅仅是给变量赋值也不叫使用,仍然需要在某处使用它,比如输出出来

    package main
    
    import "fmt"
    
    var globalVal  = 10  //全局变量,定义了可以不使用
    
    //a定义了,但未使用,会报错 :a  declared and not used
    func defineNotUsed() {
    	var a int
    }
    
    //b有赋值,但是末使用。如输出
    func valueNotUsed() {
    	b := 3
    }
    
    func main(){
    	defineNotUsed()
    	valueNotUsed()
    }
    
    //正确
    package main
    
    import "fmt"
    
    var globalVal  = 10  //全局变量,定义了可以不使用
    
    func defineNotUsed() {
    	var a int
    	fmt.Println(a)
    }
    
    func valueNotUsed() {
    	b := 3
    	fmt.Println(b)
    }
    
    func main(){
    	defineNotUsed()
    	valueNotUsed()
    }
    
    

    3 变量定义赋值符(:=)对同一变量只能使用一次

    :=为变量同时定义及赋值,不可使用多次
    在result,err := show() 这种语法中要注意,防止重复赋值

    package main
    
    import "fmt"
    
    func main(){
    	a := 10
    	a := 20  //错误,a上面已经定义且赋值了,不可重复
    	fmt.Println(a)
    }
    

    //正确

    package main
    
    import "fmt"
    
    func main(){
    	a := 10
    	a = 20  // :=已经定义了,后续操作直接赋值就行
    	fmt.Println(a)
    }
    

    4 定义赋值符,只能在函数外部使用

    package main
    
    import "fmt"
    
    a :=3 //定义赋值操作符,不能在外面使用,外面只能用var定义
    func main(){
    	fmt.Println(a)
    }
    
    //正确
    package main
    
    import "fmt"
    func main(){
    	a :=3  //只能在内部使用这种操作符
    	fmt.Println(a)
    }
    

    5 struct成员变量不能使用简短操作符 := ,需要使用预定义变量

    package main
    
    import "fmt"
    
    type person struct {
    	Name string
    	Age int
    }
    
    func showPersion() (string,error){
    	return  "lc",nil
    }
    
    func main(){
    	var p person
    	p.Name,err := showPersion() //p.Name为struct的字段,不能直接使用 :=,需要用预定义实现
    	if err != nil {
    
    		return
    	}
    	fmt.Println(p)
    }
    
    // 正确
    func main(){
    	var p person
    	var err error //使用预定义
    	p.Name,err = showPersion() //这里改为直接=,不能用 :=
    	if err != nil {
    
    		return
    	}
    	fmt.Println(p)
    }
    
    

    6 小心变量被覆盖

    package main
    
    import "fmt"
    
    func change(a int){
    fmt.Println(a)
    }
    
    func main(){
    	a :=1
    	change(a)
    	{
    		a :=2   //这里 a的作用域只在函数内部, 除非用 a = 2就会改变
    		fmt.Println(a)
    	}
    	change(a)
    }
    //输出为 1 2 1
    
    

    7 显示类型无法用 nil初始化

    package main
    
    import "fmt"
    
    func main() {
    	var a []string = nil
    	var b map[string]string = nil
    	var c = nil //c不知道是什么类型,无法用nil初始化 ,a,b可以
    	_ = b
    	fmt.Println(a, b)
    }
    
    

    8 直接使用值为nil的slice和map

    slice,map刚定义时,值为nil不能直接使用

    package main
    
    import "fmt"
    
    func sliceTest() {
    	var s1 []string
    	//s1 = []string{"aa"}
    	//s1[0]= "a"
    	//s1 = append(s1,"b")
    	fmt.Println(s1[0]) //会报超出范围错误:index out of range,需要赋值或用append添加
    }
    
    func mapTest() {
    	var m map[string]string
    	m["name"] = "lc"   //会报错 ,m是值为nil的map:assignment to entry in nil map
    	fmt.Println(m["name"])
    }
    
    func main() {
    	sliceTest()
    	mapTest()
    }
    
    //正确
    package main
    
    import "fmt"
    
    func sliceTest() {
    	var s1 []string
    	s1 = []string{"aa"}
    	//s1[0]= "a"
    	
    	//s1 = append(s1,"b")
    	fmt.Println(s1[0]) 
    }
    
    func mapTest() {
    	//var m map[string]string
    	m := make(map[string]string) //用make来声明
    	m["name"] = "lc"   
    	fmt.Println(m["name"])
    }
    
    func main() {
    	sliceTest()
    	mapTest()
    }
    
    

    9 map不能像slice一样,使用cap检测容量大小

    package main
    
    import "fmt"
    
    func main() {
    	 m := make(map[string]int)
    	 m["a"] = 10
    	 fmt.Println(m,cap(m))
    }
    
    

    10 slice中,cap和len的区别

    cap容量,当超过容量时,会自动扩容
    len 实际大小

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    func main() {
    	s := make([]string,10)
    	s[0] = "a"
    	s[1] = "a"
    	s[2] = "a"
    	s[3] = "a"
    	s[4] = "a"
    	s[5] = "a"
    	s[6] = "a"
    	s[7] = "a"
    	s[8] = "a"
    	s[9] = "a"
    	s = append(s,"bbb")
    	fmt.Println(cap(s),len(s))
    }
    //输出: 20 ,11
    

    11 string类型的变量,值不能为nil

    package main
    
    import "fmt"
    
    func main() {
    	 var s string = nil
    	 if s == nil {
    	 	fmt.Println("empty string")
    	 }
    }
    
    //正确
    package main
    
    import "fmt"
    
    func main() {
    	 var s string 
    	 if s == "" {
    	 	fmt.Println("empty string")
    	 }
    }
    
    

    12 golang中数组作函数参数,默认值传递,修改的数据不会影响原始数组,如要实现,可以

    将数组以指针接受
    使用slice

    package main
    
    import "fmt"
    
    func arrayChange(a [3]int) {
    	a[2]  =3
    	fmt.Println(a)
    }
    
    func main() {
    	var a  = [...]int{3,4,5}
    
    	arrayChange(a)
    	fmt.Println(a)
    }
    //输出
    [3 4 3]
    [3 4 5]
    并不会改变原数组,
    
    可使用指针
    func arrayChange(a *[3]int) {
    	a[2]  =3
    	fmt.Println(a)
    }
    
    func main() {
    	var a  = [...]int{3,4,5}
    
    	arrayChange(&a)
    	fmt.Println(a)
    }
    或slice
    func arrayChange(a []int) {
    	a[2]  =3
    	fmt.Println(a)
    }
    
    func main() {
    	var a  = []int{3,4,5}
    
    	arrayChange(a)
    	fmt.Println(a)
    }
    

    13 混淆了slice的返回值,for会返回二个值,第一个值索引,第二个才是值

    func main() {
    	var a  = []string{"a","b","c"}
    	for  v  := range a {
    		fmt.Println(v)
    	}
    }
    //输出0 1 2 
    func main() {
    	var a  = []string{"a","b","c"}
    	for _, v  := range a {
    		fmt.Println(v)
    	}
    }
    用_去掉索引,输出 a b c
    

    ** 14 访问map未定义的key**

    通过第二个返回值来区分

    func main() {
    	a := map[string]string{"a":"aa", "b":"bb"}
    	if _,ok := a["c"]; !ok {
    		fmt.Println( "not found")
    	}
    
    

    15 strings是常量,不可更改

    转成rune slice后更改

    func main() {
    	s := "Hello"
    	//s[0] = "W"
    	fmt.Println(s)
    
    	//转成rune slice
    	sRune := []rune(s)
    	sRune[0] =  '世' //注意这里是单引号
    	sRune[1] =  '界' //注意这里是单引号
    	s = string(sRune)
    
    	fmt.Println(s,len(s))
    
    }
    

    16 字符串的长度

    len()返回的是字符的byte数量,而不是字符数
    utf8的RuneCountInString,返回字符数

    
    func main() {
    	s := "Hello兴"
    	fmt.Println(len(s))
    	fmt.Println(utf8.RuneCountInString(s))
    
    }
    

    **17 多行map,slice,array后的语句缺少 “,”号 **
    package main

    func main() {
    ​ a :=[]int{1,2} // 单行后 '}' ,尾部的 " , " 号非必须。
    ​ b :=[]int{1,2 //多行后,尾部的 ","为必须。
    ​ }
    }

    18 不导出的struct字段,无法被encode

    首字母为小写的字段为无法导出字段

    func main() {
    	 type person struct {
    	 	Name string
    	 	age int
    	 }
    
    	 p :=&person{"lc",3}
    	 e, _  := json.Marshal(p)
    	 fmt.Println(string(e))
    
    	var o person
    	json.Unmarshal(e,&o)
    	fmt.Printf("%#v
    ", o)
    }
    

    19 slice,map,array通过range来更新元素

    无法通过引用来更新元素
    使用索引访问来更新元素

    func main() {
    	//无法通过引用更新
    	a :=[]int{1,2,3}
    	for _,v := range a {
    		v = v * 10
    	}
    	fmt.Println(a)
    
    
    	//使用索引更新
    	b :=[]int{1,2,3}
    	for i,v := range b {
    		b[i] = v * 10
    	}
    	fmt.Println(b)
    } 
    

    20从slice切出新slice时,会引用原来的slice数组,造成大量的 内存使用

    不要重新切新的slice
    使用临时拷贝

    func main() {
    	s := make([]int,10)
    	fmt.Println(s,len(s),cap(s))
    
    	//从原slice中切出新slice会导致指向原来的底层数组,重新分配
    	c :=s[:3]
    	fmt.Println(c,len(c),cap(c))
    
    	//通过拷贝临时slice的数据,而不是重新切片
    	t :=make([]int,3)
    	copy(t,s)
    	fmt.Println(t,len(t),cap(t))
    }
    // 输出
    [0 0 0 0 0 0 0 0 0 0] 10 10
    [0 0 0] 3 10
    [0 0 0] 3 3
    

    21 从一个slice创建新slice时,注意“旧”数据问题

    
    func main() {
    	s1 := []int{1,2,3}
    	fmt.Println(s1,len(s1),cap(s1))
    
    	s2 :=s1[1:]
    	fmt.Println(s2,len(s2),cap(s2))
    	//以上s1,s2指向同一个slice
    
    
    	//继续往 s2追加s2
    	s2 = append(s2,4) //导致分配新的容量,和 s1没关系了
    	for i,v := range s2 {
    		s2[i] = v * 10
    	}
    
    	fmt.Println(s1,len(s1),cap(s1)) //s1指向的还是旧的slice
    	fmt.Println(s2,len(s2),cap(s2))
    
    
    }
    
  • 相关阅读:
    MySQL字符集编码相关
    Python基础(2):__doc__、文档字符串docString、help()
    Python基础(1):dir(),help()
    Python开发环境(3):使用Eclipse+PyDev插件创建Django项目
    Python开发环境(2):启动Eclipse时检测到PYTHONPATH发生改变
    使用免安装压缩包安装MySQL
    第一个Django项目:HelloWorld
    Django 2.0.3安装-压缩包方式
    Eclipse中各种编码格式及设置
    Python开发环境(1):Eclipse+PyDev插件
  • 原文地址:https://www.cnblogs.com/smartrui/p/10207738.html
Copyright © 2020-2023  润新知