<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Golang - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ freeCodeCamp 是一个免费学习编程的开发者社区，涵盖 Python、HTML、CSS、React、Vue、BootStrap、JSON 教程等，还有活跃的技术论坛和丰富的社区活动，在你学习编程和找工作时为你提供建议和帮助。 ]]>
        </description>
        <link>https://www.freecodecamp.org/chinese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Golang - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/chinese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 23 May 2026 08:28:44 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/chinese/news/tag/golang/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ 从 0 到 1 学习 Go ]]>
                </title>
                <description>
                    <![CDATA[ 让我们先对 Go（或称 Golang ）做一个小小的介绍。Go 是由谷歌工程师 Robert Griesemer、Rob Pike 和 Ken Thompson 设计的。它是一种静态类型的、编译的语言。第一个版本于 2012 年 3 月作为开源版本发布。 > "Go 是一种开源的编程语言，它使人们能够轻松地构建简单、可靠和高效的软件"。- GoLang 在许多编程语言中，有许多方法来解决一个特定的问题。程序员要花很多时间去思考解决它的最佳方法。 Go 却相信用较少的功能--只有一种正确的方式来解决问题。 这为开发人员节省了时间，并使大型代码库易于维护。 Go 中没有像 maps 和 filters 这样的 "表达性 "功能。 > "当你有增加表现力的功能时，通常会增加系统开销。"--Rob Pike 最近发表的新的 golang 标志: https://blog.golang.org/go-brand 入门 Go 是由 packages（包）组成的。package main 告诉 Go 编译器，该程序被编译为可执行文件，而不是共享库。它是一个应用程序的入口点。pa ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/learning-go-from-zero-to-hero-d2a3223b3d86/</link>
                <guid isPermaLink="false">668227379100b5049d88a6ae</guid>
                
                    <category>
                        <![CDATA[ Golang ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ luojiyin ]]>
                </dc:creator>
                <pubDate>Mon, 01 Jul 2024 04:15:36 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2024/07/1_30aoNxlSnaYrLhBT0O1lzw.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/learning-go-from-zero-to-hero-d2a3223b3d86/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Learning Go — from zero to hero</a>
      </p><!--kg-card-begin: markdown--><p>让我们先对 Go（或称 Golang ）做一个小小的介绍。Go 是由谷歌工程师 Robert Griesemer、Rob Pike 和 Ken Thompson 设计的。它是一种静态类型的、编译的语言。第一个版本于 2012 年 3 月作为开源版本发布。</p>
<blockquote>
<p>"Go 是一种开源的编程语言，它使人们能够轻松地构建简单、可靠和高效的软件"。- GoLang</p>
</blockquote>
<p>在许多编程语言中，有许多方法来解决一个特定的问题。程序员要花很多时间去思考解决它的最佳方法。</p>
<p>Go 却相信用较少的功能--只有一种正确的方式来解决问题。</p>
<p>这为开发人员节省了时间，并使大型代码库易于维护。 Go 中没有像 <code>maps</code> 和 <code>filters</code> 这样的 "表达性 "功能。</p>
<blockquote>
<p>"当你有增加表现力的功能时，通常会增加系统开销。"--Rob Pike</p>
</blockquote>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*AUiSG5Gqz8MzaGCvGpckGA.png" alt="1*AUiSG5Gqz8MzaGCvGpckGA" width="600" height="400" loading="lazy"></p>
<p>最近发表的新的 golang 标志: <a href="https://blog.golang.org/go-brand">https://blog.golang.org/go-brand</a></p>
<h3 id="">入门</h3>
<p>Go 是由 packages（包）组成的。package main 告诉 Go 编译器，该程序被编译为可执行文件，而不是共享库。它是一个应用程序的入口点。package main 的定义如下:</p>
<pre><code class="language-go">package main
</code></pre>
<p>让我们继续前进，在 Go workspace 创建一个 <code>main.go</code> 文件，编写一个简单的 hello world 例子。</p>
<h4 id="workspace"><strong>Workspace</strong></h4>
<p>Go 中的 workspace 是由环境变量 <code>GOPATH</code> 定义的。</p>
<p>你写的任何代码都要写在 workspace 里面。Go 将搜索 <code>GOPATH</code> 目录内的任何软件包，或者 <code>GOROOT</code> 目录，该目录在安装 Go 时默认设置。<code>GOROOT</code> 是安装 Go 的路径。</p>
<p>设置 <code>GOPATH</code> 到你想要的目录。现在，让我们把它添加到 <code>~/workspace</code> 文件夹内。</p>
<pre><code class="language-shell"># export env
export GOPATH=~/workspace
# go inside the workspace directory
cd ~/workspace
</code></pre>
<p>在我们刚刚创建的 workspace 文件夹中创建 <code>main.go</code> 文件，其中包含以下代码。</p>
<h4 id="helloworld">Hello World</h4>
<pre><code class="language-go">package main

import (
 "fmt"
)

func main(){
  fmt.Println("Hello World!")
}
</code></pre>
<p>在上面的例子中，<code>fmt</code>是 Go 中的一个内置包，它实现了用于格式化 I/O 输出的函数。</p>
<p>我们通过使用 <code>import</code> 关键字在 Go 中导入一个包。<code>func main</code> 是代码被执行的主入口点。<code>Println</code> 是包 <code>fmt</code> 中的一个函数，它为我们打印出 "hello world"。</p>
<p>让我们通过运行这个文件来看看。我们有两种方法可以运行 Go 命令。正如我们所知，Go 是一种编译语言，所以我们首先需要在执行之前编译它。</p>
<pre><code class="language-shell">&gt; go build main.go
</code></pre>
<p>这将创建一个二进制可执行文件<code>main</code>，现在我们可以运行:</p>
<pre><code class="language-shell">&gt; ./main 
# Hello World!
</code></pre>
<p>还有一种更简单的方法来运行程序。<code>go run</code> 命令有助于抽象编译步骤（译者注：直接执行源码中的 main() 函数，不会在当前目录留下可执行文件）。你可以简单地运行以下命令来执行该程序。</p>
<pre><code class="language-shell">go run main.go
# Hello World!
</code></pre>
<p><strong><em>注意</em></strong> :  <em>要尝试本博客中提到的代码，你可以使用 <a href="https://play.golang.org/">https://play.golang.org</a></em></p>
<h3 id="variables">Variables（变量）</h3>
<p>Go 中的变量是明确声明的。Go 是一种静态类型的语言。这意味着在声明变量的时候会检查变量的类型。一个变量:</p>
<pre><code class="language-go">var a int
</code></pre>
<p>在这种情况下，值将被设置为 0。 使用下面的语法来声明和初始化一个具有不同值的变量:</p>
<pre><code class="language-go">var a = 1
</code></pre>
<p>这里的变量被自动分配为<code>int</code>。 我们可以对变量的声明使用一个简短定义，即:</p>
<pre><code class="language-go">message := "hello world"
</code></pre>
<p>我们也可以在同一行中声明多个变量:</p>
<pre><code class="language-go">var b, c int = 2, 3
</code></pre>
<h3 id="datatypes">Data types（数据类型）</h3>
<p>像其他编程语言一样，Go 支持各种不同的数据结构。让我们来探索其中:</p>
<h4 id="numberstringandboolean"><strong>Number, String, and Boolean (整型 字符串和布尔值)</strong></h4>
<p>支持的整型包括 int、int8、int16、int32、int64、uint、uint8、uint16、uint32、uint64、uintptr（译者注：无符号整型，长度跟平台相关，它的长度可以用来保存一个指针地址）等。</p>
<p>字符串类型存储一个字节序列。它用关键字 <code>string</code> 来表示和声明。</p>
<p>布尔值使用关键字 <code>bool</code> 来存储。</p>
<p>Go 也支持复数类型，可以用 <code>complex64</code> 和 <code>complex128</code> 来声明。</p>
<pre><code class="language-go">var a bool = true
var b int = 1
var c string = 'hello world'
var d float32 = 1.222
var x complex128 = cmplx.Sqrt(-5 + 12i)
</code></pre>
<h4 id="arraysslicesandmaps">Arrays, Slices, and Maps（数组、切片和映射）</h4>
<p>数组是由相同数据类型的元素组成的一个序列。数组在声明时有一个固定的长度，所以它不能被扩大到超过这个长度。声明一个数组：</p>
<pre><code class="language-go">var a [5]int
</code></pre>
<p>数组也可以是多维的。我们可以简单地用以下方式创建它们:</p>
<pre><code class="language-go">var multiD [2][3]int
</code></pre>
<p>在运行时更改数组是受限的。数组也没有提供获取子数组的能力。 为此，Go 有一种数据类型，叫做切片（slices）。</p>
<p>切片存储了一连串的元素，并且可以在任何时候扩展。切片声明与数组声明类似--但没有定义容量:</p>
<pre><code class="language-go">var b []int
</code></pre>
<p>这将创建一个容量为 0、长度为 0 的切片。也可以用容量和长度来定义切片。我们可以用下面的语法来定义它：</p>
<pre><code class="language-go">numbers := make([]int,5,10)
</code></pre>
<p>这里，切片的初始长度为 5，容量为 10。</p>
<p>切片是对数组的一种抽象。切片使用一个数组作为底层结构。一个切片包含三个部分：容量、长度和一个指向底层数组的指针，如下图所示:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*P0lNCO0sQwIYHLEX_mfSOQ.png" alt="1*P0lNCO0sQwIYHLEX_mfSOQ" width="600" height="400" loading="lazy"></p>
<p>图片源自: <a href="https://blog.golang.org/go-slices-usage-and-internals">https://blog.golang.org/go-slices-usage-and-internals</a></p>
<p>一个切片的容量可以通过使用 append 或 copy 函数来增加。append 函数将值添加到数组的末端，如果需要的话也可以增加容量。</p>
<pre><code class="language-go">numbers = append(numbers, 1, 2, 3, 4)
</code></pre>
<p>另一种增加切片容量的方法是使用 copy 函数。简单地创建另一个容量更大的切片，并将原来的切片复制到新创建的切片上:</p>
<pre><code class="language-go">// create a new slice
number2 := make([]int, 15)
// copy the original slice to new slice
copy(number2, number)
</code></pre>
<p>我们可以创建一个切片的子切片。这可以通过以下命令简单地完成：</p>
<pre><code class="language-go">// initialize a slice with 4 len and values
number2 = []int{1,2,3,4}
fmt.Println(numbers) // -&gt; [1 2 3 4]
// create sub slices
slice1 := number2[2:]
fmt.Println(slice1) // -&gt; [3 4]
slice2 := number2[:3]
fmt.Println(slice2) // -&gt; [1 2 3]
slice3 := number2[1:4]
fmt.Println(slice3) // -&gt; [2 3 4]
</code></pre>
<p>映射是 Go 中的一种数据类型，它将键映射到值。我们可以使用以下命令来定义一个 map：</p>
<pre><code class="language-go">var m map[string]int
</code></pre>
<p><code>m</code> 是新的 map 变量, 它的键是 <code>string</code> 类型， 值是 <code>integers</code> 类型。我们很容易在 map 上添加键值对:</p>
<pre><code class="language-go">// adding key/value
m['clearity'] = 2
m['simplicity'] = 3
// printing the values
fmt.Println(m['clearity']) // -&gt; 2
fmt.Println(m['simplicity']) // -&gt; 3
</code></pre>
<h3 id="typecasting"><strong>Typecasting（类型转换）</strong></h3>
<p>一种类型的数据类型可以通过类型转换转换为另一种类型。让我们看看一个简单的类型转换：</p>
<pre><code class="language-go">a := 1.1
b := int(a)
fmt.Println(b)
//-&gt; 1
</code></pre>
<p>不是所有类型的数据类型都可以转换为另一种类型。请确保数据类型与转换的内容相匹配。</p>
<h3 id="conditionalstatements">Conditional Statements（条件语句）</h3>
<h4 id="ifelse">if else</h4>
<p>对于条件性语句，我们可以使用 if-else 语句，如下例所示。请确保大括号与条件语句在同一行。</p>
<pre><code class="language-go">if num := 9; num &lt; 0 {
 fmt.Println(num, "is negative")
} else if num &lt; 10 {
 fmt.Println(num, "has 1 digit")
} else {
 fmt.Println(num, "has multiple digits")
}
</code></pre>
<h4 id="switchcase">switch case</h4>
<p>Switch cases 有助于组织多个条件语句。下面的例子显示了一个简单的 switch 语句：</p>
<pre><code class="language-go">i := 2
switch i {
case 1:
 fmt.Println("one")
case 2:
 fmt.Println("two")
default:
 fmt.Println("none")
}
</code></pre>
<h3 id="looping">Looping（循环）</h3>
<p>Go 有一个循环的关键词 <code>for</code>。for 循环命令用于实现不同种类的循环：</p>
<pre><code class="language-go">i := 0
sum := 0
for i &lt; 10 {
 sum += 1
  i++
}
fmt.Println(sum)
</code></pre>
<p>上面的例子类似于 C 语言中的 while 循环。</p>
<p>Go 中的 for 语句也可以用于普通的 for 循环：</p>
<pre><code class="language-go">sum := 0
for i := 0; i &lt; 10; i++ {
  sum += i
}
fmt.Println(sum)
</code></pre>
<p>Go 中的死循环:</p>
<pre><code class="language-go">for {
}
</code></pre>
<h3 id="pointers">Pointers（指针）</h3>
<p>Go 提供了指针。指针是用来保存一个变量的地址的地方。指针是由 * 定义的。指针是根据数据的类型来定义的，例如：</p>
<pre><code class="language-go">var ap *int
</code></pre>
<p><code>ap</code> 是指向一个整数类型的指针。 <code>&amp;</code> 操作符可以用来获取一个变量的地址。</p>
<pre><code class="language-go">a := 12
ap = &amp;a
</code></pre>
<p>指针所指向的值可以使用 <code>*</code> 操作符来访问：</p>
<pre><code class="language-go">fmt.Println(*ap)
// =&gt; 12
</code></pre>
<p>在传递结构体作为参数时，或者在为定义的类型声明方法时，通常倾向于使用指针。</p>
<ol>
<li>传递值时，值实际上被复制，这意味着需要更多的内存。</li>
<li>传递指针时，函数改变的值会反映回方法/函数的调用者。</li>
</ol>
<p>例如:</p>
<pre><code class="language-go">func increment(i *int) {
  *i++
}
func main() {
  i := 10
  increment(&amp;i)
  fmt.Println(i)
}
//=&gt; 11
</code></pre>
<p>注意：当你在尝试博客中的示例代码时，不要忘记包含 <code>package main</code>，并在需要时导入 <code>fmt</code> 或其他包，如上面第一个 main.go 例子中所示。</p>
<h3 id="functions">Functions（函数）</h3>
<p>在 main package 中定义的 main 函数是 go 程序执行的入口。更多的函数可以被定义和使用。让我们来看看一个简单的例子。</p>
<pre><code class="language-go">func add(a int, b int) int {
  c := a + b
  return c
}
func main() {
  fmt.Println(add(2, 1))
}
//=&gt; 3
</code></pre>
<p>在上面的例子中我们可以看到，Go 函数是用 <strong>func</strong> 关键字来定义的，后面是函数名称。一个函数的 <strong>参数</strong> 需要根据其数据类型来定义，最后是返回的数据类型。</p>
<p>一个函数的返回值也可以在函数中预先定义：</p>
<pre><code class="language-go">func add(a int, b int) (c int) {
  c = a + b
  return
}
func main() {
  fmt.Println(add(2, 1))
}
//=&gt; 3
</code></pre>
<p>这里 c 被定义为返回变量。所以定义的变量 c 会自动返回，而不需要在最后的返回语句中定义。</p>
<p>你也可以从一个函数中返回多个返回值，用逗号来分隔返回值。</p>
<pre><code class="language-go">func add(a int, b int) (int, string) {
  c := a + b
  return c, "successfully added"
}
func main() {
  sum, message := add(2, 1)
  fmt.Println(message)
  fmt.Println(sum)
}
</code></pre>
<h3 id="methodstructsandinterfaces">Method, Structs, and Interfaces（方法，结构体，接口）</h3>
<p>Go 并不是一种完全面向对象的语言，但通过结构体（Struct）、接口（Interface）和方法（Method），它有很多面向对象的支持和感觉。</p>
<h4 id="struct">Struct（结构体）</h4>
<p>结构体是一种类型化的、不同字段的集合。结构体用于将数据分组。例如，如果我们想对 Person 类型的数据进行分组，我们可以定义一个人的属性，其中可能包括姓名、年龄、性别。可以使用以下语法来定义一个结构体:</p>
<pre><code class="language-go">type person struct {
  name string
  age int
  gender string
}
</code></pre>
<p>在定义了一个人的类型结构后，现在让我们来创建一个 person：</p>
<pre><code class="language-go">//way 1: specifying attribute and value
p = person{name: "Bob", age: 42, gender: "Male"}
//way 2: specifying only value
person{"Bob", 42, "Male"}
</code></pre>
<p>我们可以很容易地用一个点（.）来访问这些数据。</p>
<pre><code class="language-go">p.name
//=&gt; Bob
p.age
//=&gt; 42
p.gender
//=&gt; Male
</code></pre>
<p>你也可以用结构的指针直接访问其属性：</p>
<pre><code class="language-go">pp = &amp;person{name: "Bob", age: 42, gender: "Male"}
pp.name
//=&gt; Bob
</code></pre>
<h4 id="methods">Methods（方法）</h4>
<p>方法(Method)是一种特殊的函数类型，它有一个 <em>receiver</em> 。 <em>receiver</em> 可以是一个值或一个指针。让我们创建一个名为 describe 的方法(Method)，它有一个我们在上面的例子中创建的接收器类型的 person：</p>
<pre><code class="language-go">package main
import "fmt"

// struct definition
type person struct {
  name   string
  age    int
  gender string
}

// method definition
func (p *person) describe() {
  fmt.Printf("%v is %v years old.", p.name, p.age)
}
func (p *person) setAge(age int) {
  p.age = age
}

func (p person) setName(name string) {
  p.name = name
}

func main() {
  pp := &amp;person{name: "Bob", age: 42, gender: "Male"}
  pp.describe()
  // =&gt; Bob is 42 years old
  pp.setAge(45)
  fmt.Println(pp.age)
  //=&gt; 45
  pp.setName("Hari")
  fmt.Println(pp.name)
  //=&gt; Bob
}
</code></pre>
<p>正如我们在上面的例子中看到的，现在可以使用点运算符来调用该方法，如 <code>pp.describe</code>。请注意，<em>receiver</em> 是一个指针。使用指针，我们传递的是一个值的引用，所以如果我们在方法中做任何改变，都会反映在  <em>receiver</em> pp 中。它也不会创建一个新的对象的副本，这就节省了内存。</p>
<p>请注意，在上面的例子中，年龄的值被改变了，而名字的值没有改变，因为 setName 方法是 <em>receiver</em> 类型是值类型，而 setAge 是指针类型的。</p>
<h4 id="interfaces">Interfaces（接口）</h4>
<p>Go 接口（interfaces）是一个方法（methods）的集合。接口有助于将一个类型的属性组合在一起。让我们以一个接口 animal 为例：</p>
<pre><code class="language-go">type animal interface {
  description() string
}
</code></pre>
<p>animal 是一个接口（interface）类型。现在让我们创建两个不同类型的 animal，它们都实现了 animal 接口类型：</p>
<pre><code class="language-go">package main

import (
  "fmt"
)

type animal interface {
  description() string
}

type cat struct {
  Type  string
  Sound string
}

type snake struct {
  Type      string
  Poisonous bool
}

func (s snake) description() string {
  return fmt.Sprintf("Poisonous: %v", s.Poisonous)
}

func (c cat) description() string {
  return fmt.Sprintf("Sound: %v", c.Sound)
}

func main() {
  var a animal
  a = snake{Poisonous: true}
  fmt.Println(a.description())
  a = cat{Sound: "Meow!!!"}
  fmt.Println(a.description())
}

//=&gt; Poisonous: true
//=&gt; Sound: Meow!!!
</code></pre>
<p>type cat struct {<br>
在主函数中，我们创建一个动物类型的变量 <code>a</code>。我们给动物分配一个 snake 和一个 cat 的类型，并使用 Println 来打印 a.description。由于我们在两种类型（cat 和 snake）中都以不同的方式实现了 describe 方法，我们得到了打印的动物描述。</p>
<h3 id="packages">Packages（包）</h3>
<p>我们把 Go 的所有代码都写在一个包里。<strong>main</strong> package 是程序执行的入口点。Go 中有很多内置包。我们一直在使用的最著名的是<strong>fmt</strong>包。</p>
<blockquote>
<p>"Go 软件包是 Go 提供的大型编程的主要机制，它们使得将一个大型项目分割成小块成为可能。"<br>
— Robert Griesemer</p>
</blockquote>
<h4 id="installingapackage">Installing a package (安装一个包)</h4>
<pre><code class="language-shell">go get &lt;package-url-github&gt;
// example
go get github.com/satori/go.uuid
</code></pre>
<p>我们安装的软件包被保存在 GOPATH 环境变量设置的工作目录。你可以通过进入我们工作目录下的 pkg 文件夹 <code>cd $GOPATH/pkg</code> 来查看这些软件包。</p>
<h4 id="somebuiltinpackagesingogo">Some built-in packages in Go（Go 内置包）</h4>
<p><strong>fmt</strong></p>
<p>该包实现了格式化的 I/O 函数。我们已经用这个包实现了向 stdout 打印的功能。</p>
<p><strong>json</strong></p>
<p>Go 中另一个有用的包是 json 包。这有助于对 JSON 进行编码/解码。让我们举个例子，对一些 JSON 进行编码/解码：</p>
<p>编码</p>
<pre><code class="language-go">package main

import (
  "fmt"
  "encoding/json"
)

func main(){
  mapA := map[string]int{"apple": 5, "lettuce": 7}
  mapB, _ := json.Marshal(mapA)
  fmt.Println(string(mapB))
}
</code></pre>
<p>解码</p>
<pre><code class="language-go">package main

import (
  "fmt"
  "encoding/json"
)

type response struct {
  PageNumber int `json:"page"`
  Fruits []string `json:"fruits"`
}

func main(){
  str := `{"page": 1, "fruits": ["apple", "peach"]}`
  res := response{}
  json.Unmarshal([]byte(str), &amp;res)
  fmt.Println(res.PageNumber)
}
//=&gt; 1
</code></pre>
<p>当使用 unmarshal 解码 json 字节时，第一个参数是 json 字节，第二个参数是我们希望 json 被映射到的响应类型结构的地址。注意，<code>json: "page"</code>将页面键映射到结构中的 PageNumber 键。</p>
<h3 id="errorhandling">Error Handling（错误处理）</h3>
<p>错误是指程序中不想要的和意外的结果。比方说，我们正在对一个外部服务进行 API 调用。这个 API 调用可能是成功的，也可能是失败的。当错误类型出现时，Go 程序中的错误可以被识别。让我们看看这个例子:</p>
<pre><code class="language-go">resp, err := http.Get("http://example.com/")
</code></pre>
<p>在这里，API 调用可能通过也可能失败。我们可以检查错误是否为 <code>nil</code>或存在，并相应地处理响应:</p>
<pre><code class="language-go">package main

import (
  "fmt"
  "net/http"
)

func main(){
  resp, err := http.Get("http://example.com/")
  if err != nil {
    fmt.Println(err)
    return
  }
  fmt.Println(resp)
}
</code></pre>
<h4 id="returningcustomerrorfromafunction">Returning custom error from a function (从函数返回自定义错误)</h4>
<p>当我们在编写自己的函数时，有些情况下会出现错误。这些错误可以在错误对象的帮助下返回:</p>
<pre><code class="language-go">func Increment(n int) (int, error) {
  if n &lt; 0 {
    // return error object
    return nil, errors.New("math: cannot process negative number")
  }
  return (n + 1), nil
}
func main() {
  num := 5
 
  if inc, err := Increment(num); err != nil {
    fmt.Printf("Failed Number: %v, error message: %v", num, err)
  }else {
    fmt.Printf("Incremented Number: %v", inc)
  }
}
</code></pre>
<p>大多数 Go 中内置的包，或者我们使用的外部包，都有一个错误处理的机制。所以我们调用的任何函数都有可能出现错误。这些错误绝不应该被忽视，总是在我们调用这些函数的地方优雅地处理，正如我们在上面的例子中所做的那样。</p>
<h4 id="panic">Panic</h4>
<p>Panic 是指在程序执行过程中突然遇到的未被处理的东西。在 Go 中，Panic 不是处理程序中异常的理想方式。建议使用一个错误对象来代替。当 Panic 发生时，程序的执行会被停止。Panic 发生后被执行的东西是 defer。</p>
<pre><code class="language-go">//Go
package main

import "fmt"

func main() {
    f()
    fmt.Println("Returned normally from f.")
}

func f() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in f", r)
        }
    }()
    fmt.Println("Calling g.")
    g(0)
    fmt.Println("Returned normally from g.")
}

