Go 语言基础(5) | 青训营笔记

86 阅读4分钟

Go 的 结构体

先来一个小例子:

package main

import "fmt"

type user struct {
	name     string
	password string
}

func main() {
	a := user{name: "wang", password: "1024"}
	b := user{"wang", "1024"}
	c := user{name: "wang"}
	c.password = "1024"
	var d user
	d.name = "wang"
	d.password = "1024"

	fmt.Println(a, b, c, d)                 // {wang 1024} {wang 1024} {wang 1024} {wang 1024}
	fmt.Println(checkPassword(a, "haha"))   // false
	fmt.Println(checkPassword2(&a, "haha")) // false
}

func checkPassword(u user, password string) bool {
	return u.password == password
}

func checkPassword2(u *user, password string) bool {
	return u.password == password
}

结构体是带类型的字段的集合,初始化时可以带上结构体内成员名称赋值还可以直接按照位置来赋值,带上结构体内成员名称赋值可以只赋值一部分,其他的会被初始化成默认值。用 . 来访问结构体内成员的值。结构体也可以当成函数参数,指针用法就是原地操作,直接在原本的结构体上面动,可以避免拷贝开销。

例子运行结果如下:

{wang 1024} {wang 1024} {wang 1024} {wang 1024}
false
false

Go 的结构体方法

来个小例子:

package main

import "fmt"

type user struct {
	name     string
	password string
}

func (u user) checkPassword(password string) bool {
	return u.password == password
}

func (u *user) resetPassword(password string) {
	u.password = password
}

func main() {
	a := user{name: "wang", password: "1024"}
	a.resetPassword("2048")
	fmt.Println(a.checkPassword("2048")) // true
}

这么做有点像类的成员方法,和前文一样,有指针写法和不带指针的写法。写成类的方法这样也是用 . 来调用。

例子运行结果如下:

true

Go 的错误处理

来个小例子:

package main

import (
	"errors"
	"fmt"
)

type user struct {
	name     string
	password string
}

func findUser(users []user, name string) (v *user, err error) {
	for _, u := range users {
		if u.name == name {
			return &u, nil
		}
	}
	return nil, errors.New("not found")
}

func main() {
	u, err := findUser([]user{{"wang", "1024"}}, "wang")
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(u.name) // wang

	if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {
		fmt.Println(err) // not found
		return
	} else {
		fmt.Println(u.name)
	}
}

写惯 Java 的应该知道错误处理就是抛异常定好错误的名字然后异常处理里面写逻辑,Go 里面因为一个函数可以返回多个值(前面刚学完),有一个返回值是专门留给异常状态或者错误状态的,这样异常处理和原本的函数就可以一起写了,哪里出异常直接就能找到出问题的函数,然后直接在这里动工。(看来大佬们也不喜欢绞尽脑汁给错误起名字,然后再找对应的方法)实现的时候也不用 try ... catch ... 直接 if ... else ... 就完事了。返回多个值的时候表示错误的返回值类型就是 error,如果方法正常执行返回有实际意义的结果,返回错误的位置给一个 nil,如果出异常了,原本要返回有意义的值的地方返回 nil,错误信息用 error.New("error message") 这样来写。

因为有多个返回值,所以调用这些方法的时候接收返回值的变量也是多个。然后应该先看一看错误信息有没有,是什么。如果没有错误信息才去继续写正常的业务逻辑。

(我感觉有些人应该是不习惯这种写法,然后会整个下划线占位错误信息,处理写在另外的函数里,最后代码写的还是和以前一样。)

运行结果如下:

wang
not found

Go 的字符串操作

看个小例子:

package main

import (
	"fmt"
	"strings"
)

func main() {
	a := "hello"
	fmt.Println(strings.Contains(a, "ll"))                // true
	fmt.Println(strings.Count(a, "l"))                    // 2
	fmt.Println(strings.HasPrefix(a, "he"))               // true
	fmt.Println(strings.HasSuffix(a, "llo"))              // true
	fmt.Println(strings.Index(a, "ll"))                   // 2
	fmt.Println(strings.Join([]string{"he", "llo"}, "-")) // he-llo
	fmt.Println(strings.Repeat(a, 2))                     // hellohello
	fmt.Println(strings.Replace(a, "e", "E", -1))         // hEllo
	fmt.Println(strings.Split("a-b-c", "-"))              // [a b c]
	fmt.Println(strings.ToLower(a))                       // hello
	fmt.Println(strings.ToUpper(a))                       // HELLO
	fmt.Println(len(a))                                   // 5
	b := "你好"
	fmt.Println(len(b)) // 6
}

字符串操作在 strings 包里,常用的操作在上面已经列出来了,是否包含某一段,统计出现的次数,有没有某一段前缀,有没有某一段后缀,出现的位置,字符串拼接,字符串重复,查找替换,字符串分割,大小写转换等。注意 len() 是不需要 strings 包就能用的,对于中文字符,长度和英文字符是不一样的,受系统编码影响,或者指定用 utf8

运行结果如下:

true
2
true
true
2
he-llo
hellohello
hEllo
[a b c]
hello
HELLO
5
6