775 lines
16 KiB
Markdown
775 lines
16 KiB
Markdown
# 1-go基础
|
||
|
||
1. [https://go.dev/tour/](https://go.dev/tour/) (必读) , 中文版 https://tour.go-zh.org/
|
||
2. [https://go.dev/doc/effective_go](https://go.dev/doc/effective_go) (必读)
|
||
3. https://github.com/Unknwon/the-way-to-go_ZH_CN
|
||
|
||
## 初始化
|
||
|
||
```sh
|
||
go mod init github.com/用户名/模块名
|
||
# 会生成go.mod, 相当于 npm 的 package.json
|
||
# 删除不再依赖的模块
|
||
go mod tidy
|
||
```
|
||
|
||
```go
|
||
// hello-word.go
|
||
package main
|
||
|
||
import "fmt" // 标准库
|
||
|
||
func main() {
|
||
fmt.Println("Hello World")
|
||
}
|
||
```
|
||
|
||
运行 `go run ./hello-word.go`
|
||
|
||
```go
|
||
import (
|
||
"fmt"
|
||
"math"
|
||
) // 分组导入
|
||
```
|
||
|
||
在 Go 中,如果一个名字以大写字母开头,那么它就是已导出的。例如,`Pizza` 就是个已导出名,`Pi` 也同样,它导出自 `math` 包。
|
||
|
||
## 打印信息
|
||
|
||
Print: 轻量, 没有时间戳
|
||
|
||
> 开发阶段调试或者简单输出文本
|
||
|
||
```go
|
||
fmt.Println("Hello, World!") // 在末尾添加换行符
|
||
|
||
fmt.Print("Hello, ") // 不添加换行符号
|
||
fmt.Print("World!\n")
|
||
|
||
fmt.Printf("Name: %s, Age: %d\n", "Alice", 30) // 格式化字符串
|
||
```
|
||
|
||
Log:
|
||
|
||
默认带有时间戳, 可以输出日志所在的文件和行号, 可以触发程序的终止或异常
|
||
|
||
```go
|
||
log.Println("This is a log message.")
|
||
|
||
log.Print("Logging without newline.")
|
||
|
||
log.Printf("Name: %s, Age: %d", "Alice", 30)
|
||
|
||
log.Fatal("Fatal error occurred.") // 打印信息后调用 os.Exit(1),程序会立即终止。适用于严重错误
|
||
|
||
log.Panic("Panic occurred.") // 打印信息后调用 `panic`,程序会抛出异常。适用于需要立即中断的错误
|
||
|
||
```
|
||
|
||
## 变量
|
||
|
||
```go
|
||
var a = 1 // var声明变量,并赋值; 全局变量
|
||
|
||
func main() {
|
||
var b int // 声明变量后面再赋值,就得带上类型
|
||
b = 2
|
||
fmt.Println(b)
|
||
str := "Hello World" // 函数里的变量 := 直接赋值,自动推断类型
|
||
fmt.Println(str)
|
||
}
|
||
|
||
```
|
||
|
||
* `:=` 结构不能在函数外使用
|
||
* 没有明确初始化的变量声明会被赋予对应类型的 **零值**。
|
||
* 数值类型零值为 `0`,
|
||
* 布尔类型零值为 `false`,
|
||
* 字符串零值为 `""`(空字符串)。
|
||
* 常量
|
||
|
||
```go
|
||
//常量的声明与变量类似,只不过使用 const 关键字。
|
||
//常量可以是字符、字符串、布尔值或数值。
|
||
//常量不能用 := 语法声明。
|
||
const Pi = 3.14
|
||
```
|
||
|
||
## 基本类型
|
||
|
||
```go
|
||
func main() {
|
||
a := 10
|
||
fmt.Printf("a 类型为: %T\n", a) // Printf格式化输出, %T占位
|
||
|
||
b := float32(10)
|
||
fmt.Printf("b 类型为: %T\n", b)
|
||
|
||
// 基本类型
|
||
// 整数
|
||
// int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr float32 float64 complex64
|
||
// byte // uint8 别名
|
||
// rune // int32 别名
|
||
|
||
// 浮点数
|
||
// float32 float64
|
||
|
||
// 复数
|
||
// complex64 complex128
|
||
|
||
// 布尔类型
|
||
// bool
|
||
|
||
// 字符串
|
||
// string
|
||
}
|
||
```
|
||
|
||
* 类型转换: Go 在不同类型的项之间赋值时需要显式转换
|
||
|
||
```go
|
||
i := 42
|
||
f := float64(i)
|
||
u := uint(f)
|
||
```
|
||
|
||
## 运算符
|
||
|
||
```go
|
||
// 加减乘除 + - * /
|
||
// 取余 %
|
||
// 比较 == != < <= > >=
|
||
// 按位与 &
|
||
// 逻辑运算符 && ||
|
||
// 逻辑非 !
|
||
```
|
||
|
||
## 分支
|
||
|
||
### if else
|
||
|
||
* 表达式外无需小括号 `( )`
|
||
|
||
```go
|
||
func main() {
|
||
a := 10
|
||
if a > 10 {
|
||
fmt.Println("a > 10")
|
||
} else if a > 5 {
|
||
fmt.Println("5 < a <= 10")
|
||
} else {
|
||
fmt.Println("a <= 5")
|
||
}
|
||
|
||
// 局部变量声明
|
||
// 还能在if中直接用 := 赋值 ; 该语句声明的变量作用域仅在 `if` 和 `else` 之内。
|
||
if b := 10; b > 10 {
|
||
fmt.Println("b > 10")
|
||
}
|
||
}
|
||
```
|
||
|
||
### switch
|
||
|
||
```go
|
||
// switch语句, case里不需要写break
|
||
c := 10
|
||
switch c {
|
||
case 10:
|
||
fmt.Println("c == 10")
|
||
case 5:
|
||
fmt.Println("c == 5")
|
||
default:
|
||
fmt.Println("c != 10 && c != 5")
|
||
}
|
||
```
|
||
|
||
## 循环
|
||
|
||
### for循环
|
||
|
||
* 三参数形式
|
||
* 和 C、Java、JavaScript 之类的语言不同,Go 的 `for` 语句后面的三个构成部分外没有小括号
|
||
|
||
```go
|
||
// 只有一种循环形式
|
||
// for循环
|
||
|
||
func main() {
|
||
for i := 0; i < 10; i++ {
|
||
fmt.Println(i)
|
||
}
|
||
// 可以用break来退出循环
|
||
for i := 0; i < 10; i++ {
|
||
if i == 5 {
|
||
break
|
||
}
|
||
fmt.Println(i)
|
||
}
|
||
// 可以用continue来跳过当前循环
|
||
for i := 0; i < 10; i++ {
|
||
if i == 5 {
|
||
continue
|
||
}
|
||
fmt.Println(i)
|
||
}
|
||
}
|
||
```
|
||
|
||
* range 形式(用于遍历数组、切片、map 等):
|
||
|
||
```go
|
||
// 同时需要索引和值
|
||
for index, value := range array {
|
||
// 使用 index 和 value
|
||
}
|
||
|
||
// 只需要值
|
||
for _, value := range array {
|
||
// 只使用 value
|
||
}
|
||
|
||
// 只需要索引
|
||
for index := range array {
|
||
// 只使用 index
|
||
}
|
||
```
|
||
|
||
### 模拟while循环
|
||
|
||
```go
|
||
// 模拟while循环
|
||
m := 1
|
||
for m < 5 {
|
||
fmt.Println(m)
|
||
m++
|
||
}
|
||
```
|
||
|
||
## 函数
|
||
|
||
```go
|
||
func sum(a int, b int) int {
|
||
return a + b
|
||
}
|
||
|
||
func swap(a, b int) (int, int) { // a b的类型是同一种;函数可以返回多个值
|
||
return b, a
|
||
}
|
||
|
||
func main() {
|
||
fmt.Println(sum(1, 2))
|
||
a, b := swap(1, 2)
|
||
fmt.Println(a, b)
|
||
}
|
||
```
|
||
|
||
没有参数的 `return` 语句会直接返回已命名的返回值,也就是「裸」返回值。
|
||
|
||
### defer 推迟
|
||
|
||
defer 语句会将函数推迟到外层函数返回之后执行。
|
||
|
||
推迟调用的函数其参数会立即求值,但直到外层函数返回前该函数都不会被调用。
|
||
|
||
```go
|
||
func main() {
|
||
defer fmt.Println("world")
|
||
|
||
fmt.Println("hello")
|
||
}
|
||
```
|
||
|
||
## 数组 array
|
||
|
||
> **同一类型**的元素的**固定长度**序列
|
||
|
||
* 在实践中,切片比数组更常用。
|
||
|
||
```go
|
||
// Array
|
||
var arr [5]int // 声明一个包含5个整数的数组
|
||
|
||
arr[0] = 10 // 将数组的第一个元素设置为10
|
||
fmt.Println(arr[0]) // 输出: 10
|
||
|
||
|
||
a := [3]int{1, 2, 3} // 数据的长度是固定且不可变的
|
||
fmt.Println(a)
|
||
```
|
||
|
||
## 切片 slice
|
||
|
||
> 切片是对数组的抽象,它提供了更强大的功能和灵活性
|
||
> **切片的长度可以动态变化**,这是它与数组的主要区别
|
||
|
||
* 切片就像数组的引用, 切片并不存储任何数据, 更改切片的元素会修改其底层数组中对应的元素, 和它共享底层数组的切片都会观测到这些修改。
|
||
|
||
```go
|
||
// Slice
|
||
// 如果要定义可变长度的数组,可以使用slice
|
||
|
||
var slice []int // 声明一个整数切片, 这里的方括号里没有长度, 数组则是要求固定长度
|
||
slice = make([]int, 5) // 创建一个长度为5的切片
|
||
|
||
// 切片也可以通过数组的一部分创建
|
||
arr := [5]int{1, 2, 3, 4, 5}
|
||
slice2 := arr[1:4] // 创建一个包含arr[1]到arr[3]的切片
|
||
|
||
// 用make函数创建, 初始长度为0
|
||
b := make([]int, 0)
|
||
//`make` 函数会分配一个元素为零值的数组并返回一个引用了它的切片
|
||
|
||
e := make([]int, 5) // len(e)=5, cap(e)=5, [0,0,0,0,0]
|
||
|
||
// 要指定它的容量,需向 make 传入第三个参数
|
||
a := make([]int, 0, 5) // len(a)=0, cap(a)=5, []
|
||
|
||
f := e[:2] // len(f)=2, cap(f)=5, [0, 0]
|
||
g := f[2:5]
|
||
|
||
// 追加元素
|
||
b = append(b, 1, 2, 3)
|
||
fmt.Println(b)
|
||
|
||
// 或者字面值
|
||
c := []int{1, 2, 3} // []里不指定长度
|
||
c[0] = 10 // 使用下标的方式修改
|
||
fmt.Println(c)
|
||
|
||
// 选出一个半闭半开区间,包括第一个元素,但排除最后一个元素。
|
||
d := a[1:3]
|
||
fmt.Println("d的值为:", d)
|
||
```
|
||
|
||
### 切片的默认行为
|
||
|
||
```go
|
||
var a [10]int
|
||
// 以下切片表达式和它是等价的
|
||
a[0:10]
|
||
a[:10]
|
||
a[0:]
|
||
a[:]
|
||
```
|
||
|
||
### 切片的长度 len 和容量 cap
|
||
|
||
* 切片的长度就是它所包含的元素个数。
|
||
* 切片的容量是从==切片的起始==位置到==底层数组==的末尾
|
||
* 切片 `s` 的长度和容量可通过表达式 `len(s)` 和 `cap(s)` 来获取。
|
||
|
||
```go
|
||
func main() {
|
||
s := []int{2, 3, 5, 7, 11, 13}
|
||
printSlice(s)
|
||
|
||
// 截取切片使其长度为 0
|
||
s = s[:0]
|
||
printSlice(s)
|
||
|
||
// 扩展其长度
|
||
s = s[:4]
|
||
printSlice(s)
|
||
|
||
// 舍弃前两个值
|
||
s = s[2:]
|
||
printSlice(s)
|
||
}
|
||
|
||
func printSlice(s []int) {
|
||
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
|
||
}
|
||
```
|
||
|
||
输出
|
||
|
||
```go
|
||
len=6 cap=6 [2 3 5 7 11 13]
|
||
len=0 cap=6 []
|
||
len=4 cap=6 [2 3 5 7]
|
||
len=2 cap=4 [5 7]
|
||
```
|
||
|
||
* `s[:i]` 创建一个从切片 s 的开始到索引 i 的新切片,不包括索引 i 的元素。
|
||
* `s[i:]` 创建一个从索引 i 到切片 s 的末尾的新切片,包括索引 i 的元素。
|
||
* 切片操作不会改变底层数组的容量,只是改变了切片的长度和起始位置。
|
||
|
||
使用场景:
|
||
|
||
* **预分配切片**:在一些场景中,你可能知道要存储的元素个数并且希望避免动态扩展切片,可以通过预分配足够的容量来避免性能损失。
|
||
|
||
```go
|
||
data := make([]int, 50) // 长度和容量都为50
|
||
```
|
||
|
||
* 优化性能:当你需要向切片中添加元素时,如果切片的容量已经满了,Go会创建一个新的底层数组并将现有元素复制过去。这是一个相对耗时的操作。如果你提前知道大致需要的容量,可以预先分配好,避免多次内存分配和数据复制操作。
|
||
|
||
```go
|
||
// 预先分配容量,避免多次扩容
|
||
s := make([]int, 0, 100) // 长度为0,容量为100
|
||
for i := 0; i < 100; i++ {
|
||
s = append(s, i)
|
||
}
|
||
|
||
```
|
||
|
||
* 切片的高效操作:利用切片的容量可以进行更高效的切片操作,而不必担心底层数组的频繁重新分配
|
||
|
||
```go
|
||
// 假设有一个初始切片
|
||
s := make([]int, 0, 10) // 长度为0,容量为10
|
||
|
||
// 可以安全地追加元素,直到达到容量限制
|
||
for i := 0; i < 10; i++ {
|
||
s = append(s, i)
|
||
}
|
||
|
||
// 如果继续追加元素,则会触发容量的扩展
|
||
s = append(s, 10) // 触发扩展,容量将翻倍
|
||
|
||
```
|
||
|
||
### nil 的切片
|
||
|
||
切片的零值是 `nil`。 (零值: 没有赋值时的默认值)
|
||
|
||
nil 切片的长度和容量为 0 且没有底层数组
|
||
|
||
```go
|
||
var s []int
|
||
fmt.Println(s, len(s), cap(s))
|
||
if s == nil {
|
||
fmt.Println("nil!")
|
||
}
|
||
```
|
||
|
||
### 二维切片
|
||
|
||
切片可以包含任何类型,当然也包括其他切片。
|
||
|
||
```go
|
||
// 创建一个井字棋(经典游戏)
|
||
board := [][]string{
|
||
[]string{"_", "_", "_"},
|
||
[]string{"_", "_", "_"},
|
||
[]string{"_", "_", "_"},
|
||
}
|
||
|
||
// 两个玩家轮流打上 X 和 O
|
||
board[0][0] = "X"
|
||
board[2][2] = "O"
|
||
board[1][2] = "X"
|
||
board[1][0] = "O"
|
||
board[0][2] = "X"
|
||
|
||
for i := 0; i < len(board); i++ {
|
||
fmt.Printf("%s\n", strings.Join(board[i], " "))
|
||
}
|
||
```
|
||
|
||
### 切片追加元素
|
||
|
||
切片(slice)的容量是动态增长的。当你向切片追加元素时,如果当前容量不足以容纳新元素,Go会自动扩容切片。切片的容量增长策略通常是当前容量的1.25倍
|
||
|
||
```go
|
||
package main
|
||
|
||
import "fmt"
|
||
|
||
func main() {
|
||
var s []int
|
||
printSlice(s)
|
||
|
||
// 可在空切片上追加
|
||
s = append(s, 0)
|
||
printSlice(s)
|
||
|
||
// 这个切片会按需增长
|
||
s = append(s, 1)
|
||
printSlice(s)
|
||
|
||
// 可以一次性添加多个元素
|
||
s = append(s, 2, 3, 4)
|
||
printSlice(s)
|
||
}
|
||
|
||
func printSlice(s []int) {
|
||
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
|
||
}
|
||
```
|
||
|
||
```go
|
||
// 输出
|
||
len=0 cap=0 []
|
||
len=1 cap=1 [0]
|
||
len=2 cap=2 [0 1]
|
||
len=5 cap=6 [0 1 2 3 4]
|
||
```
|
||
|
||
分析添加 2,3,4的过程
|
||
|
||
```
|
||
初始状态:长度2,容量2。
|
||
追加第一个元素(2):长度变为3,容量变为4。
|
||
追加第二个元素(3):长度变为4,容量仍为4。
|
||
追加第三个元素(4):长度变为5,容量变为6。
|
||
```
|
||
|
||
### range遍历
|
||
|
||
`for` 循环的 `range` 形式可遍历切片或映射。
|
||
|
||
当使用 `for` 循环遍历切片时,每次迭代都会返回两个值。 第一个值为当前元素的下标,第二个值为该下标所对应元素的一份副本。
|
||
|
||
```go
|
||
package main
|
||
|
||
import "fmt"
|
||
|
||
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
|
||
|
||
func main() {
|
||
for i, v := range pow {
|
||
fmt.Printf("2**%d = %d\n", i, v)
|
||
}
|
||
}
|
||
|
||
```
|
||
|
||
可以将下标或值赋予 `_` 来忽略它。
|
||
|
||
```go
|
||
for i, _ := range pow
|
||
|
||
for _, value := range pow
|
||
```
|
||
|
||
若你只需要索引,忽略第二个变量即可。
|
||
|
||
```go
|
||
for i := range pow
|
||
```
|
||
|
||
## 映射 map
|
||
|
||
> 一种无序的键值对集合。映射的键必须是唯一的,且通常是可比较的类型(如字符串、数字等)
|
||
> 相当于 js 里的对象
|
||
|
||
```go
|
||
// Map
|
||
var m1 map[string]int // 声明一个键为字符串、值为整数的映射
|
||
m1 = make(map[string]int) // 初始化映射
|
||
|
||
m := map[string]int{"a": 1, "b": 2, "c": 3} // []里是key的类型,后面是value的类型
|
||
fmt.Println(m)
|
||
fmt.Println(m["a"])
|
||
|
||
// 如果不需要初始值, 也可以用make函数创建
|
||
// 映射的零值是nil
|
||
n := make(map[string]int)
|
||
n["a"] = 1
|
||
n["b"] = 2
|
||
n["c"] = 3
|
||
fmt.Println(n)
|
||
```
|
||
|
||
### 删除元素
|
||
|
||
```go
|
||
delete(m, key)
|
||
```
|
||
|
||
### 检测某个键是否存在
|
||
|
||
```go
|
||
elem, ok = m[key]
|
||
|
||
// 若 `key` 在 `m` 中,`ok` 为 `true` ;否则,`ok` 为 `false`。
|
||
|
||
// 若 `key` 不在映射中,则 `elem` 是该映射元素类型的零值。
|
||
```
|
||
|
||
## 结构体 struct
|
||
|
||
```go
|
||
// Struct 结构体, 类似于js里的class类
|
||
type Student struct {
|
||
Name string
|
||
Age int
|
||
Sex string
|
||
}
|
||
|
||
s := Student{"jqtm", 18, "male"}
|
||
t := s
|
||
s.Age = 19 // 使用.修改值
|
||
fmt.Println(s)
|
||
fmt.Println(t) // s的修改不会影响t,因为t是s的副本
|
||
// Go 语言中结构体是值类型,赋值操作会创建一个新的副本,而不是引用
|
||
// 如果想要引用传递,可以使用指针
|
||
u := &s // go中通过&来获取变量指针地址
|
||
s.Age = 20
|
||
fmt.Println(s)
|
||
fmt.Println(*u) // 使用*来解引用
|
||
u.Age = 21 // 修改值时可以省略*, go会自动进行解引用
|
||
fmt.Println(s)
|
||
fmt.Println(*u) // 使用*来声明这是个指针,进行解引用
|
||
|
||
p := Point{1, 2}
|
||
p.SetPoint(3, 4)
|
||
fmt.Println(p)
|
||
```
|
||
|
||
### 结构体添加方法
|
||
|
||
```go
|
||
// 结构体也能添加方法
|
||
type Point struct {
|
||
X int
|
||
Y int
|
||
}
|
||
|
||
func (p *Point) SetPoint(x, y int) { // 大部分情况需要*,否则只是修改副本;方法名首字母大写; 方法是定义在结构体的外部
|
||
p.X = x
|
||
p.Y = y
|
||
}
|
||
```
|
||
|
||
## 接口
|
||
|
||
```go
|
||
// 接口
|
||
type Shape interface { // type 接口名 interface
|
||
Print() // 在里面定义方法,不需要func
|
||
}
|
||
|
||
// 结构体实现接口
|
||
type Rectangle struct{}
|
||
type Circle struct{}
|
||
|
||
|
||
// 定义同名的方法
|
||
func (r Rectangle) Print() {
|
||
fmt.Println("矩形")
|
||
}
|
||
|
||
func (c Circle) Print() {
|
||
fmt.Println("圆形")
|
||
}
|
||
|
||
func main() {
|
||
//接口的实现
|
||
var i Shape // 类型是接口
|
||
i = Rectangle{} // 赋值不同的结构,实现多态
|
||
i.Print()
|
||
i = Circle{}
|
||
i.Print()
|
||
}
|
||
```
|
||
|
||
## 错误处理
|
||
|
||
```go
|
||
func main() {
|
||
// 错误处理
|
||
n, err := fmt.Println("Hello") // 错误是通过函数的多个返回值实现的
|
||
// 需要判断err是否为nil或者有没有值
|
||
if err != nil {
|
||
// 执行正常代码
|
||
} else {
|
||
// 执行异常代码
|
||
}
|
||
}
|
||
```
|
||
|
||
## 多线程
|
||
|
||
```go
|
||
func func1() {
|
||
time.Sleep(500 * time.Millisecond) // Millisecond 毫秒
|
||
fmt.Println("func1")
|
||
}
|
||
|
||
func func2() {
|
||
fmt.Println("func2")
|
||
}
|
||
|
||
func func3(ch chan string) {
|
||
time.Sleep(2 * time.Second)
|
||
ch <- "func3" // 发送数据到管道
|
||
}
|
||
|
||
func func4(ch chan string) {
|
||
time.Sleep(1 * time.Second)
|
||
ch <- "func4"
|
||
}
|
||
|
||
func main() {
|
||
// 多线程 Goroutines
|
||
go func2() // 开启一个新的线程
|
||
func1() // 在主线程中执行,延迟了500毫秒,func2能够在主线程退出前打印
|
||
|
||
// 管道 Channels, 多个线程之间进行通信和同步
|
||
ch := make(chan string) // 使用make函数创建一个管道
|
||
go func3(ch) // 需要开启一个新的线程来接收数据
|
||
res1 := <-ch // 从管道中获取数据,接收操作是阻塞的,直到有数据发送过来
|
||
fmt.Println(res1) // 所以是先打印func3再打印func4
|
||
|
||
go func4(ch)
|
||
res2 := <-ch
|
||
fmt.Println(res2)
|
||
|
||
go func3(ch)
|
||
go func4(ch)
|
||
for i := 0; i < 2; i++ {
|
||
select { // select 可以同时接收多个管道数据,类似于switch
|
||
case res := <-ch:
|
||
fmt.Println(res)
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## 模块管理
|
||
|
||
```sh
|
||
go get github.com/hekmon/transmissionrpc/v3
|
||
|
||
# 如何卸载
|
||
# 删除 go.mod 中的依赖行
|
||
# 清理未使用的依赖
|
||
go mod tidy
|
||
```
|
||
|
||
## Vscode 断点调试
|
||
|
||
https://pengtech.net/golang/vscode_debug_golang.html
|
||
|
||
1. 安装 `delve`
|
||
|
||
```sh
|
||
https://github.com/go-delve/delve/tree/master/Documentation/installation
|
||
|
||
which dlv
|
||
```
|
||
|
||
1. 点击调试图标, 创建 `.vscode/launch.json`
|
||
|
||
```json
|
||
{
|
||
// Use IntelliSense to learn about possible attributes.
|
||
// Hover to view descriptions of existing attributes.
|
||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||
"version": "0.2.0",
|
||
"configurations": [
|
||
{
|
||
"name": "Launch golang program",
|
||
"type": "go",
|
||
"request": "launch",
|
||
"mode": "auto",
|
||
"program": "main.go"
|
||
}
|
||
]
|
||
}
|
||
``` |