五步让你成为GO 语言高手_Golang

Francesc (@francesc) 是 Go 核心团队的一员, 是提倡 Google Cloud 平台的开发者. 他是一个编程语言的爱好者, Google的技术指导大师, Go tour的创造者之一. 这个讨论的灵感来自于另一个 Raquel Vélez 在 JSConf. Slides 讨论,这个讨论已经发到了这里.

Sourcegraph 是下一代编程协作工具, 用于搜索, 探索, 和审查代码. 我们参加GopherCon India 来分享我们是怎样使用 Go 并学习别人是怎样使用它的, 对配合liveblog的这次讨论我们深感荣幸.

作为Go团队的开发者之一,Francesc可能比世界上其他人接触到的Go语言程序员都要多。正因为有了这样的有利条件,他把Go语言的学习过程划分为5个阶段。

这些阶段对于其他语言的学习也是成立的。理解自己处于哪个阶段,可以帮助你找到提高自己的最有效的方法,也可以避免每个阶段学习过程中的常见陷阱。

编者按:这篇文章对于每一个学习阶段都给出了交互式的代码片段。点击函数名你就可以跳到具体的函数定义,方便进行深入的研究。请看下文。

这里是GO程序员的五个进化阶段:

第一个阶段(菜逼): 刚刚学习了这门语言。 已经通过一些教程或者培训班了解基本的语法,可以写短的代码片段。

第二个阶段 (探索者): 可以写一个完整的程序,但不懂一些更高级的语言特征,比如“channels”。还没有使用GO写一个大项目。

第三个阶段(大手): 你能熟练的使用Go, 能够用GO去解决,生产环境中一个具体和完整的问题。已经形成了一套自己的惯用法和常用代码库。在你的编码方案中Go是一个非常好用的工具。

第四阶段 (大神): 绝逼清楚Go语言的设计选择和背后的动机。能理解的简洁和可组合性哲学。

布道师: 积极地与他人分享关于Go语言知识和你对Go语言的理解。在各种合适的场所发出自己的声音, 参与邮件列表、建立QQ群、做专题报告。成为一个布道者不见得是一个完全独立的阶段,这个角色可以在上述的任何一个阶段中。

第一阶段: 菜逼

菜鸟在这个阶段使用Go去创建一些小项目或者玩具项目。他们应该会利用到Go tour, Go playground, Go文档, 和邮件列表(golang-nuts).

func main() {    fmt.Println(stringutil.Reverse("!selpmaxe oG ,olleH"))}

这是Go语言写的简单例子,这个代码段来自golang/example代码库里面的 hello.go 。 点击就可以查看完整代码撸。

一项重要的技能,新人应该试着学习如何正确提问。很多新人在邮件列表里面这样说“嘿,这报错了”,这并没有提供足够的信息,让别人能理解并帮助他们解决问题。别人看到的是一个粘贴了几百行的代码的帖子,并没有花费精力来重点说明所遇到的问题。

所以, 应该尽量避免直接粘贴代码到论坛。而应该使用可以编辑并且可以在浏览器中直接运行的Go playground的“分享”按钮链接到代码片段。

Phase 2: the explorer

探索者已经可以使用Go写一些小的软件,但有时仍然会有些迷茫。他们可能不完全明白怎么使用Go的高级特性,比如通道。虽然他们还有很多东西要学习,但已掌握的足够做一些有用的事情了!他们开始对Go的潜能有感觉了,并对它们能使用Go创建的东西感到兴奋。

在探索阶段通常会经历两个步骤。第一,膨胀的预期达到顶点,你觉得可以用Go做所有的事情,但还并不能明白或领悟到Go的真谛。你大概会用所熟悉的语言的模式和惯用语来写Go代码,但对于什么是地道的Go,还没有比较强烈的感觉。你开始尝试着手干这样的事情--“迁移架构X,从Y语言到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框架的交互式代码片段,它是不地道的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)}

来自Negroni库的交互式代码片段,它是地道的Go的例子

其他语言在提供一些核心功能,比如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)})}

Go标准库中的ListenAndServe函数片段。如果你写过Go程序,你可能已经调用过这个函数很多次了,但是你曾经花时间看过它的实现么?去点击上面的代码片段吧。

幻灭的低谷中的幻灭感来自于这样的事实:你还在用其他语言的模式来想问题,而且你还没有完全探索过Go能提供给你什么。下面是一些好玩的事情,你可以做一下来打破困境,进一步探索这门语言中好玩的事。

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)        }    }}

一段互动的片段演示了如何编写jsonenums命令。

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()    }}

交互式的片段正说明Go的OpenGL捆绑能制作Gopher cube。点击函数或方法名去探索。

黑客马拉松和挑战