func g(i int) {
    if i &gt; 3 {
        fmt.Println("Panicking!")
        panic(fmt.Sprintf("%v", i))
    }
    defer fmt.Println("Defer in g", i)
    fmt.Println("Printing in g", i)
    g(i + 1)
}
</code></pre>
<h4 id="defer">Defer</h4>
<p>Defer 是指总是在函数的末尾被执行的东西。</p>
<p>在上面的例子中，我们用 panic() 使程序的执行陷入 panic。正如你所注意到的，这里有一个 defer 语句，它将使程序在最后执行这一行。当我们需要在函数结束时执行一些东西时也可以使用 defer，例如关闭一个文件。</p>
<h3 id="concurrency">Concurrency (并发)</h3>
<p>Go 是在考虑到并发性的情况下建立的。Go 中的并发性可以通过 Go 协程实现，它是轻量级的线程。</p>
<p><strong>Go routine (Go 协程)</strong></p>
<p>Go 协程是可以与另一个函数并行或同时运行的函数。创建一个 Go 协程非常简单。只需在一个函数前面加上关键字 Go，我们就可以让它并行执行。Go 协程是非常轻量级的，所以我们可以创建成千上万的协程。让我们来看看一个简单的例子:</p>
<pre><code class="language-go">package main
import (
  "fmt"
  "time"
)
func main() {
  go c()
  fmt.Println("I am main")
  time.Sleep(time.Second * 2)
}
func c() {
  time.Sleep(time.Second * 2)
  fmt.Println("I am concurrent")
}
//=&gt; I am main
//=&gt; I am concurrent
</code></pre>
<p>正如你在上面的例子中所看到的，函数 c 是一个 Go 例程，与 Go 主线程并行执行。有些时候，我们希望在多个线程之间共享资源。Go 倾向于不将一个线程的变量与另一个线程共享，因为这样会增加死锁和资源等待的可能性。还有一种方法可以在 Go 协程之间共享资源：通过 Go channels。</p>
<p><strong>Channels (通道)</strong></p>
<p>我们可以使用通道在两个 Go 协程之间传递数据。在创建 channel 时，有必要指定该 channel 接收什么样的数据。让我们创建一个简单的字符串类型的 channel，如下所示:</p>
<pre><code class="language-go">c := make(chan string)
</code></pre>
<p>通过这个 channel，我们可以发送字符串类型的数据。我们可以在这个 channel 中发送和接收数据:</p>
<pre><code class="language-go">package main

import "fmt"

func main(){
  c := make(chan string)
  go func(){ c &lt;- "hello" }()
  msg := &lt;-c
  fmt.Println(msg)
}
//=&gt;"hello"
</code></pre>
<p>接收方 channel 等待，直到发送方发送数据到 channel。</p>
<p><strong>One way channel (单向通道)</strong></p>
<p>有些情况下，我们希望 Go 程序通过 channel 接收数据，但不发送数据，反之亦然。为此，我们也可以创建一个<strong>单向 channel</strong>。让我们来看看一个简单的例子:</p>
<pre><code class="language-go">package main

