Golang 内置函数

Golang中内置了一些函数,在使用这些函数时,不必以包名为前缀来调用,而是直接写函数名即可调用,这些函数都是一些基础的函数,在程序设计中应用比较普遍,所以一定要牢记这些最基本的函数用法。下边来介绍一下Golang内置函数。

append

append作用是在切片变量后边追加新的数据,然后返回新的切片变量。函数声明是:

func append(slice []Type, elems ...Type) []Type

append是一个可变参数函数,第一个参数是切片类型,后边的参数是可变参数,可变参数的类型必须与切片中数据类型一致。应用示例:

package main

import (
    "fmt"
)

func main() {
    var arr1 []string
    arr2 := append(arr1, "a")
    fmt.Println(arr2)

    arr3 := append(arr1, arr2...)
    fmt.Println(arr3)
}

copy

func copy(dst, src []Type) int

copy的作用是将一个切片内容拷贝到另一个切片,被拷贝的切片称为源切片,接收内容的切片称为目标切片,源切片与目标切片数据类型一致。copy函数第一个参数是目标切片,第二个参数是源切片。copy在进行切片内容拷贝时,并不会为目标切片扩展长度,所以,想要使用copy来复制切片内容,最好是给目标切片设置足够的长度来装载源切片中的内容。示例如下:

package main

import (
    "fmt"
)

func main() {
    var arr1 []string = []string{"a", "b", "c", "d"}
    var arr2 []string
    copy(arr2, arr1)
    fmt.Println(arr2)

    arr2 = make([]string, 4)
    copy(arr2, arr1)
    fmt.Println(arr2)
}

输出信息是:

[]
[a b c d]

delete

func delete(m map[Type]Type1, key Type)

delete只能用来删除字典中的内容。delete函数接收两个参数,第一个参数是字典变量,第二个是需要删除内容的key值。delete是通过key来删除map中的内容。请看下边示例代码:

package main

import (
    "fmt"
)

func main() {
    var mp = map[string]int{
        "a": 1,
        "b": 2,
        "c": 3,
        "d": 4,
    }
    fmt.Println("字典内容是:", mp)
    //删除字典中key为a的内容
    delete(mp, "a")
    fmt.Println("删除操作后,字典内容是:", mp)
}

输出信息是:

字典内容是: map[a:1 b:2 c:3 d:4]
删除操作后,字典内容是: map[b:2 c:3 d:4]

len

func len(v Type) int

len用来获取字符串,切片,数组,通道,字典类型变量的内容长度,不同的数据类型,长度计算规则不一样。如对于切片,字典,数组,通道类型的变量,它们中每一个元素就是一个长度。对于string类型变量,它们每一个字节是一个长度。对于rune类型切片变量,它们每一个字符是一个长度,rune类型变量中的内容采用utf-8编码,一个字符可能对应4个字节。下边来看一段示例:

package main

import (
    "fmt"
)

func main() {
    // 数组
    var arr [4]string = [4]string{"a", "b", "c", "d"}
    fmt.Println("数组长度是:", len(arr))
    // 切片
    var slice []int = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    fmt.Println("slice长度是:", len(slice))
    // 通道
    var ch = make(chan int, 10)
    ch <- 1
    ch <- 2
    ch <- 3
    ch <- 4
    fmt.Println("chan长度是:", len(ch))
    // 字典
    var mp map[string]bool = map[string]bool{
        "0": false,
        "1": true,
    }
    fmt.Println("map长度是:", len(mp))
    // 字符串
    var str string = "道德经"
    fmt.Println("string类型长度是:", len(str))
    fmt.Println("rune类型长度是:", len([]rune(str)))
}

输出信息是:

数组长度是: 4
slice长度是: 10
chan长度是: 4
map长度是: 2
string类型长度是: 9
rune类型长度是: 3

len计算的不是这个变量的容量,而是计算这个变量已有数据的内容长度。上边的chan类型变量分配了10个buffer,但是len计算的长度只有4,刚好等于通道中现有数据元素个数。计算变量容量的函数是cap,将在后边进行讲解。

cap

cap用来计算切片,通道,数组类型变量的容量,也就是这个变量最多能装多少个元素。示例代码如下:

package main

import (
    "fmt"
)

