在这篇文章的最后,我们还将快速介绍如何使用简单的在Docker中运行 Go 程序Dockerfile,使其易于打包并在任何地方运行。
For 循环
我们首先要介绍的是简单的for循环。我们给出的示例有几种不同的循环类型,让我们来分解一下。
i := 1
for i <= 3 {
fmt.Println(i)
i = i + 1
}
我们的第一个循环使用上次介绍的语法i在循环外部声明并分配一个名为 的变量。它从 开始,在每次迭代中打印 的当前值,并按递增。当达到时,循环停止。:=1ii1i4
虽然在循环外声明i是可行的,但在循环初始化内声明更为常见且通常更清晰。
for j := 0; j < 3; j++ {
fmt.Println(j)
}
第二个循环更接近我的预期。变量j在循环内部声明,从 开始0,每次迭代后递增,并在j达到时停止3。每次迭代都会打印 的值j。
for i := range 3 {
fmt.Println("range", i)
}
此循环迭代 3 次,i取值 0、1 和 2。本质上,它与上一个循环相同,只是方式更简洁、更标准。这是一个范围循环,稍后我们将看到此语法与数组和切片一起使用。
for {
fmt.Println("loop")
break
}
现在事情变得有趣了。此循环没有停止条件,因此它将永远运行,除非我们break在之后立即使用,将其停止。当您需要在任何时候有条件地退出循环时Println,这很有用。
for n := range 6 {
if n%2 == 0 {
continue
}
fmt.Println(n)
}
巧妙的例子。我们在这里看到多个新事物;modulo运算符 ( %)、第一个if块和continue关键字。
此循环从 计数0到5。它n使用 来检查 是否为偶数n % 2 == 0。如果是,continue则跳过其余迭代并移至下一个数字。否则,它会打印n。
简而言之,它只打印范围内的奇数。
条件语句
条件语句是任何编程语言的必备元素,它们允许我们根据输入变量进行分支逻辑。条件块主要有两种类型:if/else语句和switch陈述,让我们来探索一下。
If/else 语句
如果语句归结为计算结果为“true或”的表达式false;如果表达式为true“做某事”,则执行其他操作。
同样,在这个例子中,我们给出了几个语句,所以让我们逐一看一下。
if 7%2 == 0 {
fmt.Println("7 is even")
} else {
fmt.Println("7 is odd")
}
我们的朋友,模数运算符又回来了。在这个例子中,我们检查数字 7 是否为偶数(它不是),并打印出它是否是偶数(同样,它不是)。
if 8%4 == 0 {
fmt.Println("8 is divisible by 4")
}
这基本上是相同的例子,但向我们表明该else部分不是必需的。
if 8%2 == 0 || 7%2 == 0 {
fmt.Println("either 8 or 7 are even")
}
这里我们有一个or条件,如果你使用过其他语言,这个条件对你来说应该很熟悉,if当任一条件为真时,语句就会执行
if num := 9; num < 0 {
fmt.Println(num, "is negative")
} else if num < 10 {
fmt.Println(num, "has 1 digit")
} else {
fmt.Println(num, "has multiple digits")
}
最后我们来看一个else if例子。这允许我们检查一个输入的多个条件并执行第一个为真的条件,最后else的 充当了通过所有其他检查的所有内容的捕获器。值得注意的是,变量的范围num仅限于if/else块。
我要说的是,我不喜欢像这样在 if 语句中声明变量。对我来说,这似乎不太可读,但我想我们可以轻松地像这样重写他们的示例以避免这种情况。
num := 9
if num < 0 {
fmt.Println(num, "is negative")
} else if num < 10 {
fmt.Println(num, "has 1 digit")
} else {
fmt.Println(num, "has multiple digits")
}
Go 没有三元运算符,如果你习惯使用其他语言的话,这就很遗憾了,但让我们看看我们是否真的想念它。
Switch 语句
switch 语句虽然类似于 if/else 块,但略有不同。它也依赖于对表达式求值,但这些表达式不一定是 be true,也false可以是任何表达式,我们可以case为每个结果设置一个条件,并设置一个defaultcase 来捕获其他任何结果。
让我们分解这个例子,看看他们是否在其中偷偷添加了任何新的东西。
i := 2
fmt.Print("Write ", i, " as ")
switch i {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
case 3:
fmt.Println("three")
}
第一个例子,他们已经偷偷地加入一些新的东西。打印Print时fmt不以换行符结尾,我们可以用逗号分隔值以将它们全部附加在一起。
除此之外,这个例子非常简单,声明i检查是否i为 1、2 或 3,如果是则打印出书面文字。
switch time.Now().Weekday() {
case time.Saturday, time.Sunday:
fmt.Println("It's the weekend")
default:
fmt.Println("It's a weekday")
}
在这里,我们第一次看到了时间包。我们似乎在使用enums,但由于我们还没有了解它们,所以我们只能等待。我们需要知道的是,我们的表达式获取的是当前星期几,我们使用case,逗号分隔符充当运算符or,我们检查它是在周末还是工作日。
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("It's before noon")
default:
fmt.Println("It's after noon")
}
此示例与前一个示例类似,只是我们的time.Now()调用存储为变量,并且我们没有将表达式传递给我们的switch。这将条件检查转移到 case 块中,使其表现得像if/else第一个truecase 执行的语句。
whatAmI := func(i interface{}) {
switch t := i.(type) {
case bool:
fmt.Println("I'm a bool")
case int:
fmt.Println("I'm an int")
default:
fmt.Printf("Don't know type %T\n", t)
}
}
whatAmI(true)
whatAmI(1)
whatAmI("hey")
好吧,我想他们可能会试图偷偷地加入一些额外的东西,就是这样,我们得到了第一个可重复使用的函数,一个interface(不管它是什么)和一个Printf函数。
让我们慢慢地分析一下,看看到底发生了什么。首先,我们有一个名为的变量声明,whatAmI即,它是一个,它func接受一个。iinterface{}
我不确定这是什么interface{}意思,所以我快速搜索了一下。这似乎interface是我们稍后会学习的东西,但interface{}它是如何告诉 Go 我们不知道将向函数传递什么类型的。可以将其视为interface{}可以容纳任何类型值的占位符。我们说的i可以是任何东西。
接下来,我们有 switch 表达式,其中我们声明了一个名为的新变量,并在其中t存储了 的类型。这使我们能够将s 设置为我们正在寻找的类型。icase
我们有 2 个cases 和一个defaultfallback。前两个cases 检查 if 是否i是bool(true或false) 或是否是int(整数),如果都不是,fallback 将打印出使用的类型Printf。Printf允许格式化输出,其中%T打印变量的类型。我们Printf在这里使用,因为%T不能直接转换为string。我们还必须以 结尾,\n以便重新放入换行符。
Docker
运行 Go 应用程序的一个好方法是将它们打包成 Docker 镜像,使它们可以在不同的系统(包括 Windows、Linux 和 macOS)之间移植。我们将使用多阶段 Dockerfile来高效地构建和执行我们的 Go 程序。
为什么要使用多阶段 Dockerfile?
多阶段构建通过将构建环境与运行时环境分开,有助于保持最终映像小巧高效。这减少了最终映像中不必要的依赖项,从而提高了安全性和性能。
让我们从编写一个开始Dockerfile。
# Build the GO binary
FROM golang:1.24.1-alpine AS build
COPY ./main.go ./main.go
RUN go build -o /bin/output ./main.go
# Execute the GO binary
FROM scratch
COPY --from=build /bin/output /bin/output
CMD ["/bin/output"]
构建阶段(构建)
我们从官方的golang:1.24.1-alpine image镜像开始。我们复制main.go到容器中(你可以重命名它)并将其编译为/bin/output。
运行时阶段
运行时阶段使用FROM scratch,这意味着它没有基本操作系统。scratch是一个空映像,没有其他内容。这最小化了最终映像的大小。我们使用命令COPY --from=build有效地仅从构建阶段复制已编译的二进制文件,并将 CMD 设置["/bin/output"]为入口命令。
构建并运行Docker镜像
要构建并运行图像,请使用以下命令:
# Build the image (dot specifies the current directory as context)
docker build -t my-go-app:latest .
# Run the container
docker run my-go-app:latest
签名
这次我们讲了很多内容,我们的程序也变得越来越复杂。我们进展顺利,语法也变得非常容易使用。下次我们将讨论数组、切片和映射。我们确实取得了一些进展,但与此同时,我们只是触及了皮毛。

