GO 语言学习的五个阶段(带例子) 返回原文
英文原文:The 5 stages of learning Go (with examples)
Francesc (@francesc) is a member of the Go core team and a developer advocate for Google Cloud Platform. He’s a lover of programming languages, a master of technical instruction at Google, and one of the creators of the Go tour. This talk was inspired by another talk from Raquel Vélez at JSConf. Slides for this talk have been posted here. Sourcegraph is building the next generation of programming collaboration tools for searching, exploring, and reviewing code. We attended GopherCon India to share how we use Go and learn from how others are using it, and were honored to coordinate the liveblog of the talks.
译者信息 Francesc (@francesc) 是 Go 核心团队的一员, 是提倡 Google Cloud 平台的开发者. 他是一个编程语言的爱好者, Google的技术指导大师, Go tour的创造者之一. 这个讨论的灵感来自于另一个 Raquel Vélez 在 JSConf. Slides 的讨论,这个讨论已经发到了这里. Sourcegraph 是下一代编程协作工具, 用于搜索, 探索, 和审查代码. 我们参加GopherCon India 来分享我们是怎样使用 Go 并学习别人是怎样使用它的, 对配合liveblog的这次讨论我们深感荣幸.
As a developer advocate on the Go team, Francesc has probably interacted with more Go programmers than almost anyone else in the world. From this unique vantage point, he has seen 5 general stages in the evolution of programmers working in Go. These stages apply to how programmers progress through other languages, too. Understanding where you are in this evolution can help you figure out the most effective ways to improve and avoid common pitfalls that befall students of programming languages at every level. Editor’s note: this post has interactive code snippets for each phase of this evolution. Clicking on any function name jumps you to the definition in code for further exploration. Scroll down to see them.
译者信息 作为Go团队的开发者之一,Francesc可能比世界上其他人接触到的Go语言程序员都要多。正因为有了这样的有利条件,他把Go语言的学习过程划分为5个阶段。 这些阶段对于其他语言的学习也是成立的。理解自己处于哪个阶段,可以帮助你找到提高自己的最有效的方法,也可以避免每个阶段学习过程中的常见陷阱。 编者按:这篇文章对于每一个学习阶段都给出了交互式的代码片段。点击函数名你就可以跳到具体的函数定义,方便进行深入的研究。请看下文。
Here the 5 phases in the evolution of a Gopher:
- Phase 1 (the newcomer): You just learned the language. You’ve gone through a few tutorials or workshops, you understand basic syntax, and you can write short snippets of code.
- Phase 2 (the explorer): You can write a complete program, but don’t understand some more advanced language features like channels. You haven’t yet written a large project in Go.
- Phase 3 (the builder): You’re proficient in Go, you’re using Go in production to solve a specific and complete problem. You have a strong command of the common patterns and idioms in the language community. Go is a strong tool in your programming toolbelt.
- Phase 4 (the expert): You understand the design choices and motivations behind the language. You grok the philosophy of simplicity and composability.
- The advocate: You’re actively sharing your knowledge and understanding of the language with others. You’re a voice in the community, a participant on mailing lists and chatrooms, and you’re giving talks at conferences. Being an advocate is not really a separate stage, but a role you can fill at any of the other stages.
译者信息 这里是GO程序员的五个进化阶段:
- 第一个阶段(菜逼): 刚刚学习了这门语言。 已经通过一些教程或者培训班了解基本的语法,可以写短的代码片段。
- 第二个阶段 (探索者): 可以写一个完整的程序,但不懂一些更高级的语言特征,比如“channels”。还没有使用GO写一个大项目。
- 第三个阶段(大手): 你能熟练的使用Go, 能够用GO去解决,生产环境中一个具体和完整的问题。已经形成了一套自己的惯用法和常用代码库。在你的编码方案中Go是一个非常好用的工具。
- 第四阶段 (大神): 绝逼清楚Go语言的设计选择和背后的动机。能理解的简洁和可组合性哲学。
- 布道师: 积极地与他人分享关于Go语言知识和你对Go语言的理解。在各种合适的场所发出自己的声音, 参与邮件列表、建立QQ群、做专题报告。成为一个布道者不见得是一个完全独立的阶段,这个角色可以在上述的任何一个阶段中。
Phase 1: the newcomer Newcomers are at the stage where they’re creating small programs and toy projects in Go. They should take advantage of the Go tour, the Go playground, the docs, and the mailing list (golang-nuts). func main() { fmt.Println(stringutil.Reverse("!selpmaxe oG ,olleH"))} View in context func main in golang/example on ✱ Sourcegraph An interactive snippet from hello.go in the golang/example repository, which contains simple examples of small Go programs. Click around to explore the code.
译者信息
第一阶段: 菜逼 菜鸟在这个阶段使用Go去创建一些小项目或者玩具项目。他们应该会利用到Go tour, Go playground, Go文档, 和邮件列表(golang-nuts). func main() { fmt.Println(stringutil.Reverse("!selpmaxe oG ,olleH"))} 查看上下文
func main in golang/example on ✱ Sourcegraph
这是Go语言写的简单例子,这个代码段来自golang/example 代码库里面的 hello.go 。 点击就可以查看完整代码撸。
An important skill newcomers should develop is learning how to ask questions. A lot of people new to the mailing list say things like, “Hey, this doesn’t work,” without providing enough context to enable others to understand and help fix their problem. At the opposite end of the spectrum are those who copy and paste hundreds of lines of code into a forum post and haven’t invested the time to come up with a focused example that illustrates their problem. Also, you should rarely copy and paste code directly into the forum. Instead use the “share” button of the Go playground to link to snippets of code that others can edit and run directly in their browser.
译者信息 一项重要的技能,新人应该试着学习如何正确提问。很多新人在邮件列表里面这样说“嘿,这报错了”,这并没有提供足够的信息,让别人能理解并帮助他们解决问题。别人看到的是一个粘贴了几百行的代码的帖子,并没有花费精力来重点说明所遇到的问题。 所以, 应该尽量避免直接粘贴代码到论坛。而应该使用可以编辑并且可以在浏览器中直接运行的Go playground的“分享”按钮链接到代码片段。
Phase 2: the explorer The explorer has written some small projects in Go, but still gets lost sometimes. They might not fully understand or know how to use more advanced language features like channels. They still have many things to learn, but know enough to build useful things. They are starting to get a sense of the potential of Go and are excited about what they’ll be able to build in the language.
Within the explorer phase, you typically go through two steps. First, you ascend to the Peak of Inflated Expectations. You think you can do everything in Go, but without really understanding or grokking the Go ethos yet. You are probably trying to apply the idioms and patterns you know from other languages in Go code, and you don’t yet have a strong sense of what constitutes idiomatic Go. You try to undertake tasks like “migrate framework X from language Y to Go”.
译者信息
Phase 2: the explorer 探索者已经可以使用Go写一些小的软件,但有时仍然会有些迷茫。他们可能不完全明白怎么使用Go的高级特性,比如通道。虽然他们还有很多东西要学习,但已掌握的足够做一些有用的事情了!他们开始对Go的潜能有感觉了,并对它们能使用Go创建的东西感到兴奋。
在探索阶段通常会经历两个步骤。第一,膨胀的预期达到顶点,你觉得可以用Go做所有的事情,但还并不能明白或领悟到Go的真谛。你大概会用所熟悉的语言的模式和惯用语来写Go代码,但对于什么是地道的Go,还没有比较强烈的感觉。你开始尝试着手干这样的事情--“迁移架构X,从Y语言到Go语言”。
Following the Peak of Inflated Expectations is the Trough of Disillusionment. You miss feature X from language Y. You haven’t fully bought into idiomatic Go. You are still trying to write in the style of other programming languages and are getting frustrated. You may use the reflect and unsafe packages a lot. This is not idiomatic Go. Idiomatic Go tends to avoid things that are too “magical”. The Martini web framework is an example of a project that exemplifies the explorer phase. Martini was an early Go web framework that adopted a lot of concepts from Ruby web frameworks (like dependency injection). It received a lot of excitement from the community initially, but eventually had many issues around performance and debuggability. Jeremy Saenz (@codegangsta), the creator of Martini, responded constructively to feedback from the Go community and wrote another library called Negroni that follows more idiomatic Go conventions. func (m *Martini) RunOnAddr(addr string) { // TODO: Should probably be implemented using a new instance of http.Server in place of // calling http.ListenAndServer directly, so that it could be stored in the martini struct for later use. // This would also allow to improve testing when a custom host and port are passed.
logger := m.Injector.Get(reflect.TypeOf(m.logger)).Interface().(*log.Logger) logger.Printf("listening on %s (%s)\n", addr, Env) logger.Fatalln(http.ListenAndServe(addr, m))} View in context (*Martini).RunOnAddr in go-martini/martini on ✱ Sourcegraph Interactive code snippet from the Martini web framework, an example of non-idiomatic Go. Note the dependency injection implemented via the reflect package. func TestNegroniServeHTTP(t *testing.T) { result := "" response := httptest.NewRecorder()
n := New() n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { result += "foo" next(rw, r) result += "ban" })) n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { result += "bar" next(rw, r) result += "baz" })) n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { result += "bat" rw.WriteHeader(http.StatusBadRequest) }))
n.ServeHTTP(response, (*http.Request)(nil))
expect(t, result, "foobarbatbazban") expect(t, response.Code, http.StatusBadRequest)} View in context func TestNegroniServeHTTP in codegangsta/negroni on ✱ Sourcegraph Interactive code snippet demonstrating use of the Negroni library, an example of more idiomatic Go.
译者信息 到达预期膨胀的顶点之后,你会遇到理想幻灭的低谷。你开始想念语言Y的特性X,此时你还没有完全的掌握地道的Go。你还在用其他编程语言的风格来写Go语言的程序,你甚至开始觉得沮丧。你可能在大量使用reflect和unsafe这两个包,但这不是地道的Go。地道的Go不会使用那些魔法一样的东西。 这个探索阶段产生的项目的一个很好的例子就是Martini Web框架。Martini是一个Go语言的早期Web框架,它从Ruby的Web框架当中吸收了很多思想(比如依赖注入)。最初,这个框架在社区中引起了强烈的反响,但是它逐渐在性能和可调试性上受到了一些批评。Martini框架的作者Jeremy Saenz积极响应这些来自Go社区的反馈,写了一个更加符合Go语言规范的库Negroni
func (m *Martini) RunOnAddr(addr string) { // TODO: Should probably be implemented using a new instance of http.Server in place of // calling http.ListenAndServer directly, so that it could be stored in the martini struct for later use. // This would also allow to improve testing when a custom host and port are passed.
logger := m.Injector.Get(reflect.TypeOf(m.logger)).Interface().(*log.Logger) logger.Printf("listening on %s (%s)\n", addr, Env) logger.Fatalln(http.ListenAndServe(addr, m))} 查看上下文
(*Martini).RunOnAddr in go-martini/martini on ✱ Sourcegraph
来自Martini框架的交互式代码片段,它是不地道的Go的例子。注意用反射包实现的依赖注入
func TestNegroniServeHTTP(t *testing.T) { result := "" response := httptest.NewRecorder()
n := New() n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { result += "foo" next(rw, r) result += "ban" })) n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { result += "bar" next(rw, r) result += "baz" })) n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { result += "bat" rw.WriteHeader(http.StatusBadRequest) }))
n.ServeHTTP(response, (*http.Request)(nil))
expect(t, result, "foobarbatbazban") expect(t, response.Code, http.StatusBadRequest)} 查看上下文
func TestNegroniServeHTTP in codegangsta/negroni on * Sourcegraph
来自Negroni库的交互式代码片段,它是地道的Go的例子
Many other languages rely heavily on third-party libraries to deliver core functionality like HTTP handling. One thing that distinguishes Go is the power of its standard library. If you think the Go standard library is not powerful enough to do what you want to do, you might be wrong. The Go standard library is incredibly powerful, and it’s worth reading through its code to learn the patterns of how it implements things. func (srv *Server) ListenAndServe() error { addr := srv.Addr if addr == "" { addr = ":http" } ln, err := net.Listen("tcp", addr) if err != nil { return err } return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})} View in context (*Server).ListenAndServe in golang/go on ✱ Sourcegraph An interactive snippet of the ListenAndServe method in the Go standard library. If you’ve written Go code, you’ve probably called this function many times, but have you bothered to see how it’s written? Go ahead and click around in the snippet above. The disillusionment of the Trough of Disillusionment comes from the fact that you are still thinking in the patterns of other languages and you haven’t yet fully explored much of what Go offers. Here are some fun things you can do to avoid stalling and dive further into cool things you can do in the language.
译者信息 其他语言在提供一些核心功能,比如HTTP处理的时候,往往需要依赖第三方库。但是Go语言在这一点上很不同,它的标准库非常强大。如果你认为Go标准库没有强大到可以做你想做的事情,那么我说你错了。Go语言标准库难以置信的强大,值得你花时间阅读它的代码,学习它实现的模式。 func (srv *Server) ListenAndServe() error { addr := srv.Addr if addr == "" { addr = ":http" } ln, err := net.Listen("tcp", addr) if err != nil { return err } return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})} 在上下文中查看
(Server).ListenAndServe in golang/go on Sourcegraph
Go标准库中的ListenAndServe函数片段。如果你写过Go程序,你可能已经调用过这个函数很多次了,但是你曾经花时间看过它的实现么?去点击上面的代码片段吧。 幻灭的低谷中的幻灭感来自于这样的事实:你还在用其他语言的模式来想问题,而且你还没有完全探索过Go能提供给你什么。下面是一些好玩的事情,你可以做一下来打破困境,进一步探索这门语言中好玩的事。
go generate Check out go generate. go generate is a command that you can use to auto-generate Go code. You can use it in conjunction with metaprograms like jsonenums (a library to auto-generate JSON marshalling boilerplate code for enum types) to automate the writing of tedious code. The Go standard library has a great API for parsing the AST, which makes metaprogramming tools simple and easy to write. This is a point covered in 2 other talks at the conference (Practical Metaprogramming in Go and Embracing the Standard Library). func main() { flag.Parse() if len(*typeNames) == 0 { log.Fatalf("the flag -type must be set") } types := strings.Split(*typeNames, ",")
// Only one directory at a time can be processed, and the default is ".". dir := "." if args := flag.Args(); len(args) == 1 { dir = args[0] } else if len(args) > 1 { log.Fatalf("only one directory at a time") }
pkg, err := parser.ParsePackage(dir, *outputSuffix+".go") if err != nil { log.Fatalf("parsing package: %v", err) }
var analysis = struct { Command string PackageName string TypesAndValues map[string][]string }{ Command: strings.Join(os.Args[1:], " "), PackageName: pkg.Name, TypesAndValues: make(map[string][]string), }
// Run generate for each type. for _, typeName := range types { values, err := pkg.ValuesOfType(typeName) if err != nil { log.Fatalf("finding values for type %v: %v", typeName, err) } analysis.TypesAndValues[typeName] = values
var buf bytes.Buffer if err := generatedTmpl.Execute(&buf, analysis); err != nil { log.Fatalf("generating code: %v", err) }
src, err := format.Source(buf.Bytes()) if err != nil { // Should never happen, but can arise when developing this code. // The user can compile the output to see the error. log.Printf("warning: internal error: invalid Go generated: %s", err) log.Printf("warning: compile the package to analyze the error") src = buf.Bytes() }
output := strings.ToLower(typeName + *outputSuffix + ".go") outputPath := filepath.Join(dir, output) if err := ioutil.WriteFile(outputPath, src, 0644); err != nil { log.Fatalf("writing output: %s", err) } }} View in context func main in campoy/jsonenums on ✱ Sourcegraph An interactive snippet showing how the jsonenums command is written.
译者信息
go generate 现在来看看go generate。go generate是一个你可以用来自动自成Go代码的命令。你可以结合例如jsonenums(一个用于为枚举类型自动生成JSON编组样板代码的类库)这样的元编程来使用go generate快速自动实现重复乏味代码的编写。在Go标准类库里面已经有大量可以用于解析AST的接口,而AST使得编写元编程工具更简单,更容易。在会议上,有另外两次讨论(Go语言中的元编程实践和拥抱标准类库)谈及到了这一点。 func main() { flag.Parse() if len(*typeNames) == 0 { log.Fatalf("the flag -type must be set") } types := strings.Split(*typeNames, ",")
// Only one directory at a time can be processed, and the default is ".". dir := "." if args := flag.Args(); len(args) == 1 { dir = args[0] } else if len(args) > 1 { log.Fatalf("only one directory at a time") }
pkg, err := parser.ParsePackage(dir, *outputSuffix+".go") if err != nil { log.Fatalf("parsing package: %v", err) }
var analysis = struct { Command string PackageName string TypesAndValues map[string][]string }{ Command: strings.Join(os.Args[1:], " "), PackageName: pkg.Name, TypesAndValues: make(map[string][]string), }
// Run generate for each type. for _, typeName := range types { values, err := pkg.ValuesOfType(typeName) if err != nil { log.Fatalf("finding values for type %v: %v", typeName, err) } analysis.TypesAndValues[typeName] = values
var buf bytes.Buffer if err := generatedTmpl.Execute(&buf, analysis); err != nil { log.Fatalf("generating code: %v", err) }
src, err := format.Source(buf.Bytes()) if err != nil { // Should never happen, but can arise when developing this code. // The user can compile the output to see the error. log.Printf("warning: internal error: invalid Go generated: %s", err) log.Printf("warning: compile the package to analyze the error") src = buf.Bytes() }
output := strings.ToLower(typeName + *outputSuffix + ".go") outputPath := filepath.Join(dir, output) if err := ioutil.WriteFile(outputPath, src, 0644); err != nil { log.Fatalf("writing output: %s", err) } }} 查看上下文
✱ Sourcegraph 站点上 campoy/jsonenums 中的 main 函数
一段互动的片段演示了如何编写jsonenums命令。
OpenGL Many people use Go for web services, but did you know that you can also create cool graphics in Go? Check out the OpenGL bindings in Go. func main() { glfw.SetErrorCallback(errorCallback)
if !glfw.Init() { panic("Can't init glfw!") } defer glfw.Terminate()
window, err := glfw.CreateWindow(Width, Height, Title, nil, nil) if err != nil { panic(err) }
window.MakeContextCurrent()
glfw.SwapInterval(1)
gl.Init()
if err := initScene(); err != nil { fmt.Fprintf(os.Stderr, "init: %s\n", err) return } defer destroyScene()
for !window.ShouldClose() { drawScene() window.SwapBuffers() glfw.PollEvents() }} View in context func main in go-gl/examples on ✱ Sourcegraph Interactive snippet illustrating use of Go OpenGL bindings to make a Gopher cube. Click a function or method name to explore. Hackathons and challenges You should also check out challenges and hackathons like the Gopher Gala and the Go Challenge. In the past, programmers from around the world have hacked together some really cool projects that you can take inspiration from.
译者信息
OpenGL 许多人使用Go作web服务,但是你知道你也可以用Go写出很cool的图形应用吗?查看Go在OpenGL中的捆绑。 func main() { glfw.SetErrorCallback(errorCallback)
if !glfw.Init() { panic("Can't init glfw!") } defer glfw.Terminate()
window, err := glfw.CreateWindow(Width, Height, Title, nil, nil) if err != nil { panic(err) }
window.MakeContextCurrent()
glfw.SwapInterval(1)
gl.Init()
if err := initScene(); err != nil { fmt.Fprintf(os.Stderr, "init: %s\n", err) return } defer destroyScene()
for !window.ShouldClose() { drawScene() window.SwapBuffers() glfw.PollEvents() }} 在文本中查看 在 ✱ Sourcegraph中 go-gl/examples 里面的函数 main 交互式的片段正说明Go的OpenGL捆绑能制作Gopher cube。点击函数或方法名去探索。 黑客马拉松和挑战 你也可以观看挑战和黑客马拉松,类似Gopher Gala和Go Challenge。在过去,来自世界各地的程序员一起挑战一些真实的酷项目,你可以从中获取灵感。
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接 我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们
-
1
-
2
-
GO 语言学习的五个阶段(带例子) 返回原文
英文原文:The 5 stages of learning Go (with examples)
Francesc (@francesc) is a member of the Go core team and a developer advocate for Google Cloud Platform. He’s a lover of programming languages, a master of technical instruction at Google, and one of the creators of the Go tour. This talk was inspired by another talk from Raquel Vélez at JSConf. Slides for this talk have been posted here. Sourcegraph is building the next generation of programming collaboration tools for searching, exploring, and reviewing code. We attended GopherCon India to share how we use Go and learn from how others are using it, and were honored to coordinate the liveblog of the talks.
译者信息 Francesc (@francesc) 是 Go 核心团队的一员, 是提倡 Google Cloud 平台的开发者. 他是一个编程语言的爱好者, Google的技术指导大师, Go tour的创造者之一. 这个讨论的灵感来自于另一个 Raquel Vélez 在 JSConf. Slides 的讨论,这个讨论已经发到了这里. Sourcegraph 是下一代编程协作工具, 用于搜索, 探索, 和审查代码. 我们参加GopherCon India 来分享我们是怎样使用 Go 并学习别人是怎样使用它的, 对配合liveblog的这次讨论我们深感荣幸.
As a developer advocate on the Go team, Francesc has probably interacted with more Go programmers than almost anyone else in the world. From this unique vantage point, he has seen 5 general stages in the evolution of programmers working in Go. These stages apply to how programmers progress through other languages, too. Understanding where you are in this evolution can help you figure out the most effective ways to improve and avoid common pitfalls that befall students of programming languages at every level. Editor’s note: this post has interactive code snippets for each phase of this evolution. Clicking on any function name jumps you to the definition in code for further exploration. Scroll down to see them.
译者信息 作为Go团队的开发者之一,Francesc可能比世界上其他人接触到的Go语言程序员都要多。正因为有了这样的有利条件,他把Go语言的学习过程划分为5个阶段。 这些阶段对于其他语言的学习也是成立的。理解自己处于哪个阶段,可以帮助你找到提高自己的最有效的方法,也可以避免每个阶段学习过程中的常见陷阱。 编者按:这篇文章对于每一个学习阶段都给出了交互式的代码片段。点击函数名你就可以跳到具体的函数定义,方便进行深入的研究。请看下文。
Here the 5 phases in the evolution of a Gopher:
- Phase 1 (the newcomer): You just learned the language. You’ve gone through a few tutorials or workshops, you understand basic syntax, and you can write short snippets of code.
- Phase 2 (the explorer): You can write a complete program, but don’t understand some more advanced language features like channels. You haven’t yet written a large project in Go.
- Phase 3 (the builder): You’re proficient in Go, you’re using Go in production to solve a specific and complete problem. You have a strong command of the common patterns and idioms in the language community. Go is a strong tool in your programming toolbelt.
- Phase 4 (the expert): You understand the design choices and motivations behind the language. You grok the philosophy of simplicity and composability.
- The advocate: You’re actively sharing your knowledge and understanding of the language with others. You’re a voice in the community, a participant on mailing lists and chatrooms, and you’re giving talks at conferences. Being an advocate is not really a separate stage, but a role you can fill at any of the other stages.
译者信息 这里是GO程序员的五个进化阶段:
- 第一个阶段(菜逼): 刚刚学习了这门语言。 已经通过一些教程或者培训班了解基本的语法,可以写短的代码片段。
- 第二个阶段 (探索者): 可以写一个完整的程序,但不懂一些更高级的语言特征,比如“channels”。还没有使用GO写一个大项目。
- 第三个阶段(大手): 你能熟练的使用Go, 能够用GO去解决,生产环境中一个具体和完整的问题。已经形成了一套自己的惯用法和常用代码库。在你的编码方案中Go是一个非常好用的工具。
- 第四阶段 (大神): 绝逼清楚Go语言的设计选择和背后的动机。能理解的简洁和可组合性哲学。
- 布道师: 积极地与他人分享关于Go语言知识和你对Go语言的理解。在各种合适的场所发出自己的声音, 参与邮件列表、建立QQ群、做专题报告。成为一个布道者不见得是一个完全独立的阶段,这个角色可以在上述的任何一个阶段中。
Phase 1: the newcomer Newcomers are at the stage where they’re creating small programs and toy projects in Go. They should take advantage of the Go tour, the Go playground, the docs, and the mailing list (golang-nuts). func main() { fmt.Println(stringutil.Reverse("!selpmaxe oG ,olleH"))} View in context func main in golang/example on ✱ Sourcegraph An interactive snippet from hello.go in the golang/example repository, which contains simple examples of small Go programs. Click around to explore the code.
译者信息
第一阶段: 菜逼 菜鸟在这个阶段使用Go去创建一些小项目或者玩具项目。他们应该会利用到Go tour, Go playground, Go文档, 和邮件列表(golang-nuts). func main() { fmt.Println(stringutil.Reverse("!selpmaxe oG ,olleH"))} 查看上下文
func main in golang/example on ✱ Sourcegraph
这是Go语言写的简单例子,这个代码段来自golang/example 代码库里面的 hello.go 。 点击就可以查看完整代码撸。
An important skill newcomers should develop is learning how to ask questions. A lot of people new to the mailing list say things like, “Hey, this doesn’t work,” without providing enough context to enable others to understand and help fix their problem. At the opposite end of the spectrum are those who copy and paste hundreds of lines of code into a forum post and haven’t invested the time to come up with a focused example that illustrates their problem. Also, you should rarely copy and paste code directly into the forum. Instead use the “share” button of the Go playground to link to snippets of code that others can edit and run directly in their browser.
译者信息 一项重要的技能,新人应该试着学习如何正确提问。很多新人在邮件列表里面这样说“嘿,这报错了”,这并没有提供足够的信息,让别人能理解并帮助他们解决问题。别人看到的是一个粘贴了几百行的代码的帖子,并没有花费精力来重点说明所遇到的问题。 所以, 应该尽量避免直接粘贴代码到论坛。而应该使用可以编辑并且可以在浏览器中直接运行的Go playground的“分享”按钮链接到代码片段。
Phase 2: the explorer The explorer has written some small projects in Go, but still gets lost sometimes. They might not fully understand or know how to use more advanced language features like channels. They still have many things to learn, but know enough to build useful things. They are starting to get a sense of the potential of Go and are excited about what they’ll be able to build in the language.
Within the explorer phase, you typically go through two steps. First, you ascend to the Peak of Inflated Expectations. You think you can do everything in Go, but without really understanding or grokking the Go ethos yet. You are probably trying to apply the idioms and patterns you know from other languages in Go code, and you don’t yet have a strong sense of what constitutes idiomatic Go. You try to undertake tasks like “migrate framework X from language Y to Go”.
译者信息
Phase 2: the explorer 探索者已经可以使用Go写一些小的软件,但有时仍然会有些迷茫。他们可能不完全明白怎么使用Go的高级特性,比如通道。虽然他们还有很多东西要学习,但已掌握的足够做一些有用的事情了!他们开始对Go的潜能有感觉了,并对它们能使用Go创建的东西感到兴奋。
在探索阶段通常会经历两个步骤。第一,膨胀的预期达到顶点,你觉得可以用Go做所有的事情,但还并不能明白或领悟到Go的真谛。你大概会用所熟悉的语言的模式和惯用语来写Go代码,但对于什么是地道的Go,还没有比较强烈的感觉。你开始尝试着手干这样的事情--“迁移架构X,从Y语言到Go语言”。
Following the Peak of Inflated Expectations is the Trough of Disillusionment. You miss feature X from language Y. You haven’t fully bought into idiomatic Go. You are still trying to write in the style of other programming languages and are getting frustrated. You may use the reflect and unsafe packages a lot. This is not idiomatic Go. Idiomatic Go tends to avoid things that are too “magical”. The Martini web framework is an example of a project that exemplifies the explorer phase. Martini was an early Go web framework that adopted a lot of concepts from Ruby web frameworks (like dependency injection). It received a lot of excitement from the community initially, but eventually had many issues around performance and debuggability. Jeremy Saenz (@codegangsta), the creator of Martini, responded constructively to feedback from the Go community and wrote another library called Negroni that follows more idiomatic Go conventions. func (m *Martini) RunOnAddr(addr string) { // TODO: Should probably be implemented using a new instance of http.Server in place of // calling http.ListenAndServer directly, so that it could be stored in the martini struct for later use. // This would also allow to improve testing when a custom host and port are passed.
logger := m.Injector.Get(reflect.TypeOf(m.logger)).Interface().(*log.Logger) logger.Printf("listening on %s (%s)\n", addr, Env) logger.Fatalln(http.ListenAndServe(addr, m))} View in context (*Martini).RunOnAddr in go-martini/martini on ✱ Sourcegraph Interactive code snippet from the Martini web framework, an example of non-idiomatic Go. Note the dependency injection implemented via the reflect package. func TestNegroniServeHTTP(t *testing.T) { result := "" response := httptest.NewRecorder()
n := New() n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { result += "foo" next(rw, r) result += "ban" })) n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { result += "bar" next(rw, r) result += "baz" })) n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { result += "bat" rw.WriteHeader(http.StatusBadRequest) }))
n.ServeHTTP(response, (*http.Request)(nil))
expect(t, result, "foobarbatbazban") expect(t, response.Code, http.StatusBadRequest)} View in context func TestNegroniServeHTTP in codegangsta/negroni on ✱ Sourcegraph Interactive code snippet demonstrating use of the Negroni library, an example of more idiomatic Go.
译者信息 到达预期膨胀的顶点之后,你会遇到理想幻灭的低谷。你开始想念语言Y的特性X,此时你还没有完全的掌握地道的Go。你还在用其他编程语言的风格来写Go语言的程序,你甚至开始觉得沮丧。你可能在大量使用reflect和unsafe这两个包,但这不是地道的Go。地道的Go不会使用那些魔法一样的东西。 这个探索阶段产生的项目的一个很好的例子就是Martini Web框架。Martini是一个Go语言的早期Web框架,它从Ruby的Web框架当中吸收了很多思想(比如依赖注入)。最初,这个框架在社区中引起了强烈的反响,但是它逐渐在性能和可调试性上受到了一些批评。Martini框架的作者Jeremy Saenz积极响应这些来自Go社区的反馈,写了一个更加符合Go语言规范的库Negroni
func (m *Martini) RunOnAddr(addr string) { // TODO: Should probably be implemented using a new instance of http.Server in place of // calling http.ListenAndServer directly, so that it could be stored in the martini struct for later use. // This would also allow to improve testing when a custom host and port are passed.
logger := m.Injector.Get(reflect.TypeOf(m.logger)).Interface().(*log.Logger) logger.Printf("listening on %s (%s)\n", addr, Env) logger.Fatalln(http.ListenAndServe(addr, m))} 查看上下文
(*Martini).RunOnAddr in go-martini/martini on ✱ Sourcegraph
来自Martini框架的交互式代码片段,它是不地道的Go的例子。注意用反射包实现的依赖注入
func TestNegroniServeHTTP(t *testing.T) { result := "" response := httptest.NewRecorder()
n := New() n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { result += "foo" next(rw, r) result += "ban" })) n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { result += "bar" next(rw, r) result += "baz" })) n.Use(HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) { result += "bat" rw.WriteHeader(http.StatusBadRequest) }))
n.ServeHTTP(response, (*http.Request)(nil))
expect(t, result, "foobarbatbazban") expect(t, response.Code, http.StatusBadRequest)} 查看上下文
func TestNegroniServeHTTP in codegangsta/negroni on * Sourcegraph
来自Negroni库的交互式代码片段,它是地道的Go的例子
Many other languages rely heavily on third-party libraries to deliver core functionality like HTTP handling. One thing that distinguishes Go is the power of its standard library. If you think the Go standard library is not powerful enough to do what you want to do, you might be wrong. The Go standard library is incredibly powerful, and it’s worth reading through its code to learn the patterns of how it implements things. func (srv *Server) ListenAndServe() error { addr := srv.Addr if addr == "" { addr = ":http" } ln, err := net.Listen("tcp", addr) if err != nil { return err } return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})} View in context (*Server).ListenAndServe in golang/go on ✱ Sourcegraph An interactive snippet of the ListenAndServe method in the Go standard library. If you’ve written Go code, you’ve probably called this function many times, but have you bothered to see how it’s written? Go ahead and click around in the snippet above. The disillusionment of the Trough of Disillusionment comes from the fact that you are still thinking in the patterns of other languages and you haven’t yet fully explored much of what Go offers. Here are some fun things you can do to avoid stalling and dive further into cool things you can do in the language.
译者信息 其他语言在提供一些核心功能,比如HTTP处理的时候,往往需要依赖第三方库。但是Go语言在这一点上很不同,它的标准库非常强大。如果你认为Go标准库没有强大到可以做你想做的事情,那么我说你错了。Go语言标准库难以置信的强大,值得你花时间阅读它的代码,学习它实现的模式。 func (srv *Server) ListenAndServe() error { addr := srv.Addr if addr == "" { addr = ":http" } ln, err := net.Listen("tcp", addr) if err != nil { return err } return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})} 在上下文中查看
(Server).ListenAndServe in golang/go on Sourcegraph
Go标准库中的ListenAndServe函数片段。如果你写过Go程序,你可能已经调用过这个函数很多次了,但是你曾经花时间看过它的实现么?去点击上面的代码片段吧。 幻灭的低谷中的幻灭感来自于这样的事实:你还在用其他语言的模式来想问题,而且你还没有完全探索过Go能提供给你什么。下面是一些好玩的事情,你可以做一下来打破困境,进一步探索这门语言中好玩的事。
go generate Check out go generate. go generate is a command that you can use to auto-generate Go code. You can use it in conjunction with metaprograms like jsonenums (a library to auto-generate JSON marshalling boilerplate code for enum types) to automate the writing of tedious code. The Go standard library has a great API for parsing the AST, which makes metaprogramming tools simple and easy to write. This is a point covered in 2 other talks at the conference (Practical Metaprogramming in Go and Embracing the Standard Library). func main() { flag.Parse() if len(*typeNames) == 0 { log.Fatalf("the flag -type must be set") } types := strings.Split(*typeNames, ",")
// Only one directory at a time can be processed, and the default is ".". dir := "." if args := flag.Args(); len(args) == 1 { dir = args[0] } else if len(args) > 1 { log.Fatalf("only one directory at a time") }
pkg, err := parser.ParsePackage(dir, *outputSuffix+".go") if err != nil { log.Fatalf("parsing package: %v", err) }
var analysis = struct { Command string PackageName string TypesAndValues map[string][]string }{ Command: strings.Join(os.Args[1:], " "), PackageName: pkg.Name, TypesAndValues: make(map[string][]string), }
// Run generate for each type. for _, typeName := range types { values, err := pkg.ValuesOfType(typeName) if err != nil { log.Fatalf("finding values for type %v: %v", typeName, err) } analysis.TypesAndValues[typeName] = values
var buf bytes.Buffer if err := generatedTmpl.Execute(&buf, analysis); err != nil { log.Fatalf("generating code: %v", err) }
src, err := format.Source(buf.Bytes()) if err != nil { // Should never happen, but can arise when developing this code. // The user can compile the output to see the error. log.Printf("warning: internal error: invalid Go generated: %s", err) log.Printf("warning: compile the package to analyze the error") src = buf.Bytes() }
output := strings.ToLower(typeName + *outputSuffix + ".go") outputPath := filepath.Join(dir, output) if err := ioutil.WriteFile(outputPath, src, 0644); err != nil { log.Fatalf("writing output: %s", err) } }} View in context func main in campoy/jsonenums on ✱ Sourcegraph An interactive snippet showing how the jsonenums command is written.
译者信息
go generate 现在来看看go generate。go generate是一个你可以用来自动自成Go代码的命令。你可以结合例如jsonenums(一个用于为枚举类型自动生成JSON编组样板代码的类库)这样的元编程来使用go generate快速自动实现重复乏味代码的编写。在Go标准类库里面已经有大量可以用于解析AST的接口,而AST使得编写元编程工具更简单,更容易。在会议上,有另外两次讨论(Go语言中的元编程实践和拥抱标准类库)谈及到了这一点。 func main() { flag.Parse() if len(*typeNames) == 0 { log.Fatalf("the flag -type must be set") } types := strings.Split(*typeNames, ",")
// Only one directory at a time can be processed, and the default is ".". dir := "." if args := flag.Args(); len(args) == 1 { dir = args[0] } else if len(args) > 1 { log.Fatalf("only one directory at a time") }
pkg, err := parser.ParsePackage(dir, *outputSuffix+".go") if err != nil { log.Fatalf("parsing package: %v", err) }
var analysis = struct { Command string PackageName string TypesAndValues map[string][]string }{ Command: strings.Join(os.Args[1:], " "), PackageName: pkg.Name, TypesAndValues: make(map[string][]string), }
// Run generate for each type. for _, typeName := range types { values, err := pkg.ValuesOfType(typeName) if err != nil { log.Fatalf("finding values for type %v: %v", typeName, err) } analysis.TypesAndValues[typeName] = values
var buf bytes.Buffer if err := generatedTmpl.Execute(&buf, analysis); err != nil { log.Fatalf("generating code: %v", err) }
src, err := format.Source(buf.Bytes()) if err != nil { // Should never happen, but can arise when developing this code. // The user can compile the output to see the error. log.Printf("warning: internal error: invalid Go generated: %s", err) log.Printf("warning: compile the package to analyze the error") src = buf.Bytes() }
output := strings.ToLower(typeName + *outputSuffix + ".go") outputPath := filepath.Join(dir, output) if err := ioutil.WriteFile(outputPath, src, 0644); err != nil { log.Fatalf("writing output: %s", err) } }} 查看上下文
✱ Sourcegraph 站点上 campoy/jsonenums 中的 main 函数
一段互动的片段演示了如何编写jsonenums命令。
OpenGL Many people use Go for web services, but did you know that you can also create cool graphics in Go? Check out the OpenGL bindings in Go. func main() { glfw.SetErrorCallback(errorCallback)
if !glfw.Init() { panic("Can't init glfw!") } defer glfw.Terminate()
window, err := glfw.CreateWindow(Width, Height, Title, nil, nil) if err != nil { panic(err) }
window.MakeContextCurrent()
glfw.SwapInterval(1)
gl.Init()
if err := initScene(); err != nil { fmt.Fprintf(os.Stderr, "init: %s\n", err) return } defer destroyScene()
for !window.ShouldClose() { drawScene() window.SwapBuffers() glfw.PollEvents() }} View in context func main in go-gl/examples on ✱ Sourcegraph Interactive snippet illustrating use of Go OpenGL bindings to make a Gopher cube. Click a function or method name to explore. Hackathons and challenges You should also check out challenges and hackathons like the Gopher Gala and the Go Challenge. In the past, programmers from around the world have hacked together some really cool projects that you can take inspiration from.
译者信息
OpenGL 许多人使用Go作web服务,但是你知道你也可以用Go写出很cool的图形应用吗?查看Go在OpenGL中的捆绑。 func main() { glfw.SetErrorCallback(errorCallback)
if !glfw.Init() { panic("Can't init glfw!") } defer glfw.Terminate()
window, err := glfw.CreateWindow(Width, Height, Title, nil, nil) if err != nil { panic(err) }
window.MakeContextCurrent()
glfw.SwapInterval(1)
gl.Init()
if err := initScene(); err != nil { fmt.Fprintf(os.Stderr, "init: %s\n", err) return } defer destroyScene()
for !window.ShouldClose() { drawScene() window.SwapBuffers() glfw.PollEvents() }} 在文本中查看 在 ✱ Sourcegraph中 go-gl/examples 里面的函数 main 交互式的片段正说明Go的OpenGL捆绑能制作Gopher cube。点击函数或方法名去探索。 黑客马拉松和挑战 你也可以观看挑战和黑客马拉松,类似Gopher Gala和Go Challenge。在过去,来自世界各地的程序员一起挑战一些真实的酷项目,你可以从中获取灵感。
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接 我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。