func main() {
    // 数组
    var arr [4]string = [4]string{"a", "b", "c", "d"}
    fmt.Println("数组长度是:", cap(arr))
    // 切片
    var slice []int = []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    fmt.Println("slice长度是:", cap(slice))
    // 通道
    var ch = make(chan int, 10)
    ch <- 1
    ch <- 2
    ch <- 3
    ch <- 4
    fmt.Println("chan长度是:", cap(ch))
}

输出信息是:

数组长度是: 4
slice长度是: 10
chan长度是: 10

cap只能用于计算数组,切片,通道类型的变量。

new

func new(Type) *Type

new函数用来创建某一个类型的指针型对象。理论上,一个数据类型,只要能够被访问,就可以使用new函数来创建这个类型的指针型对象。下边来看一段示例,使用new函数创建基本类型的指针型对象。

package main

import (
    "fmt"
)

type demo struct{}

func main() {
    var str = new(string)
    fmt.Println("string pointer", str)
    var num = new(int)
    fmt.Println("int pointer", num)
    var de = new(demo)
    fmt.Println("struct pointer", de)
}

在调用new函数时,将类型名作为参数即可创建出这个类型的指针型对象。

在golang中存在几种特殊的情况。如使用new创建chan类型指针型对象,但是仍然需要调用make来给chan类型变量分配容量。

package main

import (
    "fmt"
    "time"
)

func main() {
    // 创建通道类型指针型对象
    var ch = new(chan int)
    // 给对象分配容量,容量为1
    *ch = make(chan int)

    // 开启协程,从通道中读取数据
    go func() {
        fmt.Println(<-(*ch))
    }()

    // 向通道中写入数据
    *ch <- 8
    time.Sleep(time.Second * 1)
}

如果使用new创建通道类型变量后,不使用make来分配容量,当向通道中写入信息或从通道中读取信息时,将会出现下边错误信息:

... [chan send (nil chan)]:
... [chan receive (nil chan)]

虽然new关键字可以创建任何类型的指针型对象,但是由于某些特殊类型的对象被创建后,需要进行更多的初始化工作,所以golang中引入了make函数,通过make来创建这些特殊类型的对象。这些特殊类型就是:切片类型,通道类型,字典类型。

make

func make(t Type, size ...IntegerType) Type

make用于给切片,通道,字典类型变量分配容量空间。给不同的类型变量分配空间时,make参数不一样。下边来介绍一下不同类型使用make时的不同用法:

使用make给切片类型变量分配容量空间

var arr = make([]string,4)

给字符串类型切片分配4个容量空间。

var arr = make([]string,5,10)

给字符串类型切片分配10个容量空间,且设置切片长度为5,也就是设置切片前5个元素已经被使用,如果使用append向切片中追加元素,新追加的元素将会从第6个位置算起(下标为5)。

使用make给通道类型变量分配容量空间

var ch = make(chan int)

给切片类型变量分配一个容量的空间,创建了一个不带缓冲的通道。

var ch = make(chan int, 10)

给切片类型变量分配10个容量的空间,创建了一个带10个缓冲的通道。

使用make给字典类型变量分配容量空间

var mp = make(map[string]int)

给字典类型变量分配空间,不需要指定容量,如果在第二个参数中指定容量,这个容量值也会被忽略。如下边写法:

// 容量10被忽略
var mp = make(map[string]int,10)

虽然在给map类型变量分配容量时,指定了长度10,但由于map类型变量能够装载的数据量与系统内存有关,所以给map类型变量设置容量的做法将会被忽略。

请牢记一点:make函数只能初始化字典(map),切片(slice),通道(chan)类型。

complex

func complex(r, i FloatType) ComplexType

complex函数用来创建复数对象。这个函数需要两个参数,第一个参数是复数的实部,第二个参数是复数的虚部。复数的表型形式是:

1+2i

使用complex函数创建复数的示例如下:

package main

import (
    "fmt"
)

type demo struct{}

func main() {
    var de = complex(1, 2)
    fmt.Println(de)
}

输出信息是:

(1+2i)

real

func real(c ComplexType) FloatType

用来获取复数的实部值

imag

func imag(c ComplexType) FloatType

用来获取复数的虚部值

close

func close(c chan<- Type)

内置的close函数,只能用于chan类型变量。使用close函数关闭通道后,这个通道不允许被写入新的信息,但是关闭操作不会清除通道中已有的内容,不影响通道被读取。示例代码如下:

package main

import (
    "fmt"
    "time"
)

func write(ch chan int) {
    for i := 0; i < 10; i++ {
        ch <- i * 10
        time.Sleep(time.Second * 1)
    }
    close(ch)
}

