空白标识符 _
空白标识符作为import某个包的别名,适用场景为
1. 明确地希望适用某个包导入的副作用
2. 避免编译错误产生
package postgres
import(
"database/sql"
)
func init() {
sql.Register("Postgres", new(PostgresDriver))
}
package main
import (
"database/sql"
_ "postgres"
)
func main(){
sql.Open("postgres", "mydb")
}
上面的代码中,我们希望用到postgres包中的init函数所产生的副作用将数据库驱动注册好,同时我们的main包中没有使用任何postgres包中的成员,为了避免编译器报错,我们用_作为别名
切片
索引切片
s := []string{99:""}
声明了一个切片,有100个元素。
空切片
var s1 []int
s2 := []int{}
s3 := make([]int, 0)
声明了一个切片长度为0,容量为0
nil切片在标准库中很常用。
长度与容量
slice[i:j:k]
长度: j - i
容量: k - i
【建议】将切片的长度和容量设置为相同大小。
func main() {
var s []int
fmt.Println(len(s))
arr := [...]int{1, 2, 3, 4, 5}
sli := arr[1:3]
fmt.Println(sli)
fmt.Println(arr)
sli = append(sli, 9)
fmt.Println(sli)
fmt.Println(arr)
}
输出结果为
0
[2 3]
[1 2 3 4 5]
[2 3 9]
[1 2 3 9 5]
[2 3 10]
[1 2 3 9 5]
如果将长度和容量设为相同,可以避免很多隐晦的安全隐患。
类型和接口
类型与类型指针
package main
import (
"fmt"
)
type user struct {
name string
email string
}
func (u user) notify() {
fmt.Println("sending to:", u.name, u.email)
}
func (u *user) changeEmail(email string) {
u.email = email
}
func main() {
bill := user{name: "bill",
email: "bill@a.com",
}
bill.notify()
lisa := &user{"lisa", "lisa@a.com"}
lisa.notify() //Go在背后执行的动作:(*lisa).notify()
bill.changeEmail("bill@b.com") //(&bill).changEmail(...)
bill.notify()
lisa.changeEmail("lisa@b.com")
lisa.notify()
}
结果
sending to: bill bill@a.com
sending to: lisa lisa@a.com
sending to: bill bill@b.com
sending to: lisa lisa@b.com
方法集规则
values | methods receivers |
---|---|
T | (t T) |
*T | (t T) and (t *T) |
另一个角度:
methods receivers | values |
---|---|
(t T) | T and *T |
(t *T) | *T |
如果定义方法时类型参数的形参使用指针,那么调用方发时只能传给它指针,如果定义方发时类型参数的形参使用类型的值,那么调用方发时可以传值也可以传指针。
package main
import (
"fmt"
)
type notifier interface {
notify()
notify1()
}
type user struct {
name string
email string
}
type admin struct {
name string
email string
}
func (u *user) notify() {
fmt.Println("By pointer:", u.name, u.email)
}
func (u user) notify1() {
fmt.Println("By value:", u.name, u.email)
}
func main() {
u := user{"Alice", "alice@a.com"}
up := &u
u.notify()
u.notify1()
up.notify1()
up.notify()
sendNotification(up)
//39:18: cannot use u (type user) as type notifier in
//argument to sendNotification: user does not implement
//notifier (notify method has pointer receiver)
sendNotification(u) //编译错误
//41:19: cannot use u (type user) as type *notifier in
//argument to sendNotification1: *notifier is
//pointer to interface, not interface
sendNotification1(u) //编译错误
//43:19: cannot use up (type *user) as type *notifier
//in argument to sendNotification1: *notifier is
//pointer to interface, not interface
sendNotification1(up) //编译错误
}
func sendNotification(n notifier) {
n.notify()
}
//
func sendNotification1(n *notifier) {
(*n).notify()
}
//57:3: n.notify undefined (type *notifier is pointer to
//interface, not interface)
func sendNotification2(n *notifier) {
n.notify()
}
并发
continue / break / 标号
break 和 continue可以加标号,但二者跳转到某一个标号的后续操作不同。
race 选项
go build -race
go run -race test.go
竞争条件
原子函数
atomic.CompareAndSwapInt32(addr, old, new)
atomic.LoadInt32(addr){
atomic.StoreInt32(addr, val)
atomic.AddInt32(addr, delta)
atomic.SwapInt32(addr, new)
原子操作由底层硬件支持,效率要比mutex高
mutex
var mutex Sync.Mutex
mutex.Lock()
mutex.Unlock()
channel
读取一个channeldata, ok := <-c
有三种情况
1. c未关闭、有数据, ok == true data有值
2. c未关闭、无数据, 当前goroutine阻塞
3. c已关闭, ok == false data == 0
pits
println vs fmt.Println
println 总是要比 fmt.Println 晚输出。没搞清楚原因,怀疑
1. 缓存区不同
2. 输出前者输出到stderr, 后者输出到stdout
log
package main
import (
"io"
"io/ioutil"
"log"
"os"
)
var (
Trace *log.Logger
Info *log.Logger
Warning *log.Logger
Error *log.Logger
)
func init() {
const logfile = "errors.txt"
file, err := os.OpenFile(
logfile,
os.O_CREATE|os.O_WRONLY|os.O_APPEND,
0666)
if err != nil {
log.Fatalln("Failed to open error log file:", err)
}
Trace = log.New(ioutil.Discard,
"TRACE",
log.Ldate|log.Ltime|log.Lshortfile)
Info = log.New(os.Stdout,
"INFO",
log.Ldate|log.Ltime|log.Lshortfile)
Warning = log.New(os.Stdout,
"WARNING",
log.Ldate|log.Ltime|log.Lshortfile)
Error = log.New(io.MultiWriter(file, os.Stderr),
"ERROR",
log.Ldate|log.Ltime|log.Lshortfile)
}
func main() {
Trace.Println("tracing")
Info.Println("special infomation")
Warning.Println("be attention please")
Error.Println("something failed")
}
json
json.NewDecoder(resp.Body).Decode(&gr)
json.Unmarshal([]byte(JSON), &c) //c可以是类型,也可以是map
MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)