流行度排行
https://github.com/speedwheel/awesome-go-web-frameworks/blob/master/README.md#popularity
beego
https://beego.me/products
echo
https://echo.labstack.com/
gin go社区文档
https://learnku.com/docs/gin-gonic/2019
看云gin文档
https://www.kancloud.cn/shuangdeyu/gin_book/949418
gin快速使用
https://www.jianshu.com/p/98965b3ff638
gin-admin-vue
https://www.gin-vue-admin.com/docs/gorm
官网
https://golang.org/dl/
中文官网
https://go-zh.org/pkg/
go常用库
https://github.com/jobbole/awesome-go-cn
查看版本号
go version
查看env
go env
==拉不下来包==
export GOPATH=/usr/local/go/bin export GOPROXY=https://goproxy.io export PATH=$PATH:$GOPATH export GO111MODULE=on
开启module
go env -w GO111MODULE=on
设置代理
go env -w GOPROXY=https://goproxy.io,direct
go-kit
go micro
go-zero
go基础知识
go基础知识v1
go基础知识v2---micro
菜鸟GO
文档和技术论坛
gomod 详细使用
go官网库
==go官方pkg中文==
github对pgk的使用例子
https://github.com/xxjwxc/gormt.git
https://github.com/hantmac/fuckdb.git
1 使用docker-composer方式启动 2 遇到docker容器不能链接本地mysql的时候 在本地mysql GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION; FLUSH PRIVILEGES
go get -u github.com/gohouse/converter
err := converter.NewTable2Struct(). SavePath("./model.go"). Dsn("root:root@tcp(127.0.0.1:3306)/foroo_beta_shopify?charset=utf8"). TagKey("db"). EnableJsonTag(true). Table("fr_store"). Run() fmt.Println(err) return
变量或者方法使用小写相当于protect 首字母大写 相当于public
使用+来拼接字符串
变量声明 var age int;
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
可以将 var f string = "Runoob" 简写为 f := "Runoob":
函数外的每个语句都必须以关键字开始(var、const、func等)
:=不能使用在函数外。
_多用于占位,表示忽略值。import _ "./hello" 用_占位只会执行导入包内的init,其他方法都不能调用
iota是go语言的常量计数器,只能在常量的表达式中使用
strconv.Itoa(97) 将一个数字转成字符串类型 string(97)go自带的会把数字转换成对应的ascii码
const ( n1 = iota //0 n2 //1 n3 //2 n4 //3 )
map、slice、chan、指针、interface默认以引用的方式传递。
互斥锁 var lock sync.Mutex
var x int64 var wg sync.WaitGroup var lock sync.Mutex func add() { for i := 0; i < 5000; i++ { lock.Lock() // 加锁 x = x + 1 lock.Unlock() // 解锁 } wg.Done() } func main() { wg.Add(2) go add() go add() wg.Wait() fmt.Println(x) }
读写互斥锁 需要注意的是读写锁非常适合读多写少的场景,如果读和写的操作差别不大,读写锁的优势就发挥不出来。
var ( x int64 wg sync.WaitGroup lock sync.Mutex rwlock sync.RWMutex ) func write() { // lock.Lock() // 加互斥锁 rwlock.Lock() // 加写锁 x = x + 1 time.Sleep(10 * time.Millisecond) // 假设读操作耗时10毫秒 rwlock.Unlock() // 解写锁 // lock.Unlock() // 解互斥锁 wg.Done() } func read() { // lock.Lock() // 加互斥锁 rwlock.RLock() // 加读锁 time.Sleep(time.Millisecond) // 假设读操作耗时1毫秒 rwlock.RUnlock() // 解读锁 // lock.Unlock() // 解互斥锁 wg.Done() } func main() { start := time.Now() for i := 0; i < 10; i++ { wg.Add(1) go write() } for i := 0; i < 1000; i++ { wg.Add(1) go read() } wg.Wait() end := time.Now() fmt.Println(end.Sub(start)) }
sync.Once其实内部包含一个互斥锁和一个布尔值,互斥锁保证布尔值和数据的安全,而布尔值用来记录初始化是否完成。这样设计就能保证初始化操作的时候是并发安全的并且初始化操作也不会被执行多次。
var icons map[string]image.Image var loadIconsOnce sync.Once func loadIcons() { icons = map[string]image.Image{ "left": loadIcon("left.png"), "up": loadIcon("up.png"), "right": loadIcon("right.png"), "down": loadIcon("down.png"), } } // Icon 是并发安全的 func Icon(name string) image.Image { loadIconsOnce.Do(loadIcons) return icons[name] }
==goroutine高并发下 操作map 要用 sync.Map 来操作==
//sync.Map内置了诸如Store、Load、LoadOrStore、Delete、Range等操作方法 var m = sync.Map{} func main() { wg := sync.WaitGroup{} for i := 0; i < 20; i++ { wg.Add(1) go func(n int) { key := strconv.Itoa(n) m.Store(key, n) value, _ := m.Load(key) fmt.Printf("k=:%v,v:=%v\n", key, value) wg.Done() }(i) } wg.Wait() }
==代码中的加锁操作因为涉及内核态的上下文切换会比较耗时、代价比较高。针对基本数据类型我们还可以使用原子操作来保证并发安全,因为原子操作是Go语言提供的方法它在用户态就可以完成,因此性能比加锁操作更好。Go语言中原子操作由内置的标准库sync/atomic提供。==
// 原子操作版加函数 func atomicAdd() { atomic.AddInt64(&x, 1) wg.Done() }
make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身
变量之间使用引用地址直接可以改变
package main func swap(a, b *int) { var temp int temp = *a *a = *b *b = temp } func main() { var a, b = 1, 2 swap(&a, &b) fmt.Println(a) fmt.Println(b) } //指针小案例 var a int fmt.Println(&a) var p *int p = &a *p = 20 fmt.Println(a)
在参数赋值时可以不用用一个一个的赋值,可以直接传递一个数组或者切片,特别注意的是在参数后加上“…”即可
package main import ( "fmt" ) func test(s string, n ...int) string { var x int for _, i := range n { x += i } return fmt.Sprintf(s, x) } func main() { s := []int{1, 2, 3} res := test("sum: %d", s...) // slice... 展开slice println(res) }
"_"标识符,用来忽略函数的某个返回值
没有参数的 return 语句返回各个返回变量的当前值。这种用法被称作“裸”返回。
func add(a, b int) (c int) { c = a + b return } func calc(a, b int) (sum int, avg int) { sum = a + b avg = (a + b) / 2 return } func main() { var a, b int = 1, 2 c := add(a, b) sum, avg := calc(a, b) fmt.Println(a, b, c, sum, avg) }
命名返回参数允许 defer 延迟调用通过闭包读取和修改。
defer 是先进后出
package main func add(x, y int) (z int) { defer func() { z += 100 }() z = x + y return } func main() { println(add(1, 2)) }
定义一维数组
var a [5]int = [5]int{1,2,5,6,7} var a = [...]int{1,2,3,5,6} d := [...]struct { name string age uint8 }{ {"user1", 10}, // 可省略元素类型。 {"user2", 20}, // 别忘了最后一行的逗号。 }
定义二维数组 第 2 纬度不能用 "..."
var b [3][2]int = [3][2]int{{1,2},{3,4},{5,6}} var arr1 [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
定义slice
全局: var arr = [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} var slice0 []int = arr[start:end] var slice1 []int = arr[:end] var slice2 []int = arr[start:] var slice3 []int = arr[:] var slice4 = arr[:len(arr)-1] //去掉切片的最后一个元素 局部: arr2 := [...]int{9, 8, 7, 6, 5, 4, 3, 2, 1, 0} slice5 := arr[start:end] slice6 := arr[:end] slice7 := arr[start:] slice8 := arr[:] slice9 := arr[:len(arr)-1] //去掉切片的最后一个元素 //通过make来创建切片 var slice []type = make([]type, len) slice := make([]type, len) slice := make([]type, len, cap)
定义map
scoreMap := make(map[string]int) scoreMap["张三"] = 90 scoreMap["小明"] = 100 // 如果key存在ok为true,v为对应的值;不存在ok为false,v为值类型的零值 v, ok := scoreMap["张三"] if ok { fmt.Println(v) } else { fmt.Println("查无此人") } //使用delete()函数删除键值对 delete(scoreMap, "小明")//将小明:100从map中删除
数组按照下标排序输出
tetcc := make(map[int]string,5) tetcc[1] = "smallsha" tetcc[5] = "smallsha1" tetcc[3] = "smallsha2" sli := []int{} for i := range tetcc { sli = append(sli,i) } sort.Ints(sli) for i := 0; i < len(tetcc); i++ { fmt.Println("内容",tetcc[sli[i]]) } fmt.Printf("%#v\n", sli) return
if判断
if test2 :=19; test2> 20 { fmt.Println("test11212121212") }else if test2 < 20 { fmt.Println("test1111") }
switch使用
test3 := 80 switch test3 { case 80: fmt.Println("1") fallthrough //可以使用fallthrough强制执行后面的case代码。 case 90: fmt.Println("2") case 100: fmt.Println("3") default: fmt.Println("default") }
多个参数调用
func test(s string, n ...int) string { var x int for _, i := range n { x += i } return fmt.Sprintf(s, x) } s := []int{1, 2, 3} res := test("sum: %d", s...) // slice... 展开slice println(res)
闭包使用
func a() func() int { i := 0 b := func() int{ i++ fmt.Println(i) return i } return b } ccccc := a() ccccc() ccccc() return
关键字 defer 用于注册延迟调用。
这些调用直到 return 前才被执。因此,可以用来做资源清理。
多个defer语句,按先进后出的方式执行
惯例是:导致关键流程出现不可修复性错误的使用 panic,其他使用 error。
func main() { test() } func test() { defer func() { if err := recover(); err != nil { println(err.(string)) // 将 interface{} 转型为具体类型。 } }() panic("panic error!") }
表达式
package main import "fmt" type User struct { id int name string } func (self *User) Test() { fmt.Printf("%p, %v\n", self, self) } func main() { u := User{1, "Tom"} u.Test() mValue := u.Test mValue() // 隐式传递 receiver mExpression := (*User).Test mExpression(&u) // 显式传递 receiver }
定义error http://www.topgoer.com/%E6%96%B9%E6%B3%95/%E8%87%AA%E5%AE%9A%E4%B9%89error.html
//返回异常 package main import ( "errors" "fmt" ) func getCircleArea(radius float32) (area float32, err error) { if radius < 0 { // 构建个异常对象 err = errors.New("半径不能为负") return } area = 3.14 * radius * radius return } func main() { area, err := getCircleArea(-5) if err != nil { fmt.Println(err) } else { fmt.Println(area) } } //系统抛异常 package main import "fmt" func test01() { a := [5]int{0, 1, 2, 3, 4} a[1] = 123 fmt.Println(a) //a[10] = 11 index := 10 a[index] = 10 fmt.Println(a) } func getCircleArea(radius float32) (area float32) { if radius < 0 { // 自己抛 panic("半径不能为负") } return 3.14 * radius * radius } func test02() { getCircleArea(-5) } // func test03() { // 延时执行匿名函数 // 延时到何时?(1)程序正常结束 (2)发生异常时 defer func() { // recover() 复活 恢复 // 会返回程序为什么挂了 if err := recover(); err != nil { fmt.Println(err) } }() getCircleArea(-5) fmt.Println("这里有没有执行") } func test04() { test03() fmt.Println("test04") } func main() { test04() } //自定义error package main import ( "fmt" "os" "time" ) type PathError struct { path string op string createTime string message string } func (p *PathError) Error() string { return fmt.Sprintf("path=%s \nop=%s \ncreateTime=%s \nmessage=%s", p.path, p.op, p.createTime, p.message) } func Open(filename string) error { file, err := os.Open(filename) if err != nil { return &PathError{ path: filename, op: "read", message: err.Error(), createTime: fmt.Sprintf("%v", time.Now()), } } defer file.Close() return nil } func main() { err := Open("/Users/5lmh/Desktop/go/src/test.txt") switch v := err.(type) { case *PathError: fmt.Println("get path error,", v) default: } }
interface 应用
// 空接口作为函数参数 func show(a interface{}) { fmt.Printf("type:%T value:%v\n", a, a) } // 空接口作为map值 var studentInfo = make(map[string]interface{}) studentInfo["name"] = "李白" studentInfo["age"] = 18 studentInfo["married"] = false fmt.Println(studentInfo)
go调度 sync.WaitGroup
var wg sync.WaitGroup func hello(i int) { defer wg.Done() // goroutine结束就登记-1 fmt.Println("Hello Goroutine!", i) } func main() { for i := 0; i < 10; i++ { wg.Add(1) // 启动一个goroutine就登记+1 go hello(i) } wg.Wait() // 等待所有登记的goroutine都结束 }
go runtime.Gosched()
//让出CPU时间片,重新等待安排任务(大概意思就是本来计划的好好的周末出去烧烤,但是你妈让你去相亲,两种情况第一就是你相亲速度非常快,见面就黄不耽误你继续烧烤,第二种情况就是你相亲速度特别慢,见面就是你侬我侬的,耽误了烧烤,但是还馋就是耽误了烧烤你还得去烧烤) package main import ( "fmt" "runtime" ) func main() { go func(s string) { for i := 0; i < 2; i++ { fmt.Println(s) } }("world") // 主协程 for i := 0; i < 2; i++ { // 切一下,再次分配任务 runtime.Gosched() fmt.Println("hello") } }
runtime.Goexit()
//退出当前协程(一边烧烤一边相亲,突然发现相亲对象太丑影响烧烤,果断让她滚蛋,然后也就没有然后了) package main import ( "fmt" "runtime" ) func main() { go func() { defer fmt.Println("A.defer") func() { defer fmt.Println("B.defer") // 结束协程 runtime.Goexit() defer fmt.Println("C.defer") fmt.Println("B") }() fmt.Println("A") }() for { } }
runtime.GOMAXPROCS
//Go1.5版本之后,默认使用全部的CPU逻辑核心数 ///两个任务只有一个逻辑核心,此时是做完一个任务再做另一个任务。 将逻辑核心数设为2,此时两个任务并行执行,代码如下。 func a() { for i := 1; i < 10; i++ { fmt.Println("A:", i) } } func b() { for i := 1; i < 10; i++ { fmt.Println("B:", i) } } func main() { runtime.GOMAXPROCS(2) go a() go b() time.Sleep(time.Second) }
1.对一个关闭的通道再发送值就会导致panic。
2.对一个关闭的通道进行接收会一直获取值直到通道为空。
3.对一个关闭的并且没有值的通道执行接收操作会得到对应类型的零值。
4.关闭一个已经关闭的通道会导致panic。
5.chan<- int是一个只能发送的通道,可以发送但是不能接收;
6 <-chan int是一个只能接收的通道,可以接收但是不能发送。
func counter(out chan<- int) { for i := 0; i < 100; i++ { out <- i } close(out) } func squarer(out chan<- int, in <-chan int) { for i := range in { out <- i * i } close(out) } func printer(in <-chan int) { for i := range in { fmt.Println(i) } } func main() { ch1 := make(chan int) ch2 := make(chan int) go counter(ch1) go squarer(ch2, ch1) printer(ch2) }
type Job struct { Id int RandNum int } type Result struct { job *Job sum int } func createPool(num int, jobChan chan *Job,resultChan chan *Result) { for i := 0; i < num; i++ { go func(jobChan chan *Job,resultChan chan *Result) { for i2 := range jobChan { r_num := i2.RandNum var sum int for r_num !=0 { tmp := r_num % 10 sum += tmp r_num /= 10 } r := &Result{ job: i2, sum: sum, } resultChan <- r } }(jobChan,resultChan) } } func main() { flag.Parse() jobChan := make(chan *Job, 128) resultChan := make(chan *Result, 128) createPool(64, jobChan, resultChan) // 4.开个打印的协程 go func(resultChan chan *Result) { // 遍历结果管道打印 for result := range resultChan { fmt.Printf("job id:%v randnum:%v result:%d\n", result.job.Id, result.job.RandNum, result.sum) } }(resultChan) var id int // 循环创建job,输入到管道 for { id++ // 生成随机数 r_num := rand.Int() job := &Job{ Id: id, RandNum: r_num, } jobChan <- job } return }
select { case <-chan1: // 如果chan1成功读到数据,则进行该case处理语句 case chan2 <- 1: // 如果成功向chan2写入数据,则进行该case处理语句 default: // 如果上面都没有成功,则进入default处理流程 }
var name string var age int var married bool var delay time.Duration flag.StringVar(&name, "name", "张三", "姓名") flag.IntVar(&age, "age", 18, "年龄") flag.BoolVar(&married, "married", false, "婚否") flag.DurationVar(&delay, "d", 0, "延迟的时间间隔") 或者使用 name := flag.String("name", "张三", "姓名") age := flag.Int("age", 18, "年龄") married := flag.Bool("married", false, "婚否") delay := flag.Duration("d", 0, "时间间隔") //解析命令行参数 flag.Parse() fmt.Println(name, age, married, delay) //返回命令行参数后的其他参数 fmt.Println(flag.Args()) //返回命令行参数后的其他参数个数 fmt.Println(flag.NArg()) //返回使用的命令行参数个数 fmt.Println(flag.NFlag()) //os.args if len(os.Args) > 0 { for index, arg := range os.Args { fmt.Printf("args[%d]=%v\n", index, arg) } }
logFile, err := os.OpenFile("./smallsha.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644) if err != nil { fmt.Println("open log file failed, err:", err) return } log.SetOutput(logFile) //设置日志文件写入 log.SetFlags(log.Llongfile | log.Lmicroseconds | log.Ldate) log.SetPrefix("[smallsha]") log.Printf("%s","smallsha") log.Println("test") //log.Panic("test1") //log.Fatalln("test2") logger := log.New(os.Stdout, "<New>", log.Lshortfile|log.Ldate|log.Ltime) logger.Println("这是自定义的logger记录的日志。")
arr := [5]int{1,2,4,5,6,8} for i,num :=range arr { fmt.Println(i,num) } //while for 循环配合通道 var ( ch11 = make(chan int) ch12 = make(chan int) ) go func() { for i := 0; i < 10; i++ { ch11 <- i } close(ch11) }() go func() { for { i, ok := <-ch11 if !ok { break } ch12 <- i * i } close(ch12) }() for i := range ch12 { fmt.Println(i) }
const MAX int =3 func main(){ a :=[]int{1,3,5} var ptr [MAX]*int; for i = 0; i < MAX; i++ { ptr[i] = &a[i] /* 整数地址赋值给指针数组 */ } for i = 0; i < MAX; i++ { fmt.Printf("a[%d] = %d\n", i,*ptr[i] ) } }
s3, _ := strconv.ParseBool("1") fmt.Printf("%# v\n", pretty.Formatter(s3)) return //int 转 string s2 := 100 i2 := strconv.Itoa(s2) fmt.Printf("%# v\n", pretty.Formatter(i2)) return //string 转 int s1 := "100" i1, err := strconv.Atoi(s1) if err != nil { fmt.Println(err) return } fmt.Printf("%# v\n", pretty.Formatter(i1)) return
package logic import ( "fmt" "html/template" "net/http" ) func init() { http.HandleFunc("/",sayHello) err := http.ListenAndServe(":9090", nil) if err != nil { fmt.Println(err) return } } type UserInfo struct { Name string Gender string Age int } func sayHello(w http.ResponseWriter,r *http.Request) { // 解析指定文件生成模板对象 tmpl, err := template.ParseFiles("./hello.html") if err != nil { fmt.Println("create template failed, err:", err) return } // 利用给定数据渲染模板,并将结果写入w user := UserInfo{ Name: "枯藤", Gender: "男", Age: 18, } // 利用给定数据渲染模板,并将结果写入w tmpl.Execute(w, user) }
package main import "fmt" type Books struct { title string author string subject string book_id int } func main() { var Book1 Books /* 声明 Book1 为 Books 类型 */ var Book2 Books /* 声明 Book2 为 Books 类型 */ /* book 1 描述 */ Book1.title = "Go 语言" Book1.author = "www.runoob.com" Book1.subject = "Go 语言教程" Book1.book_id = 6495407 /* book 2 描述 */ Book2.title = "Python 教程" Book2.author = "www.runoob.com" Book2.subject = "Python 语言教程" Book2.book_id = 6495700 /* 打印 Book1 信息 */ printBook(&Book1) /* 打印 Book2 信息 */ printBook(&Book2) } func printBook( book *Books ) { fmt.Printf( "Book title : %s\n", book.title) fmt.Printf( "Book author : %s\n", book.author) fmt.Printf( "Book subject : %s\n", book.subject) fmt.Printf( "Book book_id : %d\n", book.book_id) } //结构体new 和赋值 type Person struct { Username string `json:"username"` Password string `json:"password"` } //声明构造方法 func newPerson(username string, password string) *Person { return &Person{ Username: username, Password: password, } } //定义修改结构体的方法 func (p *Person) setUsername(username string) { p.Username = username }
package main import "fmt" func main() { var countryCapitalMap map[string]string /*创建集合 */ countryCapitalMap = make(map[string]string) /* map插入key - value对,各个国家对应的首都 */ countryCapitalMap [ "France" ] = "巴黎" countryCapitalMap [ "Italy" ] = "罗马" countryCapitalMap [ "Japan" ] = "东京" countryCapitalMap [ "India " ] = "新德里" /*使用键输出地图值 */ for country := range countryCapitalMap { fmt.Println(country, "首都是", countryCapitalMap [country]) } /*查看元素在集合中是否存在 */ capital, ok := countryCapitalMap [ "American" ] /*删除元素*/ delete(countryCapitalMap, "France") /*如果确定是真实的,则存在,否则不存在 */ /*fmt.Println(capital) */ /*fmt.Println(ok) */ if (ok) { fmt.Println("American 的首都是", capital) } else { fmt.Println("American 的首都不存在") } }
声明变量 package main import "fmt" func main() { var a string = "Runoob" fmt.Println(a) var b, c int = 1, 2 fmt.Println(b, c) } 声明方法 package main import "fmt" func swap(x, y string) (string, string) { return y, x } func main() { a, b := swap("Google", "Runoob") fmt.Println(a, b) } 定义数组 var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0} var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0} package main import "fmt" func main() { var n [10]int /* n 是一个长度为 10 的数组 */ var i,j int /* 为数组 n 初始化元素 */ for i = 0; i < 10; i++ { n[i] = i + 100 /* 设置元素为 i + 100 */ } /* 输出每个数组元素的值 */ for j = 0; j < 10; j++ { fmt.Printf("Element[%d] = %d\n", j, n[j] ) } }
package main import "fmt" type Books struct { title string author string subject string book_id int } func main() { var Book1 Books /* 声明 Book1 为 Books 类型 */ var Book2 Books /* 声明 Book2 为 Books 类型 */ /* book 1 描述 */ Book1.title = "Go 语言" Book1.author = "www.runoob.com" Book1.subject = "Go 语言教程" Book1.book_id = 6495407 /* book 2 描述 */ Book2.title = "Python 教程" Book2.author = "www.runoob.com" Book2.subject = "Python 语言教程" Book2.book_id = 6495700 /* 打印 Book1 信息 */ fmt.Printf( "Book 1 title : %s\n", Book1.title) fmt.Printf( "Book 1 author : %s\n", Book1.author) fmt.Printf( "Book 1 subject : %s\n", Book1.subject) fmt.Printf( "Book 1 book_id : %d\n", Book1.book_id) /* 打印 Book2 信息 */ fmt.Printf( "Book 2 title : %s\n", Book2.title) fmt.Printf( "Book 2 author : %s\n", Book2.author) fmt.Printf( "Book 2 subject : %s\n", Book2.subject) fmt.Printf( "Book 2 book_id : %d\n", Book2.book_id) } 结构体作为函数参数 func printBook( book Books ) { fmt.Printf( "Book title : %s\n", book.title) fmt.Printf( "Book author : %s\n", book.author) fmt.Printf( "Book subject : %s\n", book.subject) fmt.Printf( "Book book_id : %d\n", book.book_id) }
#函数 func main() { sum := add(1, 2) fmt.Println(sum) } func add(a, b int) int { return a + b } #方法 type person struct { name string } func (p person) String() string{ return "the person name is "+p.name } func main() { p:=person{name:"张三"} fmt.Println(p.String()) } #可变参数 func main() { print("1","2","3") } func print (a ...interface{}){ for _,v:=range a{ fmt.Print(v) } fmt.Println() }
func changeString() { s1 := "hello" // 强制类型转换 byteS1 := []byte(s1) byteS1[0] = 'H' fmt.Println(string(byteS1)) s2 := "博客" runeS2 := []rune(s2) runeS2[0] = '狗' fmt.Println(string(runeS2)) }