GO 开发者对 GO 初学者的建议

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

注:私人添加,可以订阅 Newspaper.io 的 Golang Daily,以及 @ASTA谢 的
《Go Web 编程》 【作者也出了实体书,大家可以购买】和 订阅 Golang Ask
News,社区 http://golanghome.com/,@无闻Unknown
《Go编程基础》,《Go Web基础》 和 《Go名库讲解》

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

  • 对于任何人来说学习一门新语言可能都是令人挫折的。GO
    社区是不可置信的活跃,你不是孤单的。利用所有的文档,博客,本地的
    Meetups 和用户组,比如 Slack。不要害怕问问题和参与
  • 如果你对 GO 感兴趣,使用它的一侧涉足,或是专业的使用它,如果本地有
    Go meetup,考虑参与。如果你有货,考虑去分享它
  • 如果你有计划旅行,并且有能力,努力去访问 GO 社区目的地
  • 来访的用户群体是个证明这个社区有众多的用户,支持者和雇员的途径
  • 不要浪费时间去和其他语言比较,如果你喜欢 GO,就爱上他并且去使用它
  • 接受 Go 的文化和 GO
    做事情的方式。你的代码会感谢你,如果你这样做了,你会得到很多
  • 不要冲动的引入依赖
  • 简单是 GO
    最重要的特征。避免过分设计,使用简单的代码片段而不是单一的庞大的代码库
  • 从其他语言移植库到 GO
    是一个很好的做法,它允许你剥离他人的代码并且以符合 GO
    语言的方式粘合起来。

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

给 go 初学者分享的一些问题

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

注:原文地址为 Advise from Go developers to Go programming newbies

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

原文

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

  • 通读 the Go standard library 和 Effective Go,为了学习 GO
    的规范,Effective Go 是被高度推荐的,尤其是如果你有其他语言的背景。
  • 在 Go tour 上做练习
  • 看完语言参考
  • 练习 Go by Example,而不仅仅是复制粘贴!
  • 坚持编写 GO 代码,在几周内你将会在这门语言上变得高效
  • 理解接口的功能,他们是 GO 最大的礼物之一,可能比 channels 和
    goroutines 还重要。这个关于接口的文章 article on interfaces 和
    Andrew Gerrand 在 GopherCon 2014 上的 keynote 接口的描述
    会对你非常有帮助。
  • 抛弃你的 OO 的思想包袱,如果你来自于其他语言,比如动态语言 Python
    或是 Ruby,或者是一个编译型语言如 Java 或 C#。GO
    是一个面向对象的语言,但是它不是一个基于 class 的语言和不支持继承。
  • 了解继承从 GO
    语言中移除了。实践组合的用法而不是继承的机会显现了,并且纠结于继承只会导致你沮丧
  • 不要以其他语言的风格编写 GO
  • 寻找更加有经验的 Gophers,他们能帮助你 review 代码片段和给你反馈。在
    GO 社区能得到真正的支持和帮助
  • 用 GO
    实现你想法中的一个项目或是找到一个项目来工作。然后随着你学习的更多,不断重构你的应用。利用邮件列表和参加
    Gopher Academy Slack group 来见其他的 Gophers 来得到帮助。Dave
    Cheney 的博客和 GoingGo 的博客也是一个非常好的开始
  • 不要等待泛型和函数式被添加进语言;屏住呼吸并学习爱上我们在今天拥有的这门语言

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

以促进 India 的 go 编程作为 GopherConIndia 承诺的一部分。我们采访了 40
位 Gophers(一个 Gopher 代表一个 GO 项目或是任何地方的 GO
程序员),得到了他们关于 GO 的意见。从 2014
年的八月到十一月,我们将每个星期发表两篇采访稿。如果你正好刚刚开始 go
编程,他们对于我们一些问题的答案可能会对你有非常有用。看看这些。应该做:

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

注:How do you see the market for Go Programmers in the work place?
What is the future for Go 这部分不翻译,请读者自己看

来自Martini框架的交互式代码片段,它是不地道的Go的例子。注意用反射包实现的依赖注入

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

OpenGL

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做所有的事情,但还并不能明白或领悟到Go的真谛。你大概会用所熟悉的语言的模式和惯用语来写Go代码,但对于什么是地道的Go,还没有比较强烈的感觉。你开始尝试着手干这样的事情–“迁移架构X,从Y语言到Go语言”。

go generate

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

您可能感兴趣的文章:

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

第三阶段: 老手

布道者

许多人使用Go作web服务,但是你知道你也可以用Go写出很cool的图形应用吗?查看Go在OpenGL中的捆绑

第四阶段
(大神)
:
绝逼清楚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)
        }
    }}

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

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

黑客马拉松和挑战

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

这个探索阶段产生的项目的一个很好的例子就是Martini
Web框架
。Martini是一个Go语言的早期Web框架,它从Ruby的Web框架当中吸收了很多思想(比如依赖注入)。最初,这个框架在社区中引起了强烈的反响,但是它逐渐在性能和可调试性上受到了一些批评。Martini框架的作者Jeremy
Saenz积极响应这些来自Go社区的反馈,写了一个更加符合Go语言规范的库Negroni

澳门新葡亰赌995577 1

现在来看看go generate。go
generate是一个你可以用来自动自成Go代码的命令。你可以结合例如jsonenums(一个用于为枚举类型自动生成JSON编组样板代码的类库)这样的元编程来使用go
generate快速自动实现重复乏味代码的编写。在Go标准类库里面已经有大量可以用于解析AST的接口,而AST使得编写元编程工具更简单,更容易。在会议上,有另外两次讨论(Go语言中的元编程实践拥抱标准类库)谈及到了这一点。

到达预期膨胀的顶点之后,你会遇到理想幻灭的低谷。你开始想念语言Y的特性X,此时你还没有完全的掌握地道的Go。你还在用其他编程语言的风格来写Go语言的程序,你甚至开始觉得沮丧。你可能在大量使用reflect和unsafe这两个包,但这不是地道的Go。地道的Go不会使用那些魔法一样的东西。

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