你也可以观看挑战和黑客马拉松,类似Gopher Gala和Go Challenge。在过去,来自世界各地的程序员一起挑战一些真实的酷项目,你可以从中获取灵感。

第三阶段: 老手

作为一个老手,这意味着你可以解决很多Go语言中你关心的问题。新的需要解决的问题会带来新的疑问,经过试错,你学会了在这门语言中什么是可以做的,什么是不能做的。此时,你已经对这门语言的习惯和模式有了一个坚实的理解。你可以非常高效地工作,写出可读,文档完善,可维护的代码。

成为老手的一个很好的方法就是在大项目上工作。如果你自己有一个项目的想法,开始动手去做吧(当然你要确定它并不是已经存在了)。大多数人也许并没有一个很大的项目的想法,所以他们可以对已经存在的项目做出贡献。Go语言已经有很多大型项目,而且它们正在被广泛使用,比如Docker, Kubernetes和Go本身。可以看看这个项目列表

func (cli *DockerCli) CmdRestart(args ...string) error {    cmd := cli.Subcmd("restart", "CONTAINER [CONTAINER...]", "Restart a running container", true)    nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Seconds to wait for stop before killing the container.")    cmd.Require(flag.Min, 1)     utils.ParseFlags(cmd, args, true)     v := url.Values{}    v.Set("t", strconv.Itoa(*nSeconds))     var encounteredError error    for _, name := range cmd.Args() {        _, _, err := readBody(cli.call("POST", "/containers/"+name+"/restart?"+v.Encode(), nil, false))        if err != nil {            fmt.Fprintf(cli.err, "%s\n", err)            encounteredError = fmt.Errorf("Error: failed to restart one or more containers")        } else {            fmt.Fprintf(cli.out, "%s\n", name)        }    }    return encounteredError}

Docker项目的交互式代码片段。点击函数名,开始探索之旅吧。

老手应该对Go生态系统的工具有一个很强的掌握,因为这些工具真的提高生产效率。你应该了解go generate,go vet,go test-race, 和gofmt/goimports/goreturns。你应该使用go fmt,因为它会自动把你的代码按照Go社区的风格标准来格式化。goimports可以做同样的事情,而且还会添加丢失的imports。goretures不光做了前面所说的事情,还可以在返回表达式添加丢失的错误,这是大家都讨厌的地方。

在老手阶段,你一定要开始做code review。code review的意义并不是要修改或者找到错误(那是测试人员做的事情)。code review可以帮助维持统一的编程风格,提高软件的总体质量,还可以在别人的反馈中提高你自己的编程技术。几乎所有的大型开源项目都对每一个提交做code review。

下面是一个从人类的反馈当中学习的例子:Google的Go团队以前都在main函数的外面声明命令行标记。在去年的GopherCon会议上,Francesc遇到了SoundCloud公司的Peter Bourgon(@peterbourgon)。Peter Bourgon说在SoundCloud,他们都在main函数内部声明标记,这样他们不会错误地在外部使用标记。Francesc现在认为这是最佳实践。

第四阶段:专家

作为一个专家,你很好地了解了语言的哲学思想。对于Go语言的特性,你知道何时应该使用,何时不应该使用。例如,Jeremy Saenz在dotGo风暴讨论中谈论到了何时不该使用接口。

func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call {    call := new(Call)    call.ServiceMethod = serviceMethod    call.Args = args    call.Reply = reply    if done == nil {        done = make(chan *Call, 10) // buffered.    } else {        // If caller passes done != nil, it must arrange that        // done has enough buffer for the number of simultaneous        // RPCs that will be using that channel.  If the channel        // is totally unbuffered, it's best not to run at all.        if cap(done) == 0 {            log.Panic("rpc: done channel is unbuffered")        }    }    call.Done = done    client.send(call)    return call}

来自标准类库的一小块交互代码片段使用了频道。理解标准类库里面的模式背后的决策原因是成为一个专家必经之路。

但是不要成为只局限于单一语言的专家。跟其他任何语言一样,Go仅仅只是一个工具。你还应该去探索其他语言,并且学习他们的模式和风格。Francesc从他使用Go的经验中找到了编写JavaScript的启发。他还喜欢重点关注于不可变性和致力于避免易变性的Haskell语言,并从中获得了如何编写Go代码的灵感。

布道者

作为一个布道者,你分享自己的知识,传授你学会的和你提出的最佳实践。你可以分享自己对Go喜欢或者不喜欢的地方。全世界各地都有Go会议,找到离你最近的。

你可以在任何一个阶段成为布道者,不要等到你成为这个领域的专家的时候才发出自己的声音。在你学习Go的任何一个阶段,提出问题,结合你的经验给出反馈,不要羞于提出自己不喜欢的地方。你提出的反馈可以帮助社区改善做事情的方法,也可能改变你自己对编程的看法。

func main() {    httpAddr := flag.String("http", "127.0.0.1:3999", "HTTP service address (e.g., '127.0.0.1:3999')")    originHost := flag.String("orighost", "", "host component of web origin URL (e.g., 'localhost')")    flag.StringVar(&basePath, "base", "", "base path for slide template and static resources")    flag.BoolVar(&present.PlayEnabled, "play", true, "enable playground (permit execution of arbitrary user code)")    nativeClient := flag.Bool("nacl", false, "use Native Client environment playground (prevents non-Go code execution)")    flag.Parse()     if basePath == "" {        p, err := build.Default.Import(basePkg, "", build.FindOnly)        if err != nil {            fmt.Fprintf(os.Stderr, "Couldn't find gopresent files: %v\n", err)            fmt.Fprintf(os.Stderr, basePathMessage, basePkg)            os.Exit(1)        }        basePath = p.Dir    }    err := initTemplates(basePath)    if err != nil {        log.Fatalf("Failed to parse templates: %v", err)    }     ln, err := net.Listen("tcp", *httpAddr)    if err != nil {        log.Fatal(err)    }    defer ln.Close()     _, port, err := net.SplitHostPort(ln.Addr().String())    if err != nil {        log.Fatal(err)    }    origin := &url.URL{Scheme: "http"}    if *originHost != "" {        origin.Host = net.JoinHostPort(*originHost, port)    } else if ln.Addr().(*net.TCPAddr).IP.IsUnspecified() {        name, _ := os.Hostname()        origin.Host = net.JoinHostPort(name, port)    } else {        reqHost, reqPort, err := net.SplitHostPort(*httpAddr)        if err != nil {            log.Fatal(err)        }        if reqPort == "0" {            origin.Host = net.JoinHostPort(reqHost, port)        } else {            origin.Host = *httpAddr        }    }     if present.PlayEnabled {        if *nativeClient {            socket.RunScripts = false            socket.Environ = func() []string {                if runtime.GOARCH == "amd64" {                    return environ("GOOS=nacl", "GOARCH=amd64p32")                }                return environ("GOOS=nacl")            }        }        playScript(basePath, "SocketTransport")        http.Handle("/socket", socket.NewHandler(origin))    }    http.Handle("/static/", http.FileServer(http.Dir(basePath)))     if !ln.Addr().(*net.TCPAddr).IP.IsLoopback() &&        present.PlayEnabled && !*nativeClient {        log.Print(localhostWarning)    }     log.Printf("Open your web browser and visit %s", origin.String())    log.Fatal(http.Serve(ln, nil))

流行的present命令的main函数,很多Go的用户使用它来制作幻灯片。许多演讲者修改了这个模块来满足自己的需要。

Q&A

问:在GO语言中,我所怀念的一项功能是一个好的调试器。

答:我们正在做了,不只是调试器,我们还会提供一个更好的总体监视工具可以让你在程序运行时更好地洞察程序在干什么(显示出所有正在运行的goroutine的状态)。在GO 1.5中探索它吧。

以上所述就是本文的全部内容了,希望大家能够喜欢。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索go
, 学习
语言
go语言和golang、golang go语言、go语言为什么叫golang、golang语言编程、c语言调用golang,以便于您获取更多的相关知识。

时间: 2024-09-20 23:12:26

五步让你成为GO 语言高手_Golang的相关文章

实例浅析:五步解答中小型旅游网站文章优化策略

中小型旅游网站没有大网站的实力和影响力,想要在高手林立的旅游网站中脱颖而出,我们要在网站内容方面下狠功夫.一般中小型旅游网站的内容建设分为产品栏目及详情页.旅游目的地相关模板内容.还有就是文章页.今天聚途旅游网小编就为大家分享一下中小型旅游网站的文章优化策略. 文章优化是网站内容优化最灵活的一个方面,可以完全控制关键词.链接.展现方式等.中小型旅游网站的文章页面的优化目的通常是作为入口页面,吸引更多新老访客.   下面就通过"文章类型"."关键词"."内外

求C语言高手解惑,一个关于const变量定义数组大小问题

问题描述 求C语言高手解惑,一个关于const变量定义数组大小问题 我在函数的外部定义了一个BlockSize,然后又定义了一个结构(两者都是在函数外部).编译的时候,出现一个error.提示说:variably modified 'array' at file scope const int BlockSize = 20;//define BlockSize 20typedef struct _node { int array[BlockSize]; struct _node* next;}N

c语言-C语言高手请进:这个分块求和C语言程序问题出在哪里??对一组无规律数据按正数、负数和零分块求和,

问题描述 C语言高手请进:这个分块求和C语言程序问题出在哪里??对一组无规律数据按正数.负数和零分块求和, 对一组无规律数据按正数.负数和零分块求和,即要求将序列中相邻的正数.零及负数分块累加输出,格式要求: 源数据: 2,3,8,6,0,0,-2,-1,-4,0,5,6,7,-5,-2,...(共100个) 整理输出为: 2,5,13,19,0,0,-2,-3,-7,0,5,11,18,-5,-7...(共100个) 以下程序哪里出了问题?我搞了2星期,总是得不到完整输出: int main(

企业混合云进化论 五步教你变身云专家

有人说混合云是私有云和公有云的结合,但得到的效果并非简单的1+1=2,而是>2.目前大多数企业都处在云入门阶段,混合云的价值几何?如何构建混合云?如何管理混合云?等问题也摆在了企业CIO的面前.而我们今天探讨的五个方向内容,恰恰可以帮助企业CIO应对这些挑战,辅助其构建并管理混合云,成功进化变身云专家. 传统企业眼中的混合云 云计算在企业信息化中早已家喻户晓,企业也都纷纷开始尝试使用,IT建设相对成熟的大中型企业大多选择私有云,IT建设相对薄弱的小型企业更多选择公有云. 但随着云计算技术以及企业

互联网创业成功之道(七):站点基础优化五步走

中介交易 SEO诊断 淘宝客 云主机 技术大厅 一.了解搜索引擎优化 说到搜索引擎优化(Search Engine Optimization,缩写为SEO),就不得不提到搜索引擎营销(SEM),SEM是一种通过搜索引擎来对网站进行推广的营销方式,而搜索引擎优化就是其中的一种,其它方式还包括搜索引擎的竞价排名广告等.由于SEO是通过技术方式来实现,无须付费进行推广,所以对于个人站长来说更实用.本章的重点,就是讲解SEO的实现方式. 1.搜索引擎优化是什么 对网站进行搜索引擎优化,就是针对各种搜索引

五步开启企业IT风险管理大门

IT风险管理绝对能够引起高管的重视.由于我们的经济开始越来越依赖于互联网和IT系统,使这些系统中存在的风险尤为瞩目.但是IT风险管理涉及多方人员:首席信息官,首席信息安全官,企业风险管理团队,合规和监管人员以及内部和外部审计员等.以下五个步骤将助你开启IT风险管理大门: 第一步:选择正确的语言 通常有这样两种首席信息官:基础设施管理型的和思考家型的,一般后者能够成功开展IT风险管理工作,因为他们能够选择正确的语言与高管进行谈判,他们更多地会从对企业造成的损失来说,而不是技术名词. 最好不要谈论"

用户研究分析:进行用户研究的五步方法

文章描述:进行用户研究的五步法. 想象一下,这就是你所了解的我:大学毕业,男性,年龄在35-45岁,我有一台Mac book pro,还有一台iphone 5.我通过谷歌的chrome浏览器来上网.我公开使用微博和博客,你可以发现我喜欢巧克力和小狗.我结婚了.开着一辆丰田Corolla.我有着棕色的头发和同样棕色的眼睛.我的信用卡清单显示了我最常预定的酒店和喜欢外出就餐的场所. 如果你的金融理财客户给你提供这些信息,你可以告诉他们为什么我刚打算把我的支票及存款转移到一个新的银行吗?这种情况看起来

Windows网络安全其实我们只差五步

网络安全不再仅仅是用户组网才会思考的问题,如今的网络情况纷繁复杂,网络安全更要考虑的详尽周到.笔者提醒您P2P用户,其实Windows网络安全体验我们只差五步. 1.必须有本地的安全策略 增强每一个个人的系统安全是重要的,因为在这种设置中没有组策略,你必须要依赖Windows本地的安全策略.你可以通过Window控制面板或者通过运行secpol.msc或者secpol.msc来访问你的本地安全策略设置. 要记住,你需要做的关键的设置包括:启用审计失败事件的记录.要求使用Ctrl+Alt+Del键

胖子:新站seo前的五步进行曲

做一个新站需要时间和耐性.需要自信.优化好结构,就是布局.再专心做好内容,排名不再在神马浮云,胖子为大家总结新站seo前的五步曲. 一.合理的布局关键词.包括目标和长尾词,词的布局直接关系网站的排名和权重,所谓打蛇要打七寸,目标和长尾一定要合理的安排以及服务,因为这些个词是引导蜘蛛和用户进来的. 二.高质量内容.这跟原创是有区别的,不是随随便便自己写的,就算是.高质量的内容是在原创基础上加以修饰及为关键词服务的土壤.可以张冠李戴但一定要相关及可读. 三.千万不要过度优化.过度优化,主要是站内,包