forked from fancygo/godoc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcode.html
583 lines (459 loc) · 15.8 KB
/
code.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
<!--{
"Title": "如何编写Go代码"
}-->
<h2 id="Introduction">简介</h2>
<p>
这篇文档演示了一个简单Go包(package)的开发过程并介绍了<a href="/cmd/go/">go tool</a>使用方式,
以及获取,编译,安装Go包(package)和命令的标准方法.
</p>
<p>
go tool 要求使用一种特定的方式来组织代码。
请仔细阅读这份文档。
它解释了如何用最简单的方式创建和运行Go。
</p>
<h2 id="Organization">代码组织</h2>
<h3 id="Overview">概述</h3>
<ul>
<li>通常,Go程序员把所有的Go代码保存在单个的 <i>工作空间</i>里.</li>
<li>一个工作空间包含了多个版本控制 <i>仓库</i>
(比如用Git管理的仓库).</li>
<li>每个仓库包含了一个或多个 <i>包(packages)</i>.</li>
<li>在单个目录中的每个包(package) 由一个或多个Go源文件组成.</li>
<li>指向包(package)目录的路径决定了 <i>import path</i>.</li>
</ul>
<p>
注意:这种组织方式不同于所有工程都有一个独立的工作空间和工作空间与版本控制仓库紧密相连的
其他编程环境.
</p>
<h3 id="Workspaces">工作空间</h3>
<p>
工作空间是一个在根目录有三个目录的目录层级:
</p>
<ul>
<li><code>src</code> 包含Go源文件,
<li><code>pkg</code> 包含包(package)对象,
<li><code>bin</code> 包含可执行文件。
</ul>
<p>
go tool 编译源代码并将生成的二进制文件安装到pkg和bin目录。
</p>
<p>
<code>src</code> 子目录通常包含多个版本控制库,如此可以追踪一个或多个源代码包的开发过程。
</p>
<p>
下面这个例子展示在实践中工作目录是什么样的:
</p>
<pre>
bin/
hello # command executable
outyet # command executable
pkg/
linux_amd64/
github.com/golang/example/
stringutil.a # package object
src/
<a href="https://github.com/golang/example/">github.com/golang/example/</a>
.git/ # Git repository metadata
hello/
hello.go # command source
outyet/
main.go # command source
main_test.go # test source
stringutil/
reverse.go # package source
reverse_test.go # test source
<a href="https://golang.org/x/image/">golang.org/x/image/</a>
.git/ # Git repository metadata
bmp/
reader.go # package source
writer.go # package source
... (many more repositories and packages omitted) ...
</pre>
<p>
上面这个代码树展示了一个工作目录,其包含两个仓库
(<code>example</code> 和 <code>image</code>).
<code>example</code> 仓库包含两个可执行文件 (<code>hello</code> 和
<code>outyet</code>) 和一个静态库 (<code>stringutil</code>).
<code>image</code> 仓库包含了 <code>bmp</code> 包以及
<a href="https://godoc.org/golang.org/x/image">一些其他包</a>.
</p>
<p>
通常一个工作空间包含多个含有许多包和可执行文件的仓库。
大部分的Go程序员把所有的Go源码和依赖库放在同一个工作空间。
</p>
<p>
可执行文件和依赖库从不同的源包码编译而来。后面会讨论他们之间的不同。
</p>
<h3 id="GOPATH"><code>GOPATH</code> 环境变量</h3>
<p>
<code>GOPATH</code> 环境变量指定了工作空间的位置。在使用Go开发的时候,可能是
唯一需要设置的环境变量。
</p>
<p>
刚开始,创建一个工作空间相应的把它设置为<code>GOPATH</code>。
可以设置任何位置作为工作空间,在这篇文档里使用<code>$HOME/work</code>作为工作空间。
这个路径与你安装的路径并不一定要相同。
(其他的通用设置是<code>GOPATH=$HOME</code>.)
</p>
<pre>
$ <b>mkdir $HOME/work</b>
$ <b>export GOPATH=$HOME/work</b>
</pre>
<p>
方便起见,把工作空间下的子目录 <code>bin</code> 加到环境变量 <code>PATH</code> 中去。
</p>
<pre>
$ <b>export PATH=$PATH:$GOPATH/bin</b>
</pre>
<p>
如果想学习更多有关设置 <code>GOPATH</code> 环境变量的知识,请参阅
<a href="/cmd/go/#hdr-GOPATH_environment_variable"><code>go help gopath</code></a>
</p>
<h3 id="ImportPaths">Import paths</h3>
<p>
<i>import path</i> 是唯一确认一个包的字符串。
一个包的导入路径与它在工作目录中的位置或远程仓库的位置一致(下面给出解释)。
</p>
<p>
标准库里的包(package)会用一些简短的 import paths比如<code>"fmt"</code> 和 <code>"net/http"</code>之类。
自定义包(package),必须选择一个基本路径并且不能与将来添加到标准库或外部库的路径相冲突。
</p>
<p>
如果将代码保存在源仓库的某个地方,则需要使用源仓库的跟目录作为基本路径。
比如,假设你有一个 <a href="https://github.com/">GitHub</a> 账户 <code>github.com/user</code>,这就可以作为基础路径。
</p>
<p>
注意在代码编译通过之前不要将其发布到远程仓库。如果在将来的某一天发布代码,这将对组织代码是一个好习惯。
实际上可以选择任意的路径名,只要它对标准库和Go语言系统是唯一的就好。
</p>
<p>
我们将使用 <code>github.com/user</code> 作为基础路径。在工作目录中创建一个目录用来保存源代码:
</p>
<pre>
$ <b>mkdir -p $GOPATH/src/github.com/user</b>
</pre>
<h3 id="Command">第一个程序 </h3>
<p>
为了编译运行一个简单的程序,首先选择一个路径(我们使用 <code>github.com/user/hello</code>)并在工作空间创建相应的包目录:
</p>
<pre>
$ <b>mkdir $GOPATH/src/github.com/user/hello</b>
</pre>
<p>
然后,在目录中创建一个名为 <code>hello.go</code> 的文件,包含下面的Go代码。
</p>
<pre>
package main
import "fmt"
func main() {
fmt.Printf("Hello, world.\n")
}
</pre>
<p>
现在你可以使用 <code>go</code> tool 来编译安装程序。
</p>
<pre>
$ <b>go install github.com/user/hello</b>
</pre>
<p>
注意,可以在系统的任意位置执行这个命令。<code>go</code> tool 通过指定的 <code>GOPATH</code> 环境变量
在工作空间中查找 <code>github.com/user/hello</code>包(package)中的源代码。
</p>
<p>
如直接在包(package)目录执行 <code>go install</code> 命令则可以省略包的路径:
</p>
<pre>
$ <b>cd $GOPATH/src/github.com/user/hello</b>
$ <b>go install</b>
</pre>
<p>
这个命令编译出可执行文件hello(windows下是hello.exe),然后把可执行文件安装到工作空间的bin目录。
</p>
<p>
编译时如果有错误,go tool 会把错误打印到终端输出,如果没有输出则表示编译成功。
</p>
<p>
现在可以在命令行通过键入完整路径来执行程序:
</p>
<pre>
$ <b>$GOPATH/bin/hello</b>
Hello, world.
</pre>
<p>
或者如果已经把 <code>$GOPATH/bin</code> 添加到环境变量 <code>PATH</code> 中,则只需要键入可执行文件名:
</p>
<pre>
$ <b>hello</b>
Hello, world.
</pre>
<p>
如果你使用版本控制系统,现在是时候创建一个仓库,添加代码,并把它提交。
再次说明:这一步是可选的,你可能并不需要版本控制系统写Go代码。
</p>
<pre>
$ <b>cd $GOPATH/src/github.com/user/hello</b>
$ <b>git init</b>
在 /home/user/work/src/github.com/user/hello/.git/ 初始化一个空的git仓库
$ <b>git add hello.go</b>
$ <b>git commit -m "initial commit"</b>
[master (root-commit) 0b4507d] initial commit
1 file changed, 1 insertion(+)
create mode 100644 hello.go
</pre>
<p>
读者们可以练习一下如何把代码推送到远程仓库,在这里就不介绍了。
</p>
<h3 id="Library">第一个库</h3>
<p>
让我们来写一个 library 然后在 <code>hello</code> 程序中使用。
</p>
<p>
同样,第一步创建一个包(package)路径 (我们使用<code>github.com/user/stringutil</code>) 并创建包(package)目录:
</p>
<pre>
$ <b>mkdir $GOPATH/src/github.com/user/stringutil</b>
</pre>
<p>
然后,在该目录中创建一个名叫 <code>reverse.go</code> 的文件包含以下代码。
</p>
<pre>
// Package stringutil contains utility functions for working with strings.
package stringutil
// Reverse returns its argument string reversed rune-wise left to right.
func Reverse(s string) string {
r := []rune(s)
for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
return string(r)
}
</pre>
<p>
使用 <code>go build</code> 测试该package是否编译成功:
</p>
<pre>
$ <b>go build github.com/user/stringutil</b>
</pre>
<p>
或者也可以直接在包源目录执行:
</p>
<pre>
$ <b>go build</b>
</pre>
<p>
这并不会产生一个输出文件。如果要生成输出文件,必须使用 <code>go install</code>,这将会把包对象放置在工作目录的 <code>pkg</code> 目录中。
</p>
<p>
在确认 <code>stringutil</code> 包可以编译之后,在源文件 <code>hello.go</code>(在$GOPATH/src/github.com/user/hello中) 中使用这个包:
</p>
<pre>
package main
import (
"fmt"
<b>"github.com/user/stringutil"</b>
)
func main() {
fmt.Printf(stringutil.Reverse("!oG ,olleH"))
}
</pre>
<p>
无论何时使用 <code>go</code> tool编译安装二进制文件或包的时候,会同时把它依赖的包都安装上。
所以当编译 <code>hello</code> 程序的时候
</p>
<pre>
$ <b>go install github.com/user/hello</b>
</pre>
<p>
<code>stringutil</code> 也将自动被编译。
</p>
<p>
执行新的程序,将会看到一个新的,反过来的信息:
</p>
<pre>
$ <b>hello</b>
Hello, Go!
</pre>
<p>
经过上面的步骤后,你的工作空间结构会像这样:
</p>
<pre>
bin/
hello # command executable
pkg/
linux_amd64/ # this will reflect your OS and architecture
github.com/user/
stringutil.a # package object
src/
github.com/user/
hello/
hello.go # command source
stringutil/
reverse.go # package source
</pre>
<p>
注意 <code>go install</code> 会把 <code>stringutil.a</code> 文件放到 <code>pkg/linux_amd64</code> 下与它源目录相对应的目录里,它是源目录的镜像。
这是为了以后 <code>go</code> tool 可以直接找到该包文件,而不用重新编译它。
<code>linux_amd64</code> 部分对交叉编译有帮助,这将反应出你所使用的操作系统和架构。
</p>
<p>
Go命令执行是静态链接,包对象并不需要对当前执行的Go程序可见。
</p>
<h3 id="PackageNames">包名</h3>
<p>
Go源文件的第一句必须是
</p>
<pre>
package <i>name</i>
</pre>
<p>
<code><i>name</i></code> 导入包的默认名字.
(同一个包(package)内的所有文件必须使用相同的 <code><i>name</i></code>.)
</p>
<p>
Go的惯例是把import path的最后一个元素作为包(package)名,比如导入<code>crypto/rot13</code>路径,则包名就是 <code>rot13</code>。
</p>
<p>
可执行文件必须使用 <code>package main</code>。
</p>
<p>
并不是说所有链接到一个可执行文件的所有包(package)的包名都要是唯一的,只要那些被import的路径名(绝对路径)是唯一的。
</p>
<p>
阅读<a href="/doc/effective_go.html#names">Effective Go</a> 可以学到更多关于Go命名的惯例。
</p>
<h2 id="Testing">测试</h2>
<p>
Go拥有一个由 go test 命令和 testing package 组成的轻量级的测试框架。
</p>
<p>
创建一个以_test.go为后缀名的测试程序,其中包含了以TestXXX命名,以func (t *testing.T)为标签的函数。
test框架将会执行每一个这样的函数。如果函数调用了像t.Error或者t.Fail的失败函数,则表示测试已经失败了。
</p>
<p>
在 <code>stringutil</code> 包(package)里添加一个测试文件 <code>$GOPATH/src/github.com/user/stringutil/reverse_test.go</code> 并包含以下代码:
</p>
<pre>
package stringutil
import "testing"
func TestReverse(t *testing.T) {
cases := []struct {
in, want string
}{
{"Hello, world", "dlrow ,olleH"},
{"Hello, 世界", "界世 ,olleH"},
{"", ""},
}
for _, c := range cases {
got := Reverse(c.in)
if got != c.want {
t.Errorf("Reverse(%q) == %q, want %q", c.in, got, c.want)
}
}
}
</pre>
<p>
然后使用 go test 来执行这个test程序:
</p>
<pre>
$ <b>go test github.com/user/stringutil</b>
ok github.com/user/stringutil 0.165s
</pre>
<p>
像往常一样,如果在包(package)目录下面执行 go tool,则可以省略包(package)路径:
</p>
<pre>
$ <b>go test</b>
ok github.com/user/stringutil 0.165s
</pre>
<p>
执行 go help test命令查看更多关于 <a href="/pkg/testing/">测试包的文档</a>。
</p>
<h2 id="remote">远程包</h2>
<p>
导入路径(import path)能够描述如何使用版本控制系统(Git或者Mercurial)获取这个包(package)的源码。
go tool通过使用这个特性从远程仓库获取packages。
比如,这个文档里所描述的例子都在GitHub
<code><a href="https://github.com/golang/example">github.com/golang/example</a></code>管理的Git仓库中.
如果在包的import path中包含仓库的URL,go get 命令将自动获取,编译,安装(生成可执行文件)它。
</p>
<pre>
$ <b>go get github.com/golang/example/hello</b>
$ <b>$GOPATH/bin/hello</b>
Hello, Go examples!
</pre>
<p>
如果指定的包(package)不在工作空间中,那么 go get 会把它存放到 GOPATH 指定的第一个工作空间中。
(如果这个package已经存在了,那么 go get 会跳过获取动作,与直接执行go install执行的效果一样。)
</p>
<p>
在使用了上面的 go get 命令后,工作空间目录树将会像这样:
</p>
<pre>
bin/
hello # command executable
pkg/
linux_amd64/
github.com/golang/example/
stringutil.a # package object
github.com/user/
stringutil.a # package object
src/
github.com/golang/example/
.git/ # Git repository metadata
hello/
hello.go # command source
stringutil/
reverse.go # package source
reverse_test.go # test source
github.com/user/
hello/
hello.go # command source
stringutil/
reverse.go # package source
reverse_test.go # test source
</pre>
<p>
在GitHub上托管的可执行文件<code>hello</code>依赖于同一个仓库的
<code>stringutil</code> 包(package). The imports in
<code>hello.go</code>文件中的imports按照惯例使用同样路径, 因此
<code>go get</code>命令也可以定位和安装独立的包(package)。
</p>
<pre>
import "github.com/golang/example/stringutil"
</pre>
<p>
按照惯例,这可以很方便让别人使用你的Go包(package)。
<a href="//golang.org/wiki/Projects">Go Wiki</a>
和 <a href="//godoc.org/">godoc.org</a>
提供了一系列的外部的Go项目。
</p>
<p>
如果想获得更多有关通过 go tool 使用远程仓库的信息,可以查看<code><a href="/cmd/go/#hdr-Remote_import_paths">go help importpath</a></code>。
</p>
<h2 id="next">下一步呢?</h2>
<p>
订阅 <a href="//groups.google.com/group/golang-announce">golang-announce</a> 邮件列表,这样,当一个新的稳定版本发布的时候,
以便第一时间可以通知到你。
</p>
<p>
阅读 <a href="/doc/effective_go.html">Effective Go</a> 关于写出简洁、惯用的Go代码的一些建议。
</p>
<p>
使用 <a href="//tour.golang.org/">A Tour of Go</a> 来学习正统的Go。
</p>
<p>
阅览 <a href="/doc/#articles">documentation page</a> 可以看到一些深层次的关于Go语言以及它的各种库和工具的文章。
</p>
<h2 id="help">获取帮助</h2>
<p>
可以使用 <code>#go-nuts</code>在
<a href="http://freenode.net/">Freenode</a> IRC server获取热心人的实时帮助.
</p>
<p>
官方讨论Go语言的邮件列表是
<a href="//groups.google.com/group/golang-nuts">Go Nuts</a>.
</p>
<p>
使用<a href="//golang.org/issue">Go issue tracker
来上报BUG</a>.
</p>