import (
 "fmt"
)

func main() {
 ch := make(chan string)
 
 go sc(ch)
 fmt.Println(&lt;-ch)
}

func sc(ch chan&lt;- string) {
 ch &lt;- "hello"
}
</code></pre>
<p>在上面的例子中，<code>sc</code> 是一个只用于发送消息到通道但不能接受消息的 go 协程。</p>
<h3 id="organizingmultiplechannelsforagoroutineusingselectselectgo">Organizing multiple channels for a Go routine using select (使用 select 为 Go 协程组织多个通道)</h3>
<p>一个函数可能有多个 channel 在等待。为此，我们可以使用一个选择（select）语句。让我们看一个例子，以了解更清楚的情况:</p>
<pre><code class="language-go">package main

import (
 "fmt"
 "time"
)

func main() {
 c1 := make(chan string)
 c2 := make(chan string)
 go speed1(c1)
 go speed2(c2)
 fmt.Println("The first to arrive is:")
 select {
 case s1 := &lt;-c1:
  fmt.Println(s1)
 case s2 := &lt;-c2:
  fmt.Println(s2)
 }
}

func speed1(ch chan string) {
 time.Sleep(2 * time.Second)
 ch &lt;- "speed 1"
}

func speed2(ch chan string) {
 time.Sleep(1 * time.Second)
 ch &lt;- "speed 2"
}
</code></pre>
<p>在上面的例子中，main 正在等待两个 channel，c1 和 c2。通过 select case 语句，main 函数打印出，信息从它先收到的 channel 中发送出来。</p>
<p><strong>Buffered channel(带缓冲的通道)</strong></p>
<p>你可以在 go 中创建一个缓冲 channel。有了缓冲 channel，如果缓冲区满了，发送到该 channel 的消息就会被阻断。让我们看一下这个例子:</p>
<pre><code class="language-go">package main

import "fmt"

func main(){
  ch := make(chan string, 2)
  ch &lt;- "hello"
  ch &lt;- "world"
  ch &lt;- "!" // extra message in buffer
  fmt.Println(&lt;-ch)
}