func read(ch chan int) {
    for {
        if val, ok := <-ch; ok {
            fmt.Println("从通道中读取值:", val)
        } else {
            // 通道被关闭
            fmt.Println("通道已关闭,退出读取程序")
            break
        }
    }
}

func main() {
    var ch = make(chan int, 10)
    go write(ch)
    read(ch)
}

上边的通道读取操作是:

val,ok := <-ch

当通道被关闭后,如果从通道中读取到信息,则ok值为true,val是一个有效值;如果从通道中没有读取到信息,则ok值为false,此时的val是脏数据,切勿将ok为false时的val值拿去使用,此时的val值是chan指定数据类型的默认值。

如果通道没有被关闭,当从通道中没有读取到信息时,读取操作将会产生程序阻塞

所以使用close函数的目的是关闭不会再写入数据的通道,告诉通道读取方,所有数据发送完毕。

panic

func panic(v interface{})

用来抛出异常,使用panic抛出异常后,函数执行将从调用panic的地方停止,如果函数内有defer调用,则执行defer后边的函数调用,如果defer调用的函数中没有捕获异常信息,这个异常会沿着函数调用栈往上传递,直到main函数仍然没有捕获异常,将会导致程序异常退出。示例代码:

package main

func demo() {
    panic("抛出异常")
}

func main() {
    demo()
}

输出信息是:

panic: 抛出异常

goroutine 1 [running]:
main.demo()
    ...go实战/src/github.com/hzwy23/GoDemos/demo11/main.go:4 +0x40
main.main()
    ...go实战/src/github.com/hzwy23/GoDemos/demo11/main.go:7 +0x27

请谨慎使用panic函数抛出异常,如果没有捕获异常,将会导致程序异常退出。

recover

func recover() interface{}

recover用来捕获panic函数抛出的异常信息。示例代码如下:

package main

import "fmt"

func demo() {
    panic("抛出异常")
}

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println(r)
        }
    }()
    demo()

}

输出信息是:

抛出异常

当没有异常信息抛出时,recover函数返回值是nil。recover只有在defer调用的函数内部时,才能阻止panic抛出的异常信息继续向上传递,如果不是在defer调用的函数内部,将会是什么样子呢?

package main

func main() {
    defer recover()
    panic("hello world")
}

输出信息是:

panic: hello world

goroutine 1 [running]:
main.main()
 ...go实战/src/github.com/hzwy23/GoDemos/demo11/main.go:5 +0x66

所以必须将recover函数调用放在defer调用的函数内部,如:

package main

func main() {
    defer func() {
        recover()
    }()
    panic("hello world")
}

这样才能阻止panic抛出的异常信息继续向上传递。

print

打印信息到标准输出,结尾没有默认换行符。使用方法是:

package main

func main() {
    print("hello world")
}

println

打印信息到标准输出,结尾自动换行。使用方法是:

package main

func main() {
    println("hello world")
}

error

error是golang语言中用来表示错误信息的接口,error接口定义是:

type error interface {
    Error() string
}

golang标准库中errors包内的errorString结构体实现了error接口,实现代码是:

// New returns an error that formats as the given text.
func New(text string) error {
    return &errorString{text}
}

// errorString is a trivial implementation of error.
type errorString struct {
    s string
}

func (e *errorString) Error() string {
    return e.s
}

error接口并不是系统异常接口,不会导致程序进入异常状态,error接口与其他普通接口实质上一样,没有什么系统级的特殊功能,error接口只是定义了一种错误信息处理的规范,规范就是:在程序设计中,把代表错误的信息使用error接口表示。在golang标准库中,大部分错误信息都使用error接口表示。所以error接口是程序中约定俗成的代表错误的符号。

在程序设计中,我们也可以自定义错误信息规范,
如你的代码中使用hello来表示错误信息

type hello interface{
    SayError()
}

如果你的代码给别的组织使用,则你必须告诉对方,你的错误信息是用hello接口来表示的,而对方的代码又是以error接口来表示错误信息,对方在使用你的代码时,还需要做额外的思想准备,甚至做接口转换。所以虽然golang可以使用自定义接口来表示错误信息,但是使用自定义错误信息接口会增加不同组织之间的沟通成本。error作为标准库提供的错误信息接口,有利于形成规范,提高代码可读性和可重用性。

猜你喜欢

转载自blog.csdn.net/hzwy23/article/details/79782937