原创

Go语言类型断言、类型选择、指针、数组的问题以及生成二进制可执行文件去部署项目

温馨提示:
本文最后更新于 2020年08月30日,已超过 1,545 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

类型断言

类型断言 提供了访问接口值底层具体值的方式。

t := i.(T)

该语句断言接口值 i 保存了具体类型 T,并将其底层类型为 T 的值赋予变量 t

i 并未保存 T 类型的值,该语句就会触发一个panic错误。

为了 判断 一个接口值是否保存了一个特定的类型,类型断言可返回两个值:其底层值以及一个报告断言是否成功的布尔值。

t, ok := i.(T)

i 保存了一个 T,那么 t 将会是其底层值,而 oktrue

否则,ok 将为 falset 将为 T 类型的零值,程序并不会产生panic

例:

package main

import "fmt"

func main() {
    var i interface{} = "hello"

    s := i.(string)
    fmt.Println(s)

    s, ok := i.(string)
    fmt.Println(s, ok)

    f, ok := i.(float64)
    fmt.Println(f, ok)

    f = i.(float64) // 报错(panic)
    fmt.Println(f)
}

运行结果:

hello
hello true
0 false
panic: interface conversion: interface {} is string, not float64

goroutine 1 [running]:
main.main()
    /tmp/sandbox028318339/prog.go:17 +0x1f4

类型选择

类型选择 是一种按顺序从几个类型断言中选择分支的结构。

类型选择与一般的 switch 语句相似,不过类型选择中的 case 为类型(而非值), 它们针对给定接口值所存储的值的类型进行比较。

switch v := i.(type) {
case T:
    // v 的类型为 T
case S:
    // v 的类型为 S
default:
    // 没有匹配,v 与 i 的类型相同
}

类型选择中的声明与类型断言 i.(T) 的语法相同,只是具体类型 T 被替换成了关键字 type

此选择语句判断接口值 i 保存的值类型是 T 还是 S。在 TS 的情况下,变量 v 会分别按 TS 类型保存 i 拥有的值。在默认(即没有匹配)的情况下,变量 vi 的接口类型和值相同。

例:

package main

import "fmt"

func do(i interface{}) {
    switch v := i.(type) {
    case int:
        fmt.Printf("Twice %v is %v\n", v, v*2)
    case string:
        fmt.Printf("%q is %v bytes long\n", v, len(v))
    default:
        fmt.Printf("I don't know about type %T!\n", v)
    }
}

func main() {
    do(21)
    do("hello")
    do(true)
}

运行结果:

Twice 21 is 42
"hello" is 5 bytes long
I don't know about type bool!

Program exited.

Stringer

fmt 包中定义的 Stringer 是最普遍的接口之一。

type Stringer interface {
    String() string
}

Stringer 是一个可以用字符串描述自己的类型。fmt 包(还有很多包)都通过此接口来打印值。

# 官网api介绍

type Stringer
type Stringer interface {
    String() string
}

Stringer 接口由任何拥有 String 方法的值所实现,该方法定义了该值的“原生”格式。 String 方法用于打印值,该值可作为操作数传至任何接受字符串的格式,或像 Print 这样的未格式化打印器。

例:

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

func (p Person) String() string {
    return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}

func main() {
    a := Person{"Arthur Dent", 42}
    z := Person{"Zaphod Beeblebrox", 9001}
    fmt.Println(a, z)
}

运行结果:

Arthur Dent (42 years) Zaphod Beeblebrox (9001 years)

Program exited.

Reader

io 包指定了 io.Reader 接口,它表示从数据流的末尾进行读取。

Go 标准库包含了该接口的许多实现,包括文件、网络连接、压缩和加密等等。

io.Reader 接口有一个 Read 方法:

func (T) Read(b []byte) (n int, err error)

Read 用数据填充给定的字节切片并返回填充的字节数和错误值。在遇到数据流的结尾时,它会返回一个 io.EOF 错误。

示例代码创建了一个 strings.Reader 并以每次 8 字节的速度读取它的输出。

例:

package main

import (
    "fmt"
    "io"
    "strings"
)

func main() {
    r := strings.NewReader("Hello, Reader!")

    b := make([]byte, 8)
    for {
        n, err := r.Read(b)
        fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
        fmt.Printf("b[:n] = %q\n", b[:n])
        if err == io.EOF {
            break
        }
    }
}

运行结果:

n = 8 err = <nil> b = [72 101 108 108 111 44 32 82]
b[:n] = "Hello, R"
n = 6 err = <nil> b = [101 97 100 101 114 33 32 82]
b[:n] = "eader!"
n = 0 err = EOF b = [101 97 100 101 114 33 32 82]
b[:n] = ""

Go语言指针符号的*和&

&符号的意思是对变量取地址,如:变量a的地址是&a

*符号的意思是对指针取值,如:*&a,就是a变量所在地址的值,当然也就是a的值了

生成二进制可执行文件去部署项目

set GOARCH=amd64

set GOOS=linux

go build -o ds-customers main.go

之后上传到Linux上:

chmod +x ds-customers

执行:

./ds-customers

Go语言数组的问题

直接上代码

var group []string
group[0]="abc"
fmt.Println(group[0])123

编译正常,运行报错如下

panic: runtime error: index out of range

goroutine 1 [running]:
panic(0x47a880, 0xc42000a110)
    /opt/tools/go1.7.3/src/runtime/panic.go:500 +0x1a1
main.main()
    /opt/IdeaProjects/test-embeded-struct/main.go:11 +0x14
exit status 212345678

index越界,于是改了一下代码

var group []string
fmt.Println(len(group))
fmt.Println(cap(group))
group[0]="abc"
fmt.Println(group[0])12345

输出如下

0
0
panic: runtime error: index out of range

goroutine 1 [running]:
panic(0x48fec0, 0xc42000a110)
    /opt/tools/go1.7.3/src/runtime/panic.go:500 +0x1a1
main.main()
    /opt/IdeaProjects/test-embeded-struct/main.go:13 +0x10a
exit status 212345678910

也就是说,slice声明的时候如果不同时初始化数据,默认长度和容量都是0,下标0意味着长度是1,所以index越界了。


那么,使用slice的时候有下面几种方式


第一,声明的时候初始化数据,例如

test:=[]string{"a","b","c"}1

第二,从已经初始化数据的slice‘切’出来

test:=[]string{"a","b","c"}
your_string=test[0:1]12

第三,append方法,会默认扩容,

var group []string
fmt.Println(len(group))
fmt.Println(cap(group))
group=append(group,"hahaha")
group[0]="abc"
fmt.Println(len(group))
fmt.Println(cap(group))
fmt.Println(group[0])12345678

输出

0
0
1
1
abc12345

append方法在slice长度不足的时候,会进行扩容,注释如下

// The append built-in function appends elements to the end of a slice. If
// it has sufficient capacity, the destination is resliced to accommodate the
// new elements. If it does not, a new underlying array will be allocated.
// Append returns the updated slice. It is therefore necessary to store the
// result of append, often in the variable holding the slice itself:
//  slice = append(slice, elem1, elem2)
//  slice = append(slice, anotherSlice...)
// As a special case, it is legal to append a string to a byte slice, like this:
//  slice = append([]byte("hello "), "world"...)
func append(slice []Type, elems ...Type) []Type
本文目录