ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Learn X in Y Minutes 之 Go 语言实战指南:从语法骨架到并发与 Web 编程

Learn X in Y Minutes 之 Go 语言实战指南:从语法骨架到并发与 Web 编程 文档教程【免费下载链接】learnxinyminutes-docsCode documentation written as code! How novel and totally my idea!项目地址https://gitcode.com/gh_mirrors/le/learnxinyminutes-docs点击查看免费下载本篇指南以 learnxinyminutes-docs 仓库中的匈牙利语版 Go 教程 hu/go.md 为骨架并对照英文原版 go.md 与中文版 zh-cn/go.md 展开。你将从零搭建 Go 程序的完整骨架掌握变量与类型系统、流程控制、接口、错误处理、goroutine 并发模型最终用标准库写出一个可运行的 HTTP 服务器。读完即可把整套示例代码复制到本地或 Go Playground 直接运行验证。Go 语言的设计哲学为把事做完而生文档开篇即点明 Go 的定位它不是编程语言理论的最新潮流而是一套解决真实业务问题的务实工具匈牙利语原文az életszerű feladatok könnyebb elvégzése miatt született意为为了更轻松地完成现实任务而生。其核心特征可归纳为四点C 风格语法 静态类型上手成本低编译期即可捕获大量类型错误编译快、执行快编译产物是静态可执行文件部署简单易理解的并发模型通过 goroutine 与 channel 传递消息天然适配当今多核 CPU适合构建大型系统强大的标准库从字符串转换、文件读写到内置 Web 服务器覆盖面广这也是 Go 社区持续壮大的原因。这套教程本身延续了 learnxinyminutes-docs 仓库以带注释的可运行代码讲语言的写作方式参见 README.md 对项目形态的描述因此下文所有概念都以代码为核心载体。程序骨架package、import 与 main 入口Go 的每个源文件都以package子句开头类似于 Python 的包管理其中package main是特殊名称编译它会生成可执行文件而非库。import语句声明本文件引用的外部包// 单行注释 /* 多行 注释 */ // 每个源文件都以 package 子句开头package main 是特殊名称 // 编译它生成可执行文件而非库。 package main // import 声明本文件要使用的包 import ( fmt // Go 标准库的一部分 net/http // 内置 Web 服务器 strconv // 字符串转换包 ) // 函数声明main 是程序的入口点 func main() { // Println 把参数输出到标准输出调用其他包的函数必须带包名前缀 fmt.Println(Hello world!) // 调用本包内的另一个函数 beyondHello() }英文原版 go.md 还补充了构建标签build tag的用法以//go:build开头的行注释可通过go build -tagsfoo bar控制条件编译必须放在package子句之前并紧跟空行。这是大型项目按环境裁剪代码的常用手段。函数、变量声明与多返回值函数参数放在圆括号中即使没有参数也必须保留空括号()func beyondHello() { var x int // 变量声明使用前必须先声明 x 3 // 变量赋值 // 短声明 y : 4 会根据右侧值自动推断类型并完成声明与赋值 y : 4 sum, prod : learnMultiple(x, y) // 函数可以有多个返回值 fmt.Println(sum:, sum, prod:, prod) // 简单输出 learnTypes() } // 函数可以有多个参数和多个返回值这里的 x、y 是参数 // sum、prod 是返回值标识符且都声明为 int 类型 func learnMultiple(x, y int) (sum, prod int) { return x y, x * y // 同时返回两个值 /* 等价写法 sum x y prod x * y return 使用命名返回值时裸 return 会返回这些变量的当前值 */ }多返回值是 Go 的招牌特性之一它让结果 错误成对返回成为语言惯例详见后文错误处理一节。中文版 zh-cn/go.md 还单独演示了命名返回值的进阶用法func learnNamedReturns(x, y int) (z int) { z x * y return // 隐式返回 z因为前面已经为它命名 }内置类型全景字符串、rune、浮点与复数Go 源码与字符串均为 UTF-8 编码因此非 ASCII 字符是一等公民func learnTypes() { // 短声明在大多数情况下够用 s : Tanulj Go-t! // string 类型 s2 : A raw stringekben lehetnek újsorok is! // 反引号原始字符串可以跨行类型与 s 相同 g : Σ // rune 类型等价于 int32 的别名存一个 UTF-8 码点 f : 3.14159 // float64符合 IEEE-754 标准的 64 位浮点数 c : 3 4i // complex128内部由两个 float64 表示 // var 语法 显式类型 var u uint 7 // 无符号整数与 int 一样尺寸由实现决定 var pi float32 22. / 7 // 短声明同样可以做类型转换 n : byte(\n) // byte 是 uint8 的别名 ... }原文档特别强调未使用的变量在 Go 中是编译错误而非警告。下划线_可以使用变量同时丢弃其值这一惯用法贯穿整个语言_, _, _, _, _, _, _, _, _, _ s2, g, f, u, pi, n, a3, s4, bs // 输出当然也算使用 fmt.Println(s, c, a4, s3, d2, m)数组、切片与 map值语义与引用语义的分水岭数组的大小在编译期固定切片slice则动态增长日常开发中切片的使用频率远高于数组// 数组编译期固定大小 var a4 [4]int // 4 个 int 的数组全部初始化为 0 a3 : [...]int{3, 1, 5} // 3 个 int 的数组按字面量初始化 // 切片大小动态注意没有省略号 s3 : []int{4, 5, 9} // 与 a3 对比这里没有 ... s4 : make([]int, 4) // 用 make 分配 4 个 int全部初始化为 0 var d2 [][]float64 // 仅声明尚未分配内存 bs : []byte(a slice) // 类型转换语法英文原版补充了关键的语义差异数组是值语义切片是引用语义。复制数组会产生独立实例修改副本不影响原数组而复制切片只是新增一个指向同一底层数据的引用。append用于向切片追加元素可一次追加多个元素也可用slice...展开另一个切片后整体追加s : []int{1, 2, 3} // 长度 3 的切片 s append(s, 4, 5, 6) // 追加 3 个元素长度变为 6 fmt.Println(s) // [1 2 3 4 5 6] s append(s, []int{7, 8, 9}...) // 展开切片字面量再追加 fmt.Println(s) // [1 2 3 4 5 6 7 8 9]map 是动态增长的关联数组类似其他语言的 hash 或 dictionarym : map[string]int{three: 3, four: 4} m[one] 1英文原版还提示访问 map 中不存在的键会返回该类型的零值map[string]int即 0要判断键是否存在需配合, ok惯用法这部分在错误处理一节会详细展开。指针与内存模型有指针无指针运算Go 完全支持垃圾回收拥有指针但没有指针算术——你仍可能因空指针犯错但不会像 C/C 那样因指针自增而越界对局部变量取地址并返回也是安全的// 命名返回值 p、q 的类型是指向 int 的指针 func learnMemory() (p, q *int) { p new(int) // 内置 new 函数为类型分配内存并返回指针 // 分配的 int 已被清零p 不再是空指针 s : make([]int, 20) // 为 20 个 int 分配一块连续内存 s[3] 7 // 给其中一个赋值 r : -2 // 声明另一个局部变量 return s[3], r // 取变量地址 }调用侧使用*解引用p, q : learnMemory() // 声明两个指向 int 的指针 fmt.Println(*p, *q) // * 解引用打印两个 int 的值new(T)与make(T)的分工值得注意new只分配并清零内存返回指针make专用于切片、map 和 channel 的初始化——这正是文档中同一个 make 函数既能造切片又能造 channel的原因。流程控制if、switch、for 与 rangeGo 的流程控制有两个显著风格大括号必须、圆括号免谈for是唯一的循环关键字但有多种形态。代码格式由官方工具go fmt统一无需纠结风格。func expensiveComputation() int { return 1e6 } func learnFlowControl() { // if 必须带大括号不需要圆括号 if true { fmt.Println(megmondtam) } if false { // else 分支 } else { // if/else 写法 } // 长分支链请用 switchcase 不会穿透无需写 break x : 1 switch x { case 0: case 1: // 命中即停 case 2: // 不会执行到这里 } // for 同样不需要圆括号 for x : 0; x 3; x { // 是语句而非表达式 fmt.Println(iteráció, x) } // 注意循环内的 x 是局部变量此处 x 仍 1 // for 是唯一循环关键字但有多种形态 for { // 死循环 break // 骗你的 continue // 永远不会执行 } // range 可遍历 array、slice、string、map、channel // channel 返回一个值其余类型返回键值对 for key, value : range map[string]int{one: 1, two: 2, three: 3} { fmt.Printf(key%s, value%d\n, key, value) } // 只想要值时用下划线丢弃索引 for _, name : range []string{Bob, Bill, Joe} { fmt.Printf(Hello, %s\n, name) } // 与 for 一样if 里也可以用 : 先声明赋值再判断 if y : expensiveComputation(); y x { x y } ... }英文原版 go.md 还补充了两个进阶形态switch支持单 case 多值匹配case 1, 2:与可选的default分支且存在fallthrough关键字强制穿透type switchswitch c : data.(type)则按接口的动态类型分发是处理interface{}的利器。闭包、函数字面量与 goto函数字面量在 Go 中即闭包可以捕获外层作用域的变量xBig : func() bool { return x 100 // 引用上面 switch 之前声明的 x } fmt.Println(xBig:, xBig()) // truex 刚被赋为 1e6 x / 1e5 // 现在 x 10 fmt.Println(xBig:, xBig()) // 10 100变为 false函数字面量还可以定义后立即调用并作为参数传递只要满足立即调用且返回类型匹配两个条件fmt.Println(两数相加乘二: , func(a, b int) int { return (a b) * 2 }(10, 2)) // 立即以 10 和 2 调用 // 两数相加乘二: 24当需要时老派的goto依然可用本教程用它作为跳转到下一主题的仪式感收尾goto love love: learnInterfaces() // 有趣的部分开始了函数工厂与 defer两个函数的高级玩法函数工厂——返回函数的函数是装饰器模式在 Go 中的替代品英文原版称其func returning func is fun(3)(3)func learnFunctionFactory() { // 下面两种写法等价第二种更实用 fmt.Println(sentenceFactory(summer)(A beautiful, day!)) d : sentenceFactory(summer) fmt.Println(d(A beautiful, day!)) fmt.Println(d(A lazy, afternoon!)) } func sentenceFactory(mystring string) func(before, after string) string { return func(before, after string) string { return fmt.Sprintf(%s %s %s, before, mystring, after) } }defer——把函数调用压入延迟队列在外层函数返回之后按 LIFO后进先出顺序执行。最典型的用途是打开文件后紧挨着写关闭让关闭逻辑与打开逻辑的代码彼此靠近func learnDefer() (ok bool) { defer fmt.Println(deferred statements execute in reverse (LIFO) order.) defer fmt.Println(\nThis line is being printed first because) // defer 常用于关闭文件使关闭代码紧邻打开代码 return true }接口与结构体隐式实现与 StringerGo 的接口是隐式实现的——类型只需实现接口中声明的全部方法即可无需显式声明implements。定义接口、结构体与方法接收者receiver// 定义 Stringer 接口只有一个方法 String type Stringer interface { String() string } // 定义 pair 结构体两个 int 字段 x 和 y type pair struct { x, y int } // 为 pair 定义方法p 是接收者pair 因此实现了 Stringer 接口 func (p pair) String() string { // Sprintf 是 fmt 包的公有函数行为与 C 语言对应函数一致 // 用点号访问结构体 p 的字段 return fmt.Sprintf((%d, %d), p.x, p.y) }接口变量可以持有任何实现了该接口的具体类型调用其方法产生多态效果fmt包还会自动调用对象的String方法获取可打印表示类似 Java 的toString()func learnInterfaces() { p : pair{3, 4} // 花括号是结构体字面量按字段顺序初始化 fmt.Println(p.String()) // 调用 p 的 String 方法 var i Stringer // 声明 i 为 Stringer 接口类型 i p // 合法pair 实现了 Stringer 接口 fmt.Println(i.String()) // 通过接口调用结果同上 // fmt 包的函数会自动调用 String 方法获取对象的文本表示 fmt.Println(p) // 结果同上Println 自动调用 String fmt.Println(i) // 同上 }英文原版还补充了变长参数variadic params用...声明可接收任意数量参数any是interface{}的别名func learnVariadicParams(myStrings ...any) { for _, param : range myStrings { // 下划线丢弃索引 fmt.Println(param:, param) } fmt.Println(params:, fmt.Sprintln(myStrings...)) // 展开后再传递 }错误处理, ok 惯用法与 error 值Go 没有异常机制错误以普通返回值传递。判断 map 键是否存在用, ok惯用法func learnErrorHandling() { m : map[int]string{3: three, 4: four} if x, ok : m[1]; !ok { // ok 为 false因为 1 不在 map 中 fmt.Println(nincs meg) // 没找到 } else { fmt.Print(x) // 若存在x 就是对应值 } }错误值比是否 ok携带更多信息。strconv.Atoi解析失败时返回的 error 可直接打印if _, err : strconv.Atoi(non-int); err ! nil { // _ 丢弃无用的返回值 // 输出类似strconv.ParseInt: parsing non-int: invalid syntax fmt.Println(err) } }这也是learnMultiple等函数返回多值的现实意义函数签名上结果 错误成对出现调用方用if err ! nil显式处理语言层面保证了错误不被悄悄吞掉。并发编程goroutine、channel 与 selectGo 的并发模型建立在通过通信共享内存之上。c是一个 channel即并发安全的通信对象// c 是 channel并发安全的通信对象 func inc(i int, c chan int) { c - i 1 // - 出现在左侧是发送操作符把 i1 送入 channel }go语句启动 goroutine三个 goroutine 并发地向同一个 channel 发送结果——如果机器支持且配置得当甚至可能并行执行func learnConcurrency() { // 与之前创建切片用的是同一个 make 函数 // make 负责分配并初始化切片、map 和 channel c : make(chan int) // 启动三个并发 goroutine全部向同一 channel 发送 go inc(0, c) // go 语句启动新 goroutine go inc(10, c) go inc(-805, c) // 从 channel 读取 3 次结果并打印 // 结果到达的顺序完全不可预知 fmt.Println(-c, -c, -c) // - 在右侧是接收操作符channel 可以组合出更复杂的通信拓扑channel 里传 channel匿名 goroutine 仅用于发送一个值cs : make(chan string) // 又一个 channel这次传字符串 cc : make(chan chan string) // channel 的 channel go func() { c - 84 }() // 启动 goroutine 只为发一个数字 go func() { cs - wordy }() // 同理向 cs 发字符串select语法像switch但每个 case 都是 channel 操作它会从已就绪可通信的 case 中随机挑选一个执行select { case i : -c: // 接收到的值可赋给变量 fmt.Println(ez egy, i) case -cs: // 也可以直接丢弃接收值 fmt.Println(ez egy string volt) case -cc: // 空 channel永远不会有数据到达 fmt.Println(sose futok le :( ) } // 至此c 或 cs 中必有一个 goroutine 已完成通信 // 另一个则继续阻塞等待。 }用标准库搭建 Web 服务器ListenAndServe 与 ServeHTTPnet/http包让 Web 编程变得出乎意料地简单——一个函数即可启动服务器func learnWebProgramming() { // ListenAndServe 第一个参数是监听地址TCP 端口 // 第二个参数是接口具体来说是 http.Handler 接口 err : http.ListenAndServe(:8080, pair{}) fmt.Println(err) // 别忘了打印潜在错误 }让pair实现http.Handler只需实现其唯一方法ServeHTTP// 让 pair 实现 http.Handler实现其唯一方法 ServeHTTP func (p pair) ServeHTTP(w http.ResponseWriter, r *http.Request) { // 通过 http.ResponseWriter 向每个连接返回数据 w.Write([]byte(Megtanultad a Go-t Y perc alatt!)) }英文原版进一步展示了完整的服务器 客户端闭环服务器在 goroutine 中启动主流程随后用http.Get请求自身并读取响应还示范了defer resp.Body.Close()的典型资源管理写法func learnWebProgramming() { go func() { err : http.ListenAndServe(:8080, pair{}) fmt.Println(err) // 不要无视错误 }() requestServer() } func requestServer() { resp, err : http.Get(http://localhost:8080) fmt.Println(err) defer resp.Body.Close() // 读取完响应后关闭 Body body, err : io.ReadAll(resp.Body) fmt.Printf(\nWebserver said: %s, string(body)) }这里的核心启发是接口http.Handler与结构体方法ServeHTTP在前面几节铺垫的语法能力在 Web 编程中直接转化为生产力——教程内部的知识是环环相扣的。组合成完整程序一份可运行的 Go 入门代码将上述片段按教程顺序组装即可得到一份完整可运行的 Go 源文件对应 hu/go.md 的核心代码块也是英文原版 go.md 的learngo.go形态。建议读者新建learngo.go按顺序粘贴package main、import、main、beyondHello、learnTypes、learnMemory、learnFlowControl、learnInterfaces、learnErrorHandling、learnConcurrency、learnWebProgramming及pair类型定义在终端执行go run learngo.go编译运行若在浏览器中可直接粘贴到 Go Playground 在线运行无需安装环境观察输出顺序fmt.Println(-c, -c, -c)的三次结果顺序不可预测这正是并发随机性的直观体现若希望服务器演示生效确保learnWebProgramming中的ListenAndServe正常监听 8080 端口http.Get请求自身端口即可验证闭环。编译前请留意文档强调的规则所有声明的变量都必须被使用否则编译直接报错代码格式可用go fmt一键标准化这也是仓库内各语言版本教程反复强调的工程习惯。进一步学习教程末尾给出了官方推荐的学习路径均源自原文档的További olvasmányok章节官方资料一切 Go 相关内容的源头是 Go 官方网站可在其中跟随教程、在线试玩并阅读大量资料语言规范强烈建议通读 Go 语言规范它相对简短且收获巨大标准库源码想深入掌握 Go 的最佳实践可从标准库源码入手——在 文档 中点击函数名即可直达对应实现这是学习地道的 Go 风格与惯用法的最好途径示例学习若想按主题查找类似本篇的速成描述Go by Example 提供了大量主题化示例。此外本仓库本身就是一份多语言教材英文原版 go.md 包含构建标签、类型 switch、值语义/引用语义对比、变长参数、defer、函数工厂与服务器自请求等更丰富的补充内容中文版 zh-cn/go.md 对每个代码片段配有中文注解可作为对照阅读的第三份资料。若想参与这类以代码教语言的文档共建可参考仓库的 CONTRIBUTING.md 与 README.md 了解贡献规范。赞分享文档教程【免费下载链接】learnxinyminutes-docsCode documentation written as code! How novel and totally my idea!项目地址https://gitcode.com/gh_mirrors/le/learnxinyminutes-docs点击查看免费下载相关推荐Jest Timer Mocks 完全指南用假定时器精确控制测试时间Jest Timer Mocks 完全指南用假定时器精确控制测试时间 导读 在 Jest 测试中 setTimeout 、 setInterval 等原生定文档教程Learn X in Y Minutes 之 SmallBASIC从语法速览到图形与 JSON 实战Learn X in Y Minutes 之 SmallBASIC从语法速览到图形与 JSON 实战 本指南基于 learnxinyminutes docs文档教程Erlang 语言实战入门从模式匹配到 Actor 并发的完整指南Learn X in Y minutes 法语版精讲Erlang 语言实战入门从模式匹配到 Actor 并发的完整指南Learn X in Y minutes 法语版精讲 本文基于开源仓库 learnxin文档教程上一篇PdfGptIndexer基于PDF的智能索引与搜索工具下一篇从0到1搭建管理后台shadcn-ui-sidebar与shadcn/ui组件的完美配合创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表