// =&gt; fatal error: all goroutines are asleep - deadlock!
</code></pre>
<p>正如我们在上面看到的，一个 channel 接受的信息不超过 2 条。</p>
<h4 id="golang">为什么 Golang 会成功？</h4>
<blockquote>
<p>简洁性…… — Rob-pike</p>
</blockquote>
<h3 id="great">Great</h3>
<p>我们学习了 Go 的一些主要组成部分和特点。</p>
<ol>
<li>变量、数据类型</li>
<li>数组 切片 和 映射</li>
<li>函数</li>
<li>循环和条件语句</li>
<li>指针</li>
<li>软件包</li>
<li>方法、结构体和接口</li>
<li>错误处理</li>
<li>并发 - Go 协程和通道</li>
</ol>
<p>恭喜你，你现在对 Go 有了相当的了解。</p>
<blockquote>
<p>我最有成效的一天是减少了 1000 行代码。<br>
— Ken Thompson</p>
</blockquote>
<p>不要停在这里。继续向前推进。思考一个小的应用并开始创建。</p>
<p><a href="https://www.linkedin.com/in/milap-neupane-99a4b565/">LinkedIn</a><br>
<a href="http://github.com/milap-neupane">Github</a><br>
<a href="https://twitter.com/_milap">Twitter</a></p>
<p>本文也发布在 Milap Neupane 博客：<a href="https://milapneupane.com.np/2019/07/06/learning-golang-from-zero-to-hero/">从 0 到 1 学习 Go</a></p>
<!--kg-card-end: markdown--><p>感谢 <a href="https://www.freecodecamp.org/chinese/news/author/mingxun/">Mingxun Liu</a> 校对这篇译文。</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Go 语言手册 ]]>
                </title>
                <description>
                    <![CDATA[ Golang 是一门非常棒的简洁且快速的现代编程语言。 它是编译的、开源的、强类型的。 Golang——也被称为 Go——由谷歌工程师创建，主要目标如下：  * 让他们的项目编译（和运行）更快  * 要简单，这样人们可以在短时间内学会  * 要足够底层，但也要避免过于关注底层  * 可移植（经过编译的 Go 程序不需要其他文件的支持，就可以跨平台运行，因此它们很轻易分发）  * 简洁而枯燥、稳定、可预测，从而减少犯错的机会  * 易于利用多处理器系统 Go 是为了取代 C 和 C++ 代码库。它旨在通过垃圾回收机制使并发和内存管理等事情变得更简单。 此外，由于其与 C 语言的互操作特性，可以在 C 和 C++ 代码库中使用 Go 语言。 你可以用 Go 做很多不一样的任务，它既可以解决简单的问题也可以解决复杂的问题。 你可以使用 Go 来创建命令行工具和网络服务器，它广泛用于许多不同的场景。 举个例子，Docker 和 K8s 是用 ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/go-beginners-handbook/</link>
                <guid isPermaLink="false">6374f4897cdd940712f7c6b2</guid>
                
                    <category>
                        <![CDATA[ Golang ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ herosql ]]>
                </dc:creator>
                <pubDate>Wed, 16 Nov 2022 04:19:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/11/golang.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>原文：</strong> <a href="https://www.freecodecamp.org/news/go-beginners-handbook/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">The Go Handbook – Learn Golang for Beginners</a>
      </p><!--kg-card-begin: markdown--><p>Golang 是一门非常棒的简洁且快速的现代编程语言。</p>
<p>它是编译的、开源的、强类型的。</p>
<p>Golang——也被称为 Go——由谷歌工程师创建，主要目标如下：</p>
<ul>
<li>让他们的项目编译（和运行）更快</li>
<li>要简单，这样人们可以在短时间内学会</li>
<li>要足够底层，但也要避免过于关注底层</li>
<li>可移植（经过编译的 Go 程序不需要其他文件的支持，就可以跨平台运行，因此它们很轻易分发）</li>
<li>简洁而枯燥、稳定、可预测，从而减少犯错的机会</li>
<li>易于利用多处理器系统</li>
</ul>
<p>Go 是为了取代 C 和 C++ 代码库。它旨在通过垃圾回收机制使并发和内存管理等事情变得更简单。</p>
<p>此外，由于其与 C 语言的互操作特性，可以在 C 和 C++ 代码库中使用 Go 语言。</p>
<p>你可以用 Go 做很多不一样的任务，它既可以解决简单的问题也可以解决复杂的问题。</p>
<p>你可以使用 Go 来创建命令行工具和网络服务器，它广泛用于许多不同的场景。</p>
<p>举个例子，Docker 和 K8s 是用 Go 编写的。</p>
<p>我最喜欢的静态网站生成工具 Hugo 是用 Go 编写的。</p>
<p>Caddy，一个非常流行的 web 服务器是用 GO 编写的。</p>
<p>众多被广泛使用的工具都是基于这门语言创建的。</p>
<p>本手册将向你介绍 Go 编程语言，以便你开始使用 Go 编程。</p>
<p><a href="https://thevalleyofcode.com/download/go/">你可以点击链接获得 GO 初学者手册的 pdf 版本和 ePub 版本</a>。</p>
<h2 id="">目录</h2>
<ol>
<li><a href="#how-to-get-started-with-go">如何开始使用 Go</a></li>
<li><a href="#how-to-install-go">如何安装 Go</a></li>
<li><a href="#how-to-setup-your-editor">如何设置编辑器</a></li>
<li><a href="#how-to-write-hello-world-in-go">如何用 Go 编写 Hello, World!</a></li>
<li><a href="#how-to-compile-and-run-a-go-program">如何编译和运行 Go 程序</a></li>
<li><a href="#the-go-workspace">Go 的工作空间</a></li>
<li><a href="#diving-into-the-go-language">深入 Go 语言</a></li>
<li><a href="#variables-in-go">Go 中的变量</a></li>
<li><a href="#basic-types-in-go">Go 的基础类型</a></li>
<li><a href="#strings-in-go">Go 中的字符串</a></li>
<li><a href="#arrays-in-go">Go 中的数组</a></li>
<li><a href="#slices-in-go">Go 中的切片</a></li>
<li><a href="#maps-in-go">Go 中的 map</a></li>
<li><a href="#loops-in-go">Go 中的循环</a></li>
<li><a href="#conditionals-in-go">Go 中的条件语句</a></li>
<li><a href="#operators-in-go">Go 中的运算符</a></li>
<li><a href="#structs-in-go">Go 中的结构体</a></li>
<li><a href="#functions-in-go">Go 中的函数</a></li>
<li><a href="#pointers-in-go">Go 中的指针</a></li>
<li><a href="#methods-in-go">Go 中的方法</a></li>
<li><a href="#interfaces-in-go">Go 中的接口</a></li>
<li><a href="#where-to-go-from-here">更多内容</a></li>
</ol>
<h2 id="dividhowtogetstartedwithgogodiv"><div id="how-to-get-started-with-go">如何开始使用 Go</div></h2>
<p>在我们深入了解语言的细节之前，你应该了解以下几点。</p>
<p>首先， <a href="https://go.dev/">https://go.dev</a> 是语言的官网。 在官网你可以获得以下资源：</p>
<ul>
<li>下载 Go 的二进制文件（<code>go</code> 命令和其他相关工具）<a href="https://go.dev/doc/install">https://go.dev/doc/install</a></li>
<li>参考 Go 的官方文档 <a href="https://go.dev/doc/">https://go.dev/doc/</a></li>
<li>查看 Go 的所有第三方库 <a href="https://pkg.go.dev/">https://pkg.go.dev/</a></li>
<li>进入 Go 游乐园 <a href="https://go.dev/play/">https://go.dev/play/</a></li>
</ul>
<p>......等等。</p>
<h2 id="dividhowtoinstallgogodiv"><div id="how-to-install-go">如何安装 Go</div></h2>
<p>去 <a href="https://go.dev/doc/install">https://go.dev/doc/install</a> 下载适用于你电脑操作系统的软件包。</p>
<p>运行并安装，在最后一个步骤你需要在命令行设置 <code>go</code> 命令：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_10.19.21.png" alt="欢迎进行 Go 的安装" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>欢迎进行 Go 的安装</figcaption>
</figure>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_10.20.54.png" alt="安装成功" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>安装成功</figcaption>
</figure>
<p>打开命令行并运行 <code>go version</code> 你会看到以下内容：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_10.21.32.png" alt="展示你当前的 Go 版本" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>展示你当前的 Go 版本</figcaption>
</figure>
<p>注意： 在运行程序之前，你可能需要打开一个新的命令行，因为安装程序将 Go 二进制文件文件夹添加到了路径中。</p>
<p>Go 安装文件的确切位置取决于你的操作系统。</p>
<p>在 Mac 系统中，它在 <code>/usr/local/go</code> ， 运行文件在 <code>/usr/local/go/bin</code> 。</p>
<p>在 Windows 系统中，它在 <code>C:\Program Files\go</code>。</p>
<p>在 Windows 和 Mac 安装中 Go 执行文件路径都是自动设定的。</p>
<p>在 Mac 你可以在 Homebrew 中使用 <code>brew install golang</code> 命令安装。 这样方式更容易升级到最新版本。</p>
<p>在 Linux 上，你必须将 Go 二进制文件文件夹添加到你的环境变量中，然后才能在使用以下命令将 Linux 包解压缩到 <code>/usr/local/go</code>之后运行 <code>go</code> 命令：</p>
<pre><code class="language-bash">echo 'export PATH=$PATH:/usr/local/go/bin' &gt;&gt; $HOME/.profile
source $HOME/.profile
</code></pre>
<h2 id="dividhowtosetupyoureditordiv"><div id="how-to-setup-your-editor">如何设置编辑器</div></h2>
<p>我推荐使用 <a href="https://code.visualstudio.com/"><strong>Visual Studio Code</strong></a>（也叫 VS Code）作为你的编辑器。</p>
<p>请阅读<a href="https://code.visualstudio.com/docs/languages/go">在 Visual Studio Code 的 Go</a>，了解快速安装并运行的方法。安装<a href="https://marketplace.visualstudio.com/items?itemName=golang.go">Go 的扩展</a>。</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_10.54.06.png" alt="VS Code 中的 Go 扩展" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>VS Code 中的 Go 扩展</figcaption>
</figure>
<p>这个扩展将让你的生活更加轻松。因为它提供智能感知（语法高亮显示、自动补全、悬停信息、错误高亮显示…）和其他功能，如自动格式化、安装软件包的菜单选项、测试等。</p>
<h2 id="dividhowtowritehelloworldingogohelloworlddiv"><div id="how-to-write-hello-world-in-go">如何用 Go 编写 Hello, World!</div></h2>
<p>现在我们准备创建我们第一个 Go 程序！</p>
<p>程序员的传统是让第一个程序打印 “Hello, World!” 字符串到命令行。所以我们先做这个，然后解释我们是如何做到的。</p>
<p>或许你可以在你主目录下创建一个文件夹,保存你所有编写的项目和测试。</p>
<p>在这，创建一个新的文件夹，比如取名叫 <code>hello</code>。</p>
<p>在这，创建一个叫 <code>hello.go</code> 的文件（你可以用任何想要用的名字）。</p>
<p>文件内容如下：</p>
<pre><code class="language-go">package main

import "fmt"

func main() {
	fmt.Println("Hello, World!")
}
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_12.17.14.png" alt="Go " hello,="" world!"="" 代码"="" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>Go "Hello, World!" 代码</figcaption>
</figure>
<p>这是你编写的第一个 Go 程序！</p>
<p>让我们逐行分析一下。</p>
<pre><code class="language-go">package main
</code></pre>
<p>我们以包的形式组织 Go 程序。</p>
<p>每个<code>.go</code> 文件首先要声明它是哪个包的一部分。</p>
<p>一个包可以由多个文件组成，也可以仅由一个文件组成。</p>
<p>一个程序可以由多个包组成。</p>
<p>这个 <code>main</code> 是程序识别可执行程序的入口。</p>
<pre><code class="language-go">import "fmt"
</code></pre>
<p>我们使用 <code>import</code> 关键字导入包。</p>
<p><code>fmt</code> 是Go提供的内置包，提供输入/输出的工具函数。</p>
<p>我们有一个<a href="https://pkg.go.dev/std">大的标准库</a>，可以随时使用，从网络连接到数学、加密、图像处理、文件系统访问等等。</p>
<p>你可以在<a href="https://pkg.go.dev/fmt">官方文档</a>阅读 <code>fmt</code> 包提供的所有功能。</p>
<pre><code class="language-go">func main() {
	
}
</code></pre>
<p>这里我们声明 <code>main()</code> 函数。</p>
<p>什么是函数？稍后我们将看到更多关于它们的信息，但同时让我们假设一个函数是一个分配了名称的代码块，包含一些指令。</p>
<p><code>main</code> 函数是特殊的，因为这也是程序启动的地方。</p>
<p>在这个简单的例子中，我们只是有一个函数——程序从这个函数开始，然后结束。</p>
<pre><code class="language-go">fmt.Println("Hello， World!")
</code></pre>
<p>这是我们定义的函数的内容。</p>
<p>我们调用了我们之前导入的 <code>fmt</code> 包中 <code>Println()</code> 函数，将字符串作为参数传递。</p>
<p>根据<a href="https://pkg.go.dev/fmt#Printf">文档</a> “<em>根据格式指定器进行格式化并写入标准输出</em>”。</p>
<p>看看这些文档，因为它们很棒。它们甚至有你可以运行的示例：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_14.18.46.png" alt="Go 基础的函数模板" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>Go 基础的函数模板</figcaption>
</figure>
<p>我们可以用 “dot” 语法 <code>fmt.Println()</code> 指定该函数由该包提供。</p>
<p>代码在执行 <code>main</code> 函数之后，它没有做其他的事就结束了执行。</p>
<h2 id="dividhowtocompileandrunagoprogramgodiv"><div id="how-to-compile-and-run-a-go-program">如何编译和运行 Go 程序</div></h2>
<p>现在在 <code>hello</code> 文件夹下打开命令行，用以下命令运行程序：</p>
<pre><code class="language-bash">go run hello.go
</code></pre>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_12.18.23.png" alt="Go 输出 Hello world" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>Go 输出 Hello world</figcaption>
</figure>
<p>我们的程序运行成功，它会在命令行输出 “Hello, World!” 。</p>
<p><code>go run</code> 会先编译并运行程序。</p>
<p>你可以用 <code>go build</code> 创建一个<strong>可执行文件</strong>：</p>
<pre><code class="language-bash">go build hello.go
</code></pre>
<p>这里会创建一个名为 <code>hello</code> 的可执行文件，你可以执行：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_12.19.50.png" alt="执行 Go 的可执行文件" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>执行 Go 的可执行文件</figcaption>
</figure>
<p>在引言部分我提到过 Go 是可移植的。</p>
<p>现在你可以分发这个二进制文件，每个人都可以按原样运行程序，因为二进制文件已经打包好了，可以执行了。</p>
<p>该程序将在我们构建它的相同架构上运行。</p>
<p>我们可以使用 <code>GOOS</code> 和 <code>GOARCH</code> 环境变量为不同的架构创建不同的二进制文件，如下所示：</p>
<pre><code class="language-go">GOOS=windows GOARCH=amd64 go build hello.go
</code></pre>
<p>这将会创建 <code>hello.exe</code> 文件，可以在 64 位的 Windows 机器上运行：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_15.36.55.png" alt="Hello.exe 执行" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>Hello.exe 执行</figcaption>
</figure>
<p>设置 64 位的 Mac 的环境变量为 <code>GOOS=darwin GOARCH=amd64</code> ，Linux 的环境变量是<code>GOOS=linux GOARCH=amd64</code>。</p>
<p>这是 Go 最好的特性之一。</p>
<h2 id="dividthegoworkspacegodiv"><div id="the-go-workspace">Go 的工作空间</div></h2>
<p>关于 Go 中的一个特殊的点，我们称为<strong>工作区</strong>。</p>
<p>这个工作区是 Go 中的“家目录”。</p>
<p>Go 默认的路径在 <code>$HOME/go</code> 下，所以你可以在你的家目录中看到 <code>go</code> 文件夹。</p>
<p>它会在你安装包（待会儿我们看一下）进行创建。</p>
<p>例如我在 VS Code 中加载 <code>hello.go</code> 文件那一刻，它提示我安装<code>[ gopls ](https://pkg.go.dev/golang.org/x/tools/gopls)</code> 命令，开发调试工具（<code>dlv</code>）， 和<a href="https://staticcheck.io/"><code>静态检查</code>行</a>。</p>
<p>它们在 <code>$HOME/go</code> 下自动安装：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_12.27.27.png" alt="`$HOME/go`" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>`$HOME/go`</figcaption>
</figure>
<p>当你使用 <code>go install</code> 安装库时，它们会保存在这里。</p>
<p>这就是我们所说的 <strong>GOPATH</strong>。</p>
<p>你可以修改环境变量 <code>GOPATH</code> 以更改 Go 安装包的位置。</p>
<p>这在同时处理不同的项目并且希望隔离所使用的库时非常有用。</p>
<h2 id="dividdivingintothegolanguagegodiv"><div id="diving-into-the-go-language">深入 Go 语言</div></h2>
<p>现在我们已经理解了第一部分，我们运行了第一个 Hello, World! 程序， 我们可以深入 Go 语言了。</p>
<p>该语言没有语义上有意义的空格。它像 C、C++、Rust、Java、JavaScript，但是不像 Python，其中空格是有意义的，用于创建块，而不是花括号。</p>
<p>分号是可选的，就像在 JavaScript 中一样（不同于 C、C++、Rust 或 Java）。</p>
<p>Go 非常重视缩进和视觉顺序。</p>
<p>在我们安装Go程序的时候自带了 <code>gofmt</code> 命令，我们可以用它对 Go 程序进行格式化。VSCode 中在底层使用它对 Go 源码文件进行格式化。</p>
<p>这是非常有趣和创新的，因为格式和问题，如制表符与空格或“我应该把花括号放在循环定义的同一行还是下一行”，都是浪费时间。</p>
<p>语言创造者定义了规则，每个人都使用这些规则。</p>
<p>这对于拥有大型团队的项目非常有用。</p>
<p>我推荐你在 VS Code 中设置“<strong>保存时格式化</strong>” 和“<strong>粘贴时格式化</strong>”：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_14.39.42.png" alt="在 VS Code 中设置 Go 的粘贴时格式化和保存时格式化" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>在 VS Code 中设置 Go 的粘贴时格式化和保存时格式化</figcaption>
</figure>
<p>你可以使用常用的 C / C++ / JavaScript / Java 语法在 Go 中写注释：</p>
<pre><code class="language-go">// this is a line comment

/*
multi
line
comment
*/
</code></pre>
<h2 id="dividvariablesingogodiv"><div id="variables-in-go">Go 中的变量</div></h2>
<p>在编程语言中，首先要做的事情之一是定义变量。</p>
<p>在 Go 中我们用 <code>var</code> 关键字声明变量：</p>
<pre><code class="language-go">var age = 20
</code></pre>
<p>你可以在包级别定义变量：</p>
<pre><code class="language-go">package main

import "fmt"

var age = 20

func main() {
	fmt.Println("Hello， World!")
}
</code></pre>
<p>或是在函数中：</p>
<pre><code class="language-go">package main

import "fmt"

func main() {
	var age = 20

	fmt.Println("Hello， World!")
}
</code></pre>
<p>在包级别定义，变量在组成包的所有文件中都可见。一个包可以由多个文件组成，你只需要创建另一个文件并在顶部使用相同的包名。</p>
<p>在函数级别定义，变量仅在该函数中可见。它在调用函数时初始化，在函数结束执行时销毁。</p>
<p>我们使用以下示例：</p>
<pre><code class="language-go">var age = 20
</code></pre>
<p>我们设置变量 <code>age</code> 的值为 <code>20</code>。</p>
<p>这使得 Go 确定变量 <code>age</code> 的<strong>类型</strong>是 <code>int</code>。</p>
<p>稍后我们将看到更多关于类型的信息，但你应该知道有很多不同的类型，从 <code>int</code>、<code>string</code> 和 <code>bool</code> 开始。</p>
<p>我们可以不给变量设置值，但是必须设置它们的类型如下：</p>
<pre><code class="language-go">var age int
var name string
var done bool
</code></pre>
<p>当你知道值时，你可以直接用短的变量声明如 <code>:=</code> 运算符：</p>
<pre><code class="language-go">age := 10
name := "Roger"
</code></pre>
<p>变量名你可以使用小写字母，数字和 <code>_</code> 或者可以使用类似于 <code>_</code> 的其他字符。</p>
<p>名字是<strong>区分大小写</strong>的。</p>
<p>如果名字太长，通常可使用驼峰命名法，例如我们想表现车的名字就用 <code>carName</code>。</p>
<p>你可以使用赋值运算符 <code>=</code> 给一个变量赋予新的值。</p>
<pre><code class="language-go">var age int
age = 10
age = 11
</code></pre>
<p>如果你有一个变量在编程过程中永远都不会变，你可以使用 <code>const</code> 关键字来声明这个变量：</p>
<pre><code class="language-go">const age = 10
</code></pre>
<p>你可以一行代码中声明多个变量：</p>
<pre><code class="language-go">var age， name
</code></pre>
<p>将它们全部都初始化：</p>
<pre><code class="language-go">var age， name = 10， "Roger"

//or

age， name := 10， "Roger"
</code></pre>
<p>在程序中使用未声明的变量，程序会报错，而且无法通过编译。</p>
<p>在 VS Code 中你可以看到警告如下：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_15.45.31.png" alt="使用未声明变量的警告" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用未声明变量的警告</figcaption>
</figure>
<p>以下是编译的报错：</p>
<figure class="kg-card kg-card-image kg-card-hascaption">
    <img src="https://www.freecodecamp.org/news/content/images/2022/10/Screen_Shot_2022-07-28_at_15.45.44.png" alt="使用未声明变量的编译器报错" class="kg-image" width="600" height="400" loading="lazy">
    <figcaption>使用未声明变量的编译器报错</figcaption>
</figure>
<p>如果你声明了一个变量且没有给这个变量一个初始值，它会自动初始化一个对应类型的初始值，例如 integer 类型的值为 <code>0</code> 而字符串的值是一个空的字符串。</p>
<h2 id="dividbasictypesingogodiv"><div id="basic-types-in-go">Go 的基础类型</div></h2>
<p>Go 是一门强类型语言。</p>
<p>我们可以看到你怎么定义一个变量，指定它的类型：</p>
<pre><code class="language-go">var age int
</code></pre>
<p>或者你可以直接给变量赋予初始值，让 Go 来推断它的类型：</p>
<pre><code class="language-go">var age = 10
</code></pre>
<p>这些是 Go 中的基础类型：</p>
<ul>
<li>整型（<code>int</code>， <code>int8</code>， <code>int16</code>， <code>int32</code>， <code>rune</code>， <code>int64</code>， <code>uint</code>， <code>uintptr</code>， <code>uint8</code>， <code>uint16</code>， <code>uint64</code>）</li>
<li>浮点型（<code>float32</code>， <code>float64</code>），用于表示带小数点的数</li>
<li>复数类型（<code>complex64</code>， <code>complex128</code>），常用于科学计算中</li>
<li>字符型（<code>byte</code>），表示一个 ASCII 字符</li>
<li>字符串（<code>string</code>）， 一个<code>byte</code>的集合</li>
<li>布尔型（<code>bool</code>）表示 true 或 false</li>
</ul>
<p>我们有很多不同类型的整数类型，在大多数情况下你只会用到 <code>int</code>，并且你可能会选择一个更专业的方法进行优化（而不是在学习时需要考虑的事情）。</p>
<p>在你使用 64 位系统的时候 <code>int</code> 类型默认为 64 位， 使用 32 位系统的时候 <code>int</code> 类型默认为32位，其他的与此类似。</p>
<p><code>uint</code> 类型是无符号的 <code>int</code> 类型，如果你知道这个数字不是负数，你可以用这个类型存储比现在大两倍的数字。</p>
<p>所有的基础类型都是<strong>值类型</strong>， 这意味着当它们作为参数传递或从函数返回时，它们通过<strong>值传递</strong>给函数。</p>
<h2 id="dividstringsingogodiv"><div id="strings-in-go">Go 中的字符串</div></h2>
<p>Go 中的字符串是 <code>byte</code> 序列。</p>
<p>像我们所看到的一样，你可以定义字符串如下：</p>
<pre><code class="language-go">var name = "test"
</code></pre>
<p>其中很重要一点是不像其他语言，字符串定义只能使用双引号表示，而不是单引号。</p>
<p>获得字符串的长度，可以使用内置函数 <code>len()</code>：</p>
<pre><code class="language-go">len(name) //4
</code></pre>
<p>你可以用方括号访问单独字符，使用索引来取得你想要的字符：</p>
<pre><code class="language-go">name[0] //"t" (indexes start at 0)
name[1] //"e"
</code></pre>
<p>你可以使用切片获取到字符串：</p>
<pre><code class="language-go">name[0:2] //"te"
name[:2]  //"te"
name[2:]  //"st"
</code></pre>
<p>你可以创建一个字符串的副本：</p>
<pre><code class="language-go">var newstring = name[:]
</code></pre>
<p>你可以将字符串赋值给一个新的变量，如下：</p>
<pre><code class="language-go">var first = "test"
var second = first
</code></pre>
<p>字符串是 <strong>不可变</strong> 的， 所以你无法修改字符串的值。</p>
<p>如果你给 <code>first</code> 赋予一个新值，<code>second</code> 的值依然是 <code>"test"</code>：</p>
<pre><code class="language-go">var first = "test"
var second = first

first = "another test"

first  //"another test"
second //"test"
</code></pre>
<p>字符串是引用类型，意味着如果你将一个字符串传递给一个方法，字符串<strong>引用</strong>会被复制，而不是它的值，但是字符串是不可变的，所在在使用过程中它和 <code>int</code> 类型并没有很大的区别，例如。</p>
<p>你可以通过 <code>+</code> 运算符连接两个字符串：</p>
<pre><code class="language-go">var first = "first"
var second = "second"

var word = first + " " + second  //"first second"
</code></pre>
<p>Go提供了 <code>strings</code> 库来进行字符串的操作。</p>
<p>我们已经知道怎么在 “Hello, World!” 的案例中导入一个包。</p>
<p>这里你可以导入 <code>strings</code>：</p>
<pre><code class="language-go">package main

import (
    "strings"
)
</code></pre>
<p>你可以使用它了。</p>
<p>在例子中我们使用 <code>HasPrefix()</code> 函数来判断一个字符串的开头是否是以另一个子串开头的：</p>
<pre><code class="language-go">package main

import (
    "strings"
)

func main() {
    strings.HasPrefix("test"， "te") // true
}
</code></pre>
<p>你可以在这找到所有的函数列表：<a href="https://pkg.go.dev/strings">https://pkg.go.dev/strings</a>。</p>
<p>以下是你经常会用到的函数列表：</p>
<ul>
<li><code>strings.ToUpper()</code> 返回一个新的字符串， 大写</li>
<li><code>strings.ToLower()</code> 返回一个新的字符串， 小写</li>
<li><code>strings.HasSuffix()</code> 检查是否以某子串结尾</li>
<li><code>strings.HasPrefix()</code> 检查是否以某子串开头</li>
<li><code>strings.Contains()</code> 检查是否包含某字串</li>
<li><code>strings.Count()</code> 计算一个某子串在当前字符串出现的次数</li>
<li><code>strings.Join()</code> 创建一个新的字符串并连接多个字符串</li>
<li><code>strings.Split()</code> 创建一个数组来保存通过特殊字符串对字符串进行分割的结果，例如通常使用空格</li>
<li><code>strings.ReplaceAll()</code> 使用替换，可以使用一个新的字符串替换掉原字符中的字符串</li>
</ul>
<h2 id="dividarraysingogodiv"><div id="arrays-in-go">Go 中的数组</div></h2>
<p>数组是单个类型的序列。</p>
<p>我们可以这种方式定义数组：</p>
<pre><code class="language-go">var myArray [3]string //an array of 3 strings
</code></pre>
<p>或者你也可以给数组赋予初始值：</p>
<pre><code class="language-go">var myArray = [3]string{"First"， "Second"， "Third"}
</code></pre>
<p>在这个例子中，你可以让 Go 来帮你进行数组长度的推断：</p>
<pre><code class="language-go">var myArray = [...]string{"First"， "Second"， "Third"}
</code></pre>
<p>数组只能包含同一种类型的数据。</p>
<p>数组不能动态扩容——在 Go 中你必须声明数组的长度。这和类型一样是数组的一部分。当然，你不能使用一个没有声明长度的数组。</p>
<p>由于这个限制，数组在 Go 中很少使用，我们经常用到<strong>切片</strong>（稍后我们会讲到更多）。切片的底层是数组。所以我们需要知道它的工作原理。</p>
<p>你可以通过中括号获得数组中的每一个值正如我们之前获取字符串中单个字符一样：</p>
<pre><code class="language-go">myArray[0] //indexes start at 0
myArray[1]
</code></pre>
<p>你可以给数组中的成员设置新的值：</p>
<pre><code class="language-go">myArray[2] = "Another"
</code></pre>
<p>你可以通过 <code>len()</code> 函数来获取数组的长度：</p>
<pre><code class="language-go">len(myArray)
</code></pre>
<p>数组是 <strong>值类型</strong>。 这意味着复制一个数组：</p>
<pre><code class="language-go">anotherArray := myArray
</code></pre>
<p>传递一个数组给一个函数，或者一个函数返回数组，创建的都是原数组的副本。</p>
<p>这也是和其他编程语言的不同之处。</p>
<p>这里创建一个简单的示例，我们将一个新的值赋值给副本的一个元素，这个过程中不会修改原数组的元素：</p>
<pre><code class="language-go">var myArray = [3]string{"First"， "Second"， "Third"}
myArrayCopy := myArray
myArray[2] = "Another"

myArray[2]     //"Another"
myArrayCopy[2] //"Third"
</code></pre>
<p>记住你只能在数组中加入同一类型的数据，所以在例子中设置 <code>myArray[2] = 2</code> 会报错。</p>
<p>底层的元素存储在连续的内存当中。</p>
<h2 id="dividslicesingogodiv"><div id="slices-in-go">Go 中的切片</div></h2>
<p>切片是一种很像数组的数据结构，但它的大小是可以改变的。</p>
<p>底层，切片使用了数组且它是建立在数组之上的抽象概念使得本身变得更加灵活和方便使用（可以把数组想象为底层结构）。</p>
<p>你可以把切片作为高级语言中使用数组的一种使用方式。</p>
<p>你可以类似于数组定义切片，省略长度：</p>
<pre><code class="language-go">var mySlice []string //a slice of strings
</code></pre>
<p>你可以初始化切片的值：</p>
<pre><code class="language-go">var mySlice = []string{"First"， "Second"， "Third"}

//or

mySlice := []string{"First"， "Second"， "Third"}
</code></pre>
<p>你可以用 <code>make()</code> 函数创建一个有明确长度的空切片：</p>
<pre><code class="language-go">mySlice := make([]string， 3) //a slice of 3 empty strings
</code></pre>
<p>你可以用已经存在的切片创建一个新的切片，添加一个或多个元素进去：</p>
<pre><code class="language-go">mySlice := []string{"First"， "Second"， "Third"}

newSlice := append(mySlice， "Fourth"， "Fifth")
</code></pre>
<p>注意我们需要给 <code>append()</code> 的结果赋给一个新的切片，否则我们将得到一个编译器错误。原来的切片没有改变，我们将得到一个全新的切片。</p>
<p>你也可以使用 <code>copy()</code> 函数来创建一个重复的切片，这样它就不会共享另一个切片的内存，而是独立的：</p>
<pre><code class="language-go">mySlice := []string{"First"， "Second"， "Third"}

newSlice := make([]string， 3)

copy(newSlice， mySlice)
</code></pre>
<p>如果你复制的切片没有足够的空间（比原来的短），则只复制第一个元素（只到有空间为止）。</p>
<p>你可以从数组中初始化一个切片：</p>
<pre><code class="language-go">myArray := [3]string{"First"， "Second"， "Third"}

mySlice = myArray[:]
</code></pre>
<p>多个切片可以使用与底层数组相同的数组：</p>
<pre><code class="language-go">myArray := [3]string{"First"， "Second"， "Third"}

mySlice := myArray[:]
mySlice2 := myArray[:]

mySlice[0] = "test"

fmt.Println(mySlice2[0]) //"test"
</code></pre>
<p>这两个切片现在共享同一块内存。修改一个切片底层数组也会跟着改变，并导致从该数组生成的另一个切片也会被修改。</p>
<p>与数组一样，每一个切片中的元素同样存储在内存的连续内存之中。</p>
<p>如果你知道你必须用到切片，你可以在初始化的时候设置所需的容量。这种方式，在你需要更多空间的时候，这些空间已经准备好了（替代了选择和移动切片到从老的内存空间到新的内存的方式，并减少了垃圾回收）。</p>
<p>我们可以通过 <code>make()</code> 函数的第三个参数来指定<strong>容量</strong>：</p>
<pre><code class="language-go">newSlice := make([]string， 0， 10)
//an empty slice with capacity 10
</code></pre>
<p>例如字符串，你可以以下语法获得切片中的一部分：</p>
<pre><code class="language-go">mySlice := []string{"First"， "Second"， "Third"}

newSlice := mySlice[:2] //get the first 2 items
newSlice2 := mySlice[2:] //ignore the first 2 items
newSlice3 := mySlice[1:3] //new slice with items in position 1-2
</code></pre>
<h2 id="dividmapsingogomapdiv"><div id="maps-in-go">Go 中的 map</div></h2>
<p>map 是 Go 中一种常见的数据类型。</p>
<p>在其他语言被称为_字典_ 或 <em>哈希表</em> 或 <em>关联数组</em>。</p>
<p>下面是创建一个 map 的方式：</p>
<pre><code class="language-go">agesMap := make(map[string]int)
</code></pre>
<p>你不需要对 map 设置容纳多少项。</p>
<p>你可以通过这种方式添加新的元素到 map 中：</p>
<pre><code class="language-go">agesMap["flavio"] = 39
</code></pre>
<p>你可以用以下语法初始化 map 并赋值：</p>
<pre><code class="language-go">agesMap := map[string]int{"flavio": 39}
</code></pre>
<p>你可以通过键来获取对应的值：</p>
<pre><code class="language-go">age := agesMap["flavio"]
</code></pre>
<p>你可以通过 <code>delete()</code> 函数来删除 map 中的元素：</p>
<pre><code class="language-go">delete(agesMap， "flavio")
</code></pre>
<h2 id="dividloopsingogodiv"><div id="loops-in-go">Go 中的循环</div></h2>
<p>Go 中最好的特性是它只提供最少的选择。</p>
<p>我们只有一个循环关键字：<code>for</code>。</p>
<p>你可以像这样使用它：</p>
<pre><code class="language-go">for i := 0; i &lt; 10; i++ {
	fmt.Println(i)
}
</code></pre>
<p>我们首先初始化一个循环的变量， 我们设置一个条件用于检查我们的循环是否应该结束。最后我们设置 <em>post 语句</em>， 在每一次循环后执行， 这里例子中是增长 <code>i</code>。</p>
<p><code>i++</code> 增长 <code>i</code> 变量.</p>
<p><code>&lt;</code> <em>运算符</em> 用于比较 <code>i</code> 和 <code>10</code> 的值,会返回 <code>true</code> 或 <code>false</code>， 决定循环体是否执行。</p>
<p>我们不需要用圆括号来包围代码块，与 C 和 JavaScript 不太一样。</p>
<p>其他语言有各种不同的循环结构，但是 Go 中只有这一个，我们可以有像 <code>while</code> 一样的循环，如果你熟悉一门有它的语言，像这样：</p>
<pre><code class="language-go">i := 0

for i &lt; 10 {
	fmt.Println(i)
  i++
}
</code></pre>
<p>我们完全可以忽略条件，在我们想要中止可以使用 <code>break</code>：</p>
<pre><code class="language-go">i := 0

for {
	fmt.Println(i)

	if i &lt; 10 {
		break
	}

  i++
}
</code></pre>
<p>我在循环体中使用了一个 <code>if</code> 声明，但是在我们没有看到条件语句！我们下一步看。</p>
<p>我现在想要介绍一种东西 <code>range</code>。</p>
<p>我们可以使用以下语法通过 <code>for</code> 来迭代数组：</p>
<pre><code class="language-go">numbers := []int{1， 2， 3}

for i， num := range numbers {
	fmt.Printf("%d: %d\n"， i， num)
}

//0: 1
//1: 2
//2: 3
</code></pre>
<p>注意：我使用 <code>fmt.Printf()</code> 它允许我们使用占位符 <code>%d</code>，意思是整数，而 <code>\n</code> 意思是加入一个换行符。</p>
<p>在你不需要索引时，一般可以使用以下语法：</p>
<pre><code class="language-go">for _， num := range numbers {
  //...
}
</code></pre>
<p>我们可以使用 <code>_</code> 语法，它表示 “忽略 这个”，以避免Go编译器产生一个错误：“你没有使用 <code>i</code> 变量！”。</p>
<h2 id="dividconditionalsingogodiv"><div id="conditionals-in-go">Go 中的条件语句</div></h2>
<p>我们使用 <code>if</code> 声明一个条件从而执行不同的代码：</p>
<pre><code class="language-go">if age &lt; 18 {
	//underage
}
</code></pre>
<p><code>else</code> 部分是可选的：</p>
<pre><code class="language-go">if age &lt; 18 {
	//underage
} else {
  //adult
}
</code></pre>
<p>或者可以使用多个 <code>if</code>：</p>
<pre><code class="language-go">if age &lt; 12 {
	//child
} else if age &lt; 18  {
  //teen
} else {
	//adult
}
</code></pre>
<p>如果你在 <code>if</code> 中定义了任何的变量，那么只能在 <code>if</code> 中使用（<code>else</code> 中也一样且你需要在 <code>{}</code> 中写新的代码块）。</p>
<p>如果你有很多不同的 if 声明来检查同一个条件，使用 <code>switch</code> 是更好的选择：</p>
<pre><code class="language-go">switch age {
case 0: fmt.Println("Zero years old")
case 1: fmt.Println("One year old")
case 2: fmt.Println("Two years old")
case 3: fmt.Println("Three years old")
case 4: fmt.Println("Four years old")
default: fmt.Println(i + " years old")
}
</code></pre>
<p>与 C， JavaScript和其他语言相比，你不需要在每一个 <code>case</code> 中写 <code>break</code>。</p>
<h2 id="dividoperatorsingogodiv"><div id="operators-in-go">Go 中的运算符</div></h2>
<p>到目前为止，我们已经在代码示例中使用了一些运算符， 如 <code>=</code>、<code>:=</code> 和 <code>&lt;</code>。</p>
<p>让我们了解更多。</p>
<p>我们有赋值运算符 <code>=</code> 和 <code>:=</code> 我们用来定义和初始化变量：</p>
<pre><code class="language-go">var a = 1

b := 1
</code></pre>
<p>我们有比较运算符 <code>==</code> 和 <code>!=</code> 用来比较两个参数并会返回一个布尔值：</p>
<pre><code class="language-go">var num = 1
num == 1 //true
num != 1 //false
</code></pre>
<p>还有 <code>&lt;</code>， <code>&lt;=</code>， <code>&gt;</code>， <code>&gt;=</code>：</p>
<pre><code class="language-go">var num = 1
num &gt; 1 //false
num &gt;= 1 //true
num &lt; 1 //false
num &lt;= 1 //true
</code></pre>
<p>我们有双元（要求有两个参数）算术运算符， 像 <code>+</code>、<code>-</code>、<code>*</code>、<code>/</code>、<code>%</code>.</p>
<pre><code class="language-go">1 + 1 //2
1 - 1 //0
1 * 2 //2
2 / 2 //1
2 % 2 //0
</code></pre>
<p><code>+</code> 可以用来连接字符串：</p>
<pre><code class="language-go">"a" + "b" //"ab"
</code></pre>
<p>我们有单元运算符 <code>++</code> 和 <code>--</code> 用来对一个数字进行自增或自减：</p>
<pre><code class="language-go">var num = 1
num++ // num == 2
num-- // num == 1
</code></pre>
<p>注意：不像 C 和 JavaScript 像这样 <code>++num</code> 让一个数字预先操作。当然，运算符不会返回任何值。</p>
<p>我们有逻辑运算符来帮我们进行基本的 <code>true</code> 和 <code>false</code> 的判断，使用：<code>&amp;&amp;</code>、<code>||</code> 和 <code>!</code>：</p>
<pre><code class="language-go">true &amp;&amp; true  //true
true &amp;&amp; false //false
true || false //true
false || false //false
!true  //false
!false //true
</code></pre>
<p>这三个是最主要。</p>
<h2 id="dividstructsingogodiv"><div id="structs-in-go">Go 中的结构体</div></h2>
<p><strong>结构体</strong> 是一种 <em>类型</em>，它包括一个或多个变量。它像是一个变量的集合。我们将它们称为字段。 它们可以有不同的类型。</p>
<p>这是一个结构体定义的示例：</p>
<pre><code class="language-go">type Person struct {
	Name string
	Age int
}
</code></pre>
<p>注意这里我们使用大写的名字作为字段名，不然它们在包内将会是私有的。且当你想让结构体作为参数传递给另一个库的函数时，像我们使用 JSON 及数据库时，这些字段都将无法访问。</p>
<p>一旦我们定义了一个结构体，我们就可以用这个类型初始化一个变量：</p>
<pre><code class="language-go">flavio := Person{"Flavio"， 39}
</code></pre>
<p>且我们可以用以下语法修改字段的值：</p>
<pre><code class="language-go">flavio.Age //39
flavio.Name //"Flavio"
</code></pre>
<p>你也可以使用这种方式将一个结构体初始化赋给一个变量：</p>
<pre><code class="language-go">flavio := Person{Age: 39， Name: "Flavio"}
</code></pre>
<p>你也可以只初始化一个字段：</p>
<pre><code class="language-go">flavio := Person{Age: 39}
</code></pre>
<p>或者不初始化任何值：</p>
<pre><code class="language-go">flavio := Person{}

//or

var flavio Person
</code></pre>
<p>或者在之后设值：</p>
<pre><code class="language-go">flavio.Name = "Flavio"
flavio.Age = 39
</code></pre>
<p>结构体很常用，因为你可以对不相关的数据进行分组，并将其传递给函数或从函数传递给函数，保存在切片中，等等。</p>
<p>一旦定义，结构体就是像 <code>int</code> 或 <code>string</code> 这样的类型，这意味着你也可以在其他结构中使用它：</p>
<pre><code class="language-go">type FullName struct {
	FirstName string
	LastName string
}

type Person struct {
	Name FullName
	Age int
}
</code></pre>
<h2 id="dividfunctionsingogodiv"><div id="functions-in-go">Go 中的函数</div></h2>
<p>一个函数是一块代码，它被赋予了一个名字且包含了一些指令。</p>
<p>在 “Hello, World!” 示例中我们创建了一个 <code>main</code> 函数， 那是程序的入口。</p>
<pre><code class="language-go">package main

import "fmt"

func main() {
	fmt.Println("Hello, World!")
}
</code></pre>
<p>这是一个特殊的函数。</p>
<p>通常我们使用自定义的名字来定义函数：</p>
<pre><code class="language-go">func doSomething() {

}
</code></pre>
<p>你可以像这样调用它：</p>
<pre><code class="language-go">doSomething()
</code></pre>
<p>一个函数可以设置入参，我们需要给参数设置类型，如下：</p>
<pre><code class="language-go">func doSomething(a int， b int) {

}

doSomething(1， 2)
</code></pre>
<p><code>a</code> 和 <code>b</code> 是函数内部参数的名字。</p>
<p>一个函数可以返回一个值，像这样：</p>
<pre><code class="language-go">func sumTwoNumbers(a int， b int) int {
	return a + b
}

result := sumTwoNumbers(1， 2)
</code></pre>
<p>注意这里我们指定的返回的值的类型。</p>
<p>一个函数可以返回多个或一个值：</p>
<pre><code class="language-go">func performOperations(a int， b int) (int， int) {
	return a + b， a - b
}

sum， diff := performOperations(1， 2)
</code></pre>
<p>有趣的是很多语言只能返回一个值。</p>
<p>函数内部定义的任何变量都是函数的本地变量。</p>
<p>一个函数也可以不限制参数的个数，且这样的函数我们称它为 <em>可变函数</em>：</p>
<pre><code class="language-go">func sumNumbers(numbers ...int) int {
	sum := 0
	for _， number := range numbers {
		sum += number
	}
	return sum
}

total := sumNumbers(1， 2， 3， 4)
</code></pre>
<h2 id="dividpointersingogodiv"><div id="pointers-in-go">Go 中的指针</div></h2>
<p>Go 支持使用指针。</p>
<p>假设你有一个变量：</p>
<pre><code class="language-go">age := 20
</code></pre>
<p>使用 <code>&amp;age</code> 你获得这个变量的指针，它是内存地址。</p>
<p>当你拥有一个变量的指针时，你可以使用 <code>*</code> 运算符获取它的值：</p>
<pre><code class="language-go">age := 20
ageptr = &amp;age
agevalue = *ageptr
</code></pre>
<p>通常当你想要传递一个参数给你调用的函数时。Go 默认会在函数内部复制这个变量的值，所以这不会改变 <code>age</code> 的值：</p>
<pre><code class="language-go">func increment(a int) {
	a = a + 1
}

func main() {
	age := 20
	increment(age)

	//age is still 20
}
</code></pre>
<p>你可以像这样使用指针：</p>
<pre><code class="language-go">func increment(a *int) {
	*a = *a + 1
}

func main() {
	age := 20
	increment(&amp;age)

	//age is now 21
}
</code></pre>
<h2 id="dividmethodsingogodiv"><div id="methods-in-go">Go 中的方法</div></h2>
<p>你可以给一个结构赋值一个函数，在这种情况下我们称它为“方法”。</p>
<p>示例：</p>
<pre><code class="language-go">type Person struct {
	Name string
	Age int
}

func (p Person) Speak() {
	fmt.Println("Hello from " + p.Name)
}

func main() {
	flavio := Person{Age: 39， Name: "Flavio"}
	flavio.Speak()
}
</code></pre>
<p>你可以定义一个方法为指针接收或值接收。</p>
<p>上述例子中我们展示的是值接收，这个接收器会复制结构体的结构。</p>
<p>这里将会有一个指针接收，接收结构实例的指针：</p>
<pre><code class="language-go">func (p *Person) Speak() {
	fmt.Println("Hello from " + p.Name)
}
</code></pre>
<h2 id="dividinterfacesingogodiv"><div id="interfaces-in-go">Go 中的接口</div></h2>
<p>接口是一个类型，可以定义一个或多个方法声明。</p>
<p>方法没有被实现，只有它们的签名：名字、参数类型和返回值类型。</p>
<p>像这样：</p>
<pre><code class="language-go">type Speaker interface {
	Speak()
}
</code></pre>
<p>现在你有一个函数，可以接纳任何类型来实现接口定义所有方法：</p>
<pre><code class="language-go">func SaySomething(s Speaker) {
	s.Speak()
}
</code></pre>
<p>我们可以让任何一个结构体来实现这些方法：</p>
<pre><code class="language-go">type Speaker interface {
	Speak()
}

type Person struct {
	Name string
	Age int
}

func (p Person) Speak() {
	fmt.Println("Hello from " + p.Name)
}

func SaySomething(s Speaker) {
	s.Speak()
}

func main() {
	flavio := Person{Age: 39， Name: "Flavio"}
	SaySomething(flavio)
}
</code></pre>
<h2 id="dividwheretogofromherediv"><div id="where-to-go-from-here">更多内容</div></h2>
<p>这份手册初步介绍了 Go 编程语言。</p>
<p>基于这里的基础，你现在可以学更多东西：垃圾回收、错误处理、并发和网络、文件系统接口，等等。</p>
<p>海阔凭鱼跃，天高任鸟飞。</p>
<p>我的建议是选择一个你想要创建的程序，然后开始学习你需要的东西。</p>
<p>它是有趣且值得做的。</p>
<p>注意：<a href="https://thevalleyofcode.com/download/go/">你可以点击链接获得 GO 初学者手册的 pdf 版本和 ePub 版本</a>。</p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ 从 0 到 1 学习 Golang ]]>
                </title>
                <description>
                    <![CDATA[ 原文：Learning Go — from zero to hero [https://www.freecodecamp.org/news/learning-go-from-zero-to-hero-d2a3223b3d86/] ，作者：Milap Neupane [https://www.freecodecamp.org/news/author/milapneupane/] 让我们先对 Go（或称 Golang ）做一个小小的介绍。Go 是由谷歌工程师 Robert Griesemer、Rob Pike 和 Ken Thompson 设计的。它是一种静态类型的、编译的语言。第一个版本于 2012 年 3 月作为开源版本发布。 > “Go 是一种开源的编程语言，它使人们能够轻松地构建简单、可靠和高效的软件”。- GoLang 在许多编程语言中，有许多方法来解决一个特定的问题。程序员要花很多时间去思考解决它的最佳方法。 Go 却相信用较少的功能——只有一种正确的方式来解决问题。 这为开发人员节省了时间，并使大型代码库易于维护。 Go 中没有像 maps  和 filters  ]]>
                </description>
                <link>https://www.freecodecamp.org/chinese/news/learning-go-from-zero-to-hero/</link>
                <guid isPermaLink="false">625e8a8099ec7406219e6cfa</guid>
                
                    <category>
                        <![CDATA[ Golang ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Go ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ luojiyin ]]>
                </dc:creator>
                <pubDate>Tue, 19 Apr 2022 03:20:00 +0000</pubDate>
                <media:content url="https://chinese.freecodecamp.org/news/content/images/2022/04/1_30aoNxlSnaYrLhBT0O1lzw.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>原文：<a href="https://www.freecodecamp.org/news/learning-go-from-zero-to-hero-d2a3223b3d86/">Learning Go — from zero to hero</a>，作者：<a href="https://www.freecodecamp.org/news/author/milapneupane/">Milap Neupane</a></p><!--kg-card-begin: markdown--><p>让我们先对 Go（或称 Golang ）做一个小小的介绍。Go 是由谷歌工程师 Robert Griesemer、Rob Pike 和 Ken Thompson 设计的。它是一种静态类型的、编译的语言。第一个版本于 2012 年 3 月作为开源版本发布。</p>
<blockquote>
<p>“Go 是一种开源的编程语言，它使人们能够轻松地构建简单、可靠和高效的软件”。- GoLang</p>
</blockquote>
<p>在许多编程语言中，有许多方法来解决一个特定的问题。程序员要花很多时间去思考解决它的最佳方法。</p>
<p>Go 却相信用较少的功能——只有一种正确的方式来解决问题。</p>
<p>这为开发人员节省了时间，并使大型代码库易于维护。 Go 中没有像 <code>maps</code> 和 <code>filters</code> 这样的 “表达性”功能。</p>
<blockquote>
<p>“当你有增加表现力的功能时，通常会增加系统开销”—— Rob Pike</p>
</blockquote>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*AUiSG5Gqz8MzaGCvGpckGA.png" alt="1*AUiSG5Gqz8MzaGCvGpckGA" width="600" height="400" loading="lazy"></p>
<p>最近发表的新的 golang 标志：<a href="https://blog.golang.org/go-brand">https://blog.golang.org/go-brand</a></p>
<h3 id="">入门</h3>
<p>Go 是由 packages（包）组成的。package main 告诉 Go 编译器，该程序被编译为可执行文件，而不是共享库。它是一个应用程序的入口点。package main 的定义如下：</p>
<pre><code class="language-go">package main
</code></pre>
<p>让我们继续前进，在 Go workspace 创建一个 <code>main.go</code> 文件，编写一个简单的 hello world 例子。</p>
<h4 id="workspace"><strong>Workspace</strong></h4>
<p>Go 中的 workspace 是由环境变量 <code>GOPATH</code> 定义的。</p>
<p>你写的任何代码都要写在 workspace 里面。Go 将搜索 <code>GOPATH</code> 目录内的任何软件包，或者 <code>GOROOT</code> 目录，该目录在安装 Go 时默认设置。<code>GOROOT</code> 是安装 Go 的路径。</p>
<p>设置 <code>GOPATH</code> 到你想要的目录。现在，让我们把它添加到 <code>~/workspace</code> 文件夹内。</p>
<pre><code class="language-shell"># export env
export GOPATH=~/workspace
# go inside the workspace directory
cd ~/workspace
</code></pre>
<p>在我们刚刚创建的 workspace 文件夹中创建 <code>main.go</code> 文件，其中包含以下代码。</p>
<h4 id="helloworld">Hello World</h4>
<pre><code class="language-go">package main

import (
 "fmt"
)

func main(){
  fmt.Println("Hello World!")
}
</code></pre>
<p>在上面的例子中，<code>fmt</code>是 Go 中的一个内置包，它实现了用于格式化 I/O 输出的函数。</p>
<p>我们通过使用 <code>import</code> 关键字在 Go 中导入一个包。<code>func main</code> 是代码被执行的主入口点。<code>Println</code> 是包 <code>fmt</code> 中的一个函数，它为我们打印出 “hello world”。</p>
<p>让我们通过运行这个文件来看看。我们有两种方法可以运行 Go 命令。正如我们所知，Go 是一种编译语言，所以我们首先需要在执行之前编译它。</p>
<pre><code class="language-shell">&gt; go build main.go
</code></pre>
<p>这将创建一个二进制可执行文件<code>main</code>，现在我们可以运行：</p>
<pre><code class="language-shell">&gt; ./main 
# Hello World!
</code></pre>
<p>还有一种更简单的方法来运行程序。<code>go run</code> 命令会编译源代码，并直接执行源码中的 main() 函数，不会在当前目录留下可执行文件。你可以简单地运行以下命令来执行该程序。</p>
<pre><code class="language-shell">go run main.go
# Hello World!
</code></pre>
<p><strong><em>注意</em></strong>：<em>要尝试本博客中提到的代码，你可以使用 <a href="https://play.golang.org/">https://play.golang.org</a></em></p>
<h3 id="">变量</h3>
<p>Go 中的变量是明确声明的。Go 是一种静态类型的语言。这意味着在声明变量的时候会检查变量的类型。一个变量可以被声明：</p>
<pre><code class="language-go">var a int
</code></pre>
<p>在这种情况下，值将被设置为 0。使用下面的语法来声明和初始化一个具有不同值的变量：</p>
<pre><code class="language-go">var a = 1
</code></pre>
<p>这里的变量被自动分配为 int。我们可以对变量的声明使用一个简短定义，即：</p>
<pre><code class="language-go">message := "hello world"
</code></pre>
<p>我们也可以在同一行中声明多个变量：</p>
<pre><code class="language-go">var b, c int = 2, 3
</code></pre>
<h3 id="">数据类型</h3>
<p>像其他编程语言一样，Go 支持各种不同的数据结构。让我们来探索其中：</p>
<h4 id="">整型、字符串和布尔值</h4>
<p>支持的整型包括 int, int8, int16, int32, int64,<br>
uint, uint8, uint16, uint32, uint64, uintptr(无符号整型，长度跟平台相关，它的长度可以用来保存一个指针地址) 等</p>
<p>字符串类型存储一个字节序列。它用关键字 <code>string</code> 来表示和声明。</p>
<p>布尔值使用关键字 <code>bool</code> 来存储。</p>
<p>Go 也支持复数类型，可以用 <code>complex64</code> 和 <code>complex128</code> 来声明。</p>
<pre><code class="language-go">var a bool = true
var b int = 1
var c string = 'hello world'
var d float32 = 1.222
var x complex128 = cmplx.Sqrt(-5 + 12i)
</code></pre>
<h4 id="maps">数组、切片和 Maps</h4>
<p>数组是由相同数据类型的元素组成的一个序列。数组在声明时有一个固定的长度，所以它不能被扩大到超过这个长度。一个数组声明：</p>
<pre><code class="language-go">var a [5]int
</code></pre>
<p>数组也可以是多维的。我们可以简单地用以下方式创建它们：</p>
<pre><code class="language-go">var multiD [2][3]int
</code></pre>
<p>数组会限制数组的值发生变化，当代码运行时。数组也没有提供获取子数组的能力。 为此，Go有一种数据类型，叫做切片（slices）。</p>
<p>切片存储了一连串的元素，并且可以在任何时候扩展。切片声明与数组声明类似——但没有定义容量：</p>
<pre><code class="language-go">var b []int
</code></pre>
<p>这将创建一个容量为 0、长度为 0 的切片。</p>
<p>也可以用容量和长度来定义切片。我们可以用下面的语法来定义它：</p>
<pre><code class="language-go">numbers := make([]int,5,10)
</code></pre>
<p>这里，切片的初始长度为 5，容量为 10。</p>
<p>分片是对数组的一种抽象。切片使用一个数组作为底层结构。一个片断包含三个部分：容量、长度和一个指向底层数组的指针，如下图所示：</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*P0lNCO0sQwIYHLEX_mfSOQ.png" alt="1*P0lNCO0sQwIYHLEX_mfSOQ" width="600" height="400" loading="lazy"></p>
<p>图片源自: <a href="https://blog.golang.org/go-slices-usage-and-internals">https://blog.golang.org/go-slices-usage-and-internals</a></p>
<p>一个切片的容量可以通过使用 append 或 copy 函数来增加。append 函数将值添加到数组的末端，如果需要的话也可以增加容量。</p>
<pre><code class="language-go">numbers = append(numbers, 1, 2, 3, 4)
</code></pre>
<p>另一种增加切片容量的方法是使用 copy 函数。简单地创建另一个容量更大的片断，并将原来的切片复制到新创建的切片上：</p>
<pre><code class="language-go">// create a new slice
number2 := make([]int, 15)
// copy the original slice to new slice
copy(number2, number)
</code></pre>
<p>我们可以创建一个切片的子切片。这可以通过以下命令简单地完成：</p>
<pre><code class="language-go">// initialize a slice with 4 len and values
number2 = []int{1,2,3,4}
fmt.Println(numbers) // -&gt; [1 2 3 4]
// create sub slices
slice1 := number2[2:]
fmt.Println(slice1) // -&gt; [3 4]
slice2 := number2[:3]
fmt.Println(slice2) // -&gt; [1 2 3]
slice3 := number2[1:4]
fmt.Println(slice3) // -&gt; [2 3 4]
</code></pre>
<p>Maps 是 Go 中的一种数据类型，它将键映射到值。我们可以使用以下命令来定义一个 map：</p>
<pre><code class="language-go">var m map[string]int
</code></pre>
<p><code>m</code> 是新的 map 变量，它的键是 <code>string</code> 类型， 值是 <code>integers</code> 类型。我们很容易在 map 上添加键值对：</p>
<pre><code class="language-go">// adding key/value
m['clearity'] = 2
m['simplicity'] = 3
// printing the values
fmt.Println(m['clearity']) // -&gt; 2
fmt.Println(m['simplicity']) // -&gt; 3
</code></pre>
<h3 id="">类型转换</h3>
<p>一种类型的数据类型可以通过类型转换转换为另一种类型。让我们看看一个简单的类型转换：</p>
<pre><code class="language-go">a := 1.1
b := int(a)
fmt.Println(b)
//-&gt; 1
</code></pre>
<p>不是所有类型的数据类型都可以转换为另一种类型。请确保数据类型与转换的内容相匹配。</p>
<h3 id="">条件语句</h3>
<h4 id="ifelse">if else</h4>
<p>对于条件性语句，我们可以使用 if-else 语句，如下例所示。请确保大括号与条件语句在同一行。</p>
<pre><code class="language-go">if num := 9; num &lt; 0 {
 fmt.Println(num, "is negative")
} else if num &lt; 10 {
 fmt.Println(num, "has 1 digit")
} else {
 fmt.Println(num, "has multiple digits")
}
</code></pre>
<h4 id="switchcase">switch case</h4>
<p>Switch cases 有助于组织多个条件语句。下面的例子显示了一个简单的 siwtch 语句：</p>
<pre><code class="language-go">i := 2
switch i {
case 1:
 fmt.Println("one")
case 2:
 fmt.Println("two")
default:
 fmt.Println("none")
}
</code></pre>
<h3 id="">循环</h3>
<p>Go 有一个循环的关键词 <code>for</code>。<code>for</code> 循环命令用于实现不同种类的循环：</p>
<pre><code class="language-go">i := 0
sum := 0
for i &lt; 10 {
 sum += 1
  i++
}
fmt.Println(sum)
</code></pre>
<p>上面的例子类似于 C 语言中的 while 循环。</p>
<p>Go 中的 for 语句也可以用于普通的 for 循环：</p>
<pre><code class="language-go">sum := 0
for i := 0; i &lt; 10; i++ {
  sum += i
}
fmt.Println(sum)
</code></pre>
<p>Go 中的死循环：</p>
<pre><code class="language-go">for {
}
</code></pre>
<h3 id="pointers">Pointers （指针）</h3>
<p>Go提供了指针。指针是用来保存一个值的地址的地方。指针是由 * 定义的。指针是根据数据的类型来定义的，例如：</p>
<pre><code class="language-go">var ap *int
</code></pre>
<p><code>ap</code> 是指向一个整数类型的指针。<code>&amp;</code> 操作符可以用来获取一个变量的地址。</p>
<pre><code class="language-go">a := 12
ap = &amp;a
</code></pre>
<p>指针所指向的值可以使用 <code>*</code> 操作符来访问：</p>
<pre><code class="language-go">fmt.Println(*ap)
// =&gt; 12
</code></pre>
<p>在传递结构体作为参数时，或者在为定义的类型声明方法时，通常倾向于使用指针。</p>
<ol>
<li>传递值时，实际上是在复制值，这意味着更多的内存。</li>
<li>通过指针，函数改变的值会反映在 方法/函数 调用者身上</li>
</ol>
<p>例如：</p>
<pre><code class="language-go">func increment(i *int) {
  *i++
}
func main() {
  i := 10
  increment(&amp;i)
  fmt.Println(i)
}
//=&gt; 11
</code></pre>
<p>注意：当你在尝试博客中的示例代码时，不要忘记用 <code>package main</code> 包含它，并在需要时导入 fmt 或其他包，如上面第一个 main.go 例子中所示。</p>
<h3 id="functions">Functions （函数）</h3>
<p>在 main package 中定义的 main 函数是 go 程序执行的入口。更多的函数可以被定义和使用。让我们来看看一个简单的例子：</p>
<pre><code class="language-go">func add(a int, b int) int {
  c := a + b
  return c
}
func main() {
  fmt.Println(add(2, 1))
}
//=&gt; 3
</code></pre>
<p>在上面的例子中我们可以看到，Go 函数是用 <strong>func</strong> 关键字来定义的，后面是函数名称。一个函数的 <strong>参数</strong> 需要根据其数据类型来定义，最后是返回的数据类型。</p>
<p>一个函数的返回值也可以在函数中预先定义：</p>
<pre><code class="language-go">func add(a int, b int) (c int) {
  c = a + b
  return
}
func main() {
  fmt.Println(add(2, 1))
}
//=&gt; 3
</code></pre>
<p>这里c被定义为返回变量。所以定义的变量c会自动返回，而不需要在最后的返回语句中定义。</p>
<p>你也可以从一个函数中返回多个返回值，用逗号来分隔返回值。</p>
<pre><code class="language-go">func add(a int, b int) (int, string) {
  c := a + b
  return c, "successfully added"
}
func main() {
  sum, message := add(2, 1)
  fmt.Println(message)
  fmt.Println(sum)
}
</code></pre>
<h3 id="">方法、结构体、接口</h3>
<p>Go并不是一种完全面向对象的语言，但通过结构体（Struct）、接口（Interface）和方法（Method），它有很多面向对象的支持和感觉。</p>
<h4 id="struct">结构体（Struct）</h4>
<p>结构体是一种类型化的、不同字段的集合。结构体用于将数据分组。例如，如果我们想对 Person 类型的数据进行分组，我们可以定义一个人的属性，其中可能包括姓名、年龄、性别。可以使用以下语法来定义一个结构体：</p>
<pre><code class="language-go">type person struct {
  name string
  age int
  gender string
}
</code></pre>
<p>在定义了一个人的类型结构后，现在让我们来创建一个 person：</p>
<pre><code class="language-go">//way 1: specifying attribute and value
p = person{name: "Bob", age: 42, gender: "Male"}
//way 2: specifying only value
person{"Bob", 42, "Male"}
</code></pre>
<p>我们可以很容易地用一个点（.）来访问这些数据。</p>
<pre><code class="language-go">p.name
//=&gt; Bob
p.age
//=&gt; 42
p.gender
//=&gt; Male
</code></pre>
<p>你也可以用结构的指针直接访问其属性：</p>
<pre><code class="language-go">pp = &amp;person{name: "Bob", age: 42, gender: "Male"}
pp.name
//=&gt; Bob
</code></pre>
<h4 id="methods">方法（Methods）</h4>
<p>方法（Method）是一种特殊的函数类型，它有一个 <em>receiver</em> 。<em>receiver</em> 可以是一个值或一个指针。让我们创建一个名为 describe 的方法（Method），它有一个我们在上面的例子中创建的接收器类型的 person：</p>
<pre><code class="language-go">package main
import "fmt"

// struct defination
type person struct {
  name   string
  age    int
  gender string
}

// method defination
func (p *person) describe() {
  fmt.Printf("%v is %v years old.", p.name, p.age)
}
func (p *person) setAge(age int) {
  p.age = age
}

func (p person) setName(name string) {
  p.name = name
}

func main() {
  pp := &amp;person{name: "Bob", age: 42, gender: "Male"}
  pp.describe()
  // =&gt; Bob is 42 years old
  pp.setAge(45)
  fmt.Println(pp.age)
  //=&gt; 45
  pp.setName("Hari")
  fmt.Println(pp.name)
  //=&gt; Bob
}
</code></pre>
<p>正如我们在上面的例子中看到的，现在可以使用点运算符来调用该方法，如 <code>pp.describe</code>。请注意，<em>receiver</em> 是一个指针。使用指针，我们传递的是一个值的引用，所以如果我们在方法中做任何改变，都会反映在  <em>receiver</em> pp中。它也不会创建一个新的对象的副本，这就节省了内存。</p>
<p>请注意，在上面的例子中，年龄的值被改变了，而名字的值没有改变，因为setName方法是 <em>receiver</em> 类型的，而 setAge 是指针类型的。</p>
<h4 id="interfaces">接口（Interfaces）</h4>
<p>Go 接口（interfaces）是一个方法（methods）的集合。接口有助于将一个类型的属性组合在一起。让我们以一个接口 animal 为例：</p>
<pre><code class="language-go">type animal interface {
  description() string
}
</code></pre>
<p>animal 是一个接口（interface）类型。现在让我们创建两个不同类型的 animal，它们都实现了 animal 接口类型：</p>
<pre><code class="language-go">package main

import (
  "fmt"
)

type animal interface {
  description() string
}

type cat struct {
  Type  string
  Sound string
}

type snake struct {
  Type      string
  Poisonous bool
}

func (s snake) description() string {
  return fmt.Sprintf("Poisonous: %v", s.Poisonous)
}

func (c cat) description() string {
  return fmt.Sprintf("Sound: %v", c.Sound)
}

func main() {
  var a animal
  a = snake{Poisonous: true}
  fmt.Println(a.description())
  a = cat{Sound: "Meow!!!"}
  fmt.Println(a.description())
}

//=&gt; Poisonous: true
//=&gt; Sound: Meow!!!
</code></pre>
<p>type cat struct {<br>
在主函数中，我们创建一个动物类型的变量 <code>a</code>。我们给动物分配一个 snake 和一个 cat 的类型，并使用 Println 来打印 a.description。由于我们在两种类型（cat 和 snake）中都以不同的方式实现了 describe 方法，我们得到了打印的动物描述。</p>
<h3 id="">包</h3>
<p>我们把Go的所有代码都写在一个包里。<strong>main</strong> package 是程序执行的入口点。Go 中有很多内置包。我们一直在使用的最著名的是<strong>fmt</strong>包。</p>
<blockquote>
<p>“Go 软件包是 Go 提供的大型编程的主要机制，它们使得将一个大型项目分割成小块成为可能。”<br>
— Robert Griesemer</p>
</blockquote>
<h4 id="installingapackage">Installing a package (安装一个包)</h4>
<pre><code class="language-shell">go get &lt;package-url-github&gt;
// example
go get github.com/satori/go.uuid
</code></pre>
<p>我们安装的软件包被保存在 GOPATH 环境变量设置的工作目录。你可以通过进入我们工作目录下的 pkg 文件夹 <code>cd $GOPATH/pkg</code> 来查看这些软件包。</p>
<h4 id="">创建自定义包</h4>
<p>让我们先创建一个文件夹 custom_package：</p>
<pre><code class="language-shell">&gt; mkdir custom_package
&gt; cd custom_package
</code></pre>
<p>要创建一个自定义包，我们需要首先创建一个文件夹，并加上我们需要的包名。比方说，我们要建立一个 <code>person</code> 包。为此，让我们在 <code>custom_package</code> 文件夹中创建一个名为 <code>person</code> 的文件夹：</p>
<pre><code class="language-shell">&gt; mkdir person
&gt; cd person
</code></pre>
<p>现在让我们在这个文件夹中创建一个文件 person.go。</p>
<pre><code class="language-go">package person
func Description(name string) string {
  return "The person name is: " + name
}
func secretName(name string) string {
  return "Do not share"
}
</code></pre>
<p>我们现在需要安装这个包，以便它可以被导入和使用。因此，让我们来安装它：</p>
<pre><code class="language-shell">&gt; go install
</code></pre>
<p>现在让我们回到custom_package文件夹，创建一个 main.go 文件</p>
<pre><code class="language-go">package main
import(
  "custom_package/person"
  "fmt"
)
func main(){ 
  p := person.Description("Milap")
  fmt.Println(p)
}
// =&gt; The person name is: Milap
</code></pre>
<p>在这里，我们现在可以导入我们创建的包 <code>person</code> 并使用函数 Description。注意，我们在包中创建的函数 <code>secretName</code> 将不能被访问。在 Go 中，没有大写字母开头的方法名称将是私有的。</p>
<h4 id="">包文档</h4>
<p>Go内置了对包的文档支持。运行以下命令来生成文档：</p>
<pre><code class="language-shell">godoc person Description
</code></pre>
<p>这将为我们的包 person 里面的描述函数生成文档。要看到这些文档，请使用以下命令运行一个网络服务器：</p>
<pre><code class="language-shell">godoc -http=":8080"
</code></pre>
<p>现在去URL <a href="http://localhost:6060/pkg/">http://localhost:8080/pkg/</a>，看看我们刚刚创建的包的文档。</p>
<h4 id="go">Go 内置包</h4>
<p><strong>fmt</strong></p>
<p>该包实现了格式化的 I/O 函数。我们已经用这个包实现了向 stdout 打印的功能。</p>
<p><strong>json</strong></p>
<p>Go中另一个有用的包是json包。这有助于对JSON进行编码/解码。让我们举个例子，对一些 JSON 进行编码/解码：</p>
<p>编码</p>
<pre><code class="language-go">package main

import (
  "fmt"
  "encoding/json"
)

func main(){
  mapA := map[string]int{"apple": 5, "lettuce": 7}
  mapB, _ := json.Marshal(mapA)
  fmt.Println(string(mapB))
}
</code></pre>
<p>解码</p>
<pre><code class="language-go">package main

import (
  "fmt"
  "encoding/json"
)

type response struct {
  PageNumber int `json:"page"`
  Fruits []string `json:"fruits"`
}

func main(){
  str := `{"page": 1, "fruits": ["apple", "peach"]}`
  res := response{}
  json.Unmarshal([]byte(str), &amp;res)
  fmt.Println(res.PageNumber)
}
//=&gt; 1
</code></pre>
<p>当使用 unmarshal 解码 json 字节时，第一个参数是 json 字节，第二个参数是我们希望 json 被映射到的响应类型结构的地址。注意，<code>json: "page"</code>将页面键映射到结构中的 PageNumber 键。</p>
<h3 id="">错误处理</h3>
<p>错误是指程序中不想要的和意外的结果。比方说，我们正在对一个外部服务进行 API 调用。这个 API 调用可能是成功的，也可能是失败的。当错误类型出现时，Go 程序中的错误可以被识别。让我们看看这个例子：</p>
<pre><code class="language-go">resp, err := http.Get("http://example.com/")
</code></pre>
<p>在这里，对错误对象的 API 调用可能通过也可能失败。我们可以检查错误是否为零或存在，并相应地处理响应：</p>
<pre><code class="language-go">package main

import (
  "fmt"
  "net/http"
)

func main(){
  resp, err := http.Get("http://example.com/")
  if err != nil {
    fmt.Println(err)
    return
  }
  fmt.Println(resp)
}
</code></pre>
<h4 id="">从函数返回自定义错误</h4>
<p>当我们在编写自己的函数时，有些情况下会出现错误。这些错误可以在错误对象的帮助下返回：</p>
<pre><code class="language-go">func Increment(n int) (int, error) {
  if n &lt; 0 {
    // return error object
    return nil, errors.New("math: cannot process negative number")
  }
  return (n + 1), nil
}
func main() {
  num := 5
 
  if inc, err := Increment(num); err != nil {
    fmt.Printf("Failed Number: %v, error message: %v", num, err)
  }else {
    fmt.Printf("Incremented Number: %v", inc)
  }
}
</code></pre>
<p>大多数 Go 中内置的包，或者我们使用的外部包，都有一个错误处理的机制。所以我们调用的任何函数都有可能出现错误。这些错误绝不应该被忽视，总是在我们调用这些函数的地方优雅地处理，正如我们在上面的例子中所做的那样。</p>
<h4 id="panic">Panic</h4>
<p>Panic 是指在程序执行过程中突然遇到的未被处理的东西。在Go中，Panic 不是处理程序中异常的理想方式。建议使用一个错误对象来代替。当 Panic 发生时，程序的执行会被停止。Panic 发生后被执行的东西是 defer。</p>
<pre><code class="language-go">//Go
package main

import "fmt"

func main() {
    f()
    fmt.Println("Returned normally from f.")
}

func f() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in f", r)
        }
    }()
    fmt.Println("Calling g.")
    g(0)
    fmt.Println("Returned normally from g.")
}

func g(i int) {
    if i &gt; 3 {
        fmt.Println("Panicking!")
        panic(fmt.Sprintf("%v", i))
    }
    defer fmt.Println("Defer in g", i)
    fmt.Println("Printing in g", i)
    g(i + 1)
}
</code></pre>
<h4 id="defer">Defer</h4>
<p>Defer 是指总是在函数的末尾被执行的东西。</p>
<p>在上面的例子中，我们用 panic() 使程序的执行陷入 panic。正如你所注意到的，这里有一个 defer 语句，它将使程序在最后执行这一行。当我们需要在函数结束时执行一些东西时也可以使用 defer，例如关闭一个文件。</p>
<h3 id="">并发</h3>
<p>Go 是在考虑到并发性的情况下建立的。Go 中的并发性可以通过 Go 协程实现，它是轻量级的线程。</p>
<p><strong>Go 协程</strong></p>
<p>Go 协程是可以与另一个函数并行或同时运行的函数。创建一个 Go 协程非常简单。只需在一个函数前面加上关键字 Go，我们就可以让它并行执行。Go 协程是非常轻量级的，所以我们可以创建成千上万的协程。让我们来看看一个简单的例子：</p>
<pre><code class="language-go">package main
import (
  "fmt"
  "time"
)
func main() {
  go c()
  fmt.Println("I am main")
  time.Sleep(time.Second * 2)
}
func c() {
  time.Sleep(time.Second * 2)
  fmt.Println("I am concurrent")
}
//=&gt; I am main
//=&gt; I am concurrent
</code></pre>
<p>正如你在上面的例子中所看到的，函数 c 是一个 Go 协程，与 Go 主线程并行执行。有些时候，我们希望在多个线程之间共享资源。Go 倾向于不将一个线程的变量与另一个线程共享，因为这样会增加死锁和资源等待的可能性。还有一种方法可以在 Go 协程之间共享资源：通过 Go channels。</p>
<p><strong>通道</strong></p>
<p>我们可以使用通道在两个 Go 协程之间传递数据。在创建 channel 时，有必要指定该 channel 接收什么样的数据。让我们创建一个简单的字符串类型的 channel，如下所示：</p>
<pre><code class="language-go">c := make(chan string)
</code></pre>
<p>通过这个 channel，我们可以发送字符串类型的数据。我们可以在这个 channel 中发送和接收数据:</p>
<pre><code class="language-go">package main

import "fmt"

func main(){
  c := make(chan string)
  go func(){ c &lt;- "hello" }()
  msg := &lt;-c
  fmt.Println(msg)
}
//=&gt;"hello"
</code></pre>
<p>接收方 channel 等待，直到发送方发送数据到 channel。</p>
<p><strong>单向通道</strong></p>
<p>有些情况下，我们希望 Go 程序通过 channel 接收数据，但不发送数据，反之亦然。为此，我们也可以创建一个<strong>单向 channel</strong>。让我们来看看一个简单的例子：</p>
<pre><code class="language-go">package main

import (
 "fmt"
)

func main() {
 ch := make(chan string)
 
 go sc(ch)
 fmt.Println(&lt;-ch)
}

func sc(ch chan&lt;- string) {
 ch &lt;- "hello"
}
</code></pre>
<p>在上面的例子中，<code>sc</code> 是一个 Go 协程，它只能向通道发送消息，但不能接收消息。</p>
<h3 id="selectgo">使用 select 为 Go 例程组织多个通道</h3>
<p>一个函数可能有多个 channel 在等待。为此，我们可以使用一个选择（select）语句。让我们看一个例子，以了解更清楚的情况：</p>
<pre><code class="language-go">package main

import (
 "fmt"
 "time"
)

func main() {
 c1 := make(chan string)
 c2 := make(chan string)
 go speed1(c1)
 go speed2(c2)
 fmt.Println("The first to arrive is:")
 select {
 case s1 := &lt;-c1:
  fmt.Println(s1)
 case s2 := &lt;-c2:
  fmt.Println(s2)
 }
}

func speed1(ch chan string) {
 time.Sleep(2 * time.Second)
 ch &lt;- "speed 1"
}

func speed2(ch chan string) {
 time.Sleep(1 * time.Second)
 ch &lt;- "speed 2"
}
</code></pre>
<p>在上面的例子中，main正在等待两个 channel，c1 和 c2。通过 select case 语句，main 函数打印出，信息从它先收到的 channel 中发送出来。</p>
<p><strong>带缓冲的通道</strong></p>
<p>你可以在go中创建一个缓冲 channel。有了缓冲 channel，如果缓冲区满了，发送到该 channel 的消息就会被阻断。让我们看一下这个例子：</p>
<pre><code class="language-go">package main

import "fmt"

func main(){
  ch := make(chan string, 2)
  ch &lt;- "hello"
  ch &lt;- "world"
  ch &lt;- "!" # extra message in buffer
  fmt.Println(&lt;-ch)
}

// =&gt; fatal error: all goroutines are asleep - deadlock!
</code></pre>
<p>正如我们在上面看到的，一个 channel 接受的信息不超过2条。</p>
<h4 id="golang">为什么 Golang 会成功？</h4>
<blockquote>
<p>简洁性… — Rob-pike</p>
</blockquote>
<h3 id="great">Great</h3>
<p>我们学习了 Go 的一些主要组成部分和特点。</p>
<ol>
<li>变量、数据类型</li>
<li>数组 切片 和 maps</li>
<li>函数</li>
<li>循环和条件语句</li>
<li>指针</li>
<li>软件包</li>
<li>方法、结构体和接口</li>
<li>错误处理</li>
<li>并发 - Go 协程和通道</li>
</ol>
<p>恭喜你，你现在对 Go 有了相当的了解。</p>
<blockquote>
<p>我最有成效的一天是减少了 1000 行代码。<br>
— Ken Thompson</p>
</blockquote>
<p>不要停在这里，继续向前推进，思考一个小的应用并开始创建。</p>
<p><a href="https://www.linkedin.com/in/milap-neupane-99a4b565/">LinkedIn</a><br>
<a href="http://github.com/milap-neupane">Github</a><br>
<a href="https://twitter.com/_milap">Twitter</a></p>
<p>我也在 Milap Neupane 博客发布本文：<a href="https://milapneupane.com.np/2019/07/06/learning-golang-from-zero-to-hero/">学习Go，从0到1</a></p>
<!--kg-card-end: markdown--> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
