分享
  1. 首页
  2. 文章

Go语言

newsyoung1 · · 6263 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

最近在学习并用go语言编程,发现go语言蛮好玩也挺有意思

本文通过对A Tour of Go的实践,总结Go语言的基础用法。

1 Go语言"奇怪用法"有哪些?


1,go的变量声明顺序是:"先写变量名,再写类型名",此与C/C++的语法孰优孰劣,可见下文解释:
http://blog.golang.org/gos-declaration-syntax

2,go是通过package来组织的(与python类似),只有package名为main的包可以包含main函数,一个可执行程序有且仅有一个main包,通过import关键字来导入其他非main包。

3,可见性规则。go语言中,使用大小写来决定该常量、变量、类型、接口、结构或函数是否可以被外部包含调用。根据约定,函数名首字母小写即为private,函数名首字母大写即为public。

4,go内置关键字(25个均为小写)。

5,函数不用先声明,即可使用。

6,在函数内部可以通过 := 隐士定义变量。(函数外必须显示使用var定义变量)

7,go程序使用UTF-8编码的纯Unicode文本编写。

8,使用big.Int的陷阱:
http://stackoverflow.com/questions/11270547/go-big-int-factorial-with-recursion

9,从技术层面讲,go语言的语句是以分号分隔的,但这些是由编译器自动添加的,不用手动输入,除非需要在同一行中写入多个语句。没有分号及只需少量的逗号和圆括号,使得go语言的程序更容易阅读。

10,go语言只有一个循环结构——for循环。

11,go里的自增运算符只有——"后++"

12,go语言中的slice用法类似python中数组,关于slice的详细用法可见:http://blog.golang.org/go-slices-usage-and-internals

13,函数也是一个值,使用匿名函数返回一个值。

14,函数闭包的使用,闭包是一个匿名函数值,会引用到其外部的变量。



2 代码实践


  1. /* gerryyang
  2. * 2013年11月23日
  3. */
  4. package main
  5. import (
  6. "fmt"
  7. "math"
  8. "math/big"
  9. "math/cmplx"
  10. "math/rand"
  11. "net/http"
  12. "os"
  13. "runtime"
  14. "time"
  15. )
  16. // Outside a function, every construct begins with a keyword (var, func, and so on) and the := construct is not available
  17. // The var statement declares a list of variables; as in function argument lists, the type is last
  18. var x, y, z int
  19. var c, python, java bool
  20. // A var declaration can include initializers, one per variable
  21. var x1, y1, z1 int = 1, 2, 3
  22. // If an initializer is present, the type can be omitted; the variable will take the type of the initializer
  23. var c1, python1, java1 = true, false, "no!"
  24. // basic types
  25. // bool
  26. // string
  27. // int int8 int16 int32 int64
  28. // uint uint8 uint16 uint32 uint64 uintptr
  29. // byte (alias for uint8)
  30. // rune (alias for int32, represents a Unicode code point)
  31. // float32 float64
  32. // complex64 complex128
  33. var (
  34. ToBe bool = false
  35. MaxInt uint64 = 1<<64 - 1
  36. complex complex128 = cmplx.Sqrt(-5 + 12i)
  37. )
  38. // Constants are declared like variables, but with the const keyword
  39. const Pi = 3.14
  40. // Constants can be character, string, boolean, or numeric values
  41. const World = "世界"
  42. // Numeric Constants
  43. const (
  44. Big = 1 << 100
  45. Small = Big >> 99 // 2
  46. )
  47. type Vertex struct {
  48. X int
  49. Y int
  50. }
  51. type Vertex2 struct {
  52. Lat, Long float64
  53. }
  54. var m map[string]Vertex2
  55. // Map literals are like struct literals, but the keys are required
  56. var m2 = map[string]Vertex2{
  57. "gerryyang": Vertex2{
  58. 100, 200,
  59. },
  60. "wcdj": Vertex2{
  61. -300, 500,
  62. },
  63. }
  64. // If the top-level type is just a type name, you can omit it from the elements of the literal
  65. var m3 = map[string]Vertex2{
  66. "math": {20, 40},
  67. "computer": {30, 50},
  68. }
  69. type Vertex3 struct {
  70. X, Y float64
  71. }
  72. type MyFloat float64
  73. type Abser interface {
  74. Abs() float64
  75. }
  76. ////////////////////////////////////////////////////////
  77. func main() {
  78. fmt.Println("Hello Golang, I'm gerryyang")
  79. fmt.Println("The time is", time.Now())
  80. // To see a different number, seed the number generator; see rand.Seed
  81. fmt.Println("My favorite number is", rand.Intn(7))
  82. fmt.Printf("Now you have %g problesms\n", math.Nextafter(2, 3))
  83. // In Go, a name is exported if it begins with a capital letter
  84. fmt.Println(math.Pi)
  85. // Notice that the type comes after the variable name
  86. fmt.Println(add(42, 13))
  87. fmt.Println(add2(42, 13))
  88. // multiple results
  89. a, b := swap("gerry", "yang")
  90. fmt.Println(a, b)
  91. // named return
  92. fmt.Println(split(17))
  93. fmt.Println(split2(17))
  94. // var used
  95. fmt.Println(x, y, z, c, python, java)
  96. fmt.Println(x1, y1, z1, c1, python1, java1)
  97. // Inside a function, the := short assignment statement can be used in place of a var declaration with implicit type
  98. var x2, y2, z2 int = 1, 2, 3
  99. c2, python2, java2 := true, false, "yes!"
  100. fmt.Println(x2, y2, z2, c2, python2, java2)
  101. // basic types
  102. const f = "%T(%v)\n"
  103. fmt.Printf(f, ToBe, ToBe)
  104. fmt.Printf(f, MaxInt, MaxInt)
  105. fmt.Printf(f, complex, complex)
  106. // Constants cannot be declared using the := syntax
  107. const World2 = "和平"
  108. const Truth = true
  109. fmt.Println(Pi)
  110. fmt.Println("你好", World)
  111. fmt.Println("世界", World2)
  112. fmt.Println("Go rules?", Truth)
  113. // Numeric Constants
  114. fmt.Println(needInt(Small))
  115. ////fmt.Println(needInt(Big))// error, constant 1267650600228229401496703205376 overflows int
  116. ////fmt.Println(needInt64(Big)) // error, same as above
  117. ////fmt.Println(needBigInt(big.NewInt(Big)))// error, 使用big.Int貌似入参最大类型只支持int64
  118. fmt.Println(needFloat(Small))
  119. fmt.Println(needFloat(Big))
  120. // Go has only one looping construct, the for loop
  121. // The basic for loop looks as it does in C or Java, except that the ( ) are gone (they are not even optional) and the { } are required
  122. sum := 0
  123. for i := 0; i < 10; i++ {
  124. sum += i
  125. }
  126. fmt.Println(sum)
  127. // As in C or Java, you can leave the pre and post statements empty
  128. // At that point you can drop the semicolons: C's while is spelled for in Go
  129. sum1 := 1
  130. for sum1 < 1000 {
  131. sum1 += sum1
  132. }
  133. fmt.Println(sum1)
  134. // If you omit the loop condition it loops forever, so an infinite loop is compactly expressed
  135. ivar := 1
  136. for {
  137. if ivar++; ivar > 1000 {
  138. fmt.Println("leave out an infinite loop")
  139. break
  140. }
  141. }
  142. // The if statement looks as it does in C or Java, except that the ( ) are gone and the { } are required
  143. fmt.Println(sqrt(2), sqrt(-4))
  144. // Like for, the if statement can start with a short statement to execute before the condition
  145. fmt.Println(pow(3, 2, 10), pow(3, 3, 20))
  146. // If and else
  147. fmt.Println(pow2(3, 2, 10), pow2(3, 3, 20))
  148. ////////////////////////////////////////////////////////////
  149. // A struct is a collection of fields
  150. fmt.Println(Vertex{1, 2})
  151. // Struct fields are accessed using a dot
  152. v := Vertex{1, 2}
  153. v.X = 4
  154. fmt.Println(v)
  155. // Go has pointers, but no pointer arithmetic
  156. // Struct fields can be accessed through a struct pointer. The indirection through the pointer is transparent
  157. p := Vertex{1, 2}
  158. q := &p
  159. q.X = 1e9
  160. fmt.Println(p)
  161. // struct literals
  162. // A struct literal denotes a newly allocated struct value by listing the values of its fields
  163. p = Vertex{1, 2} // has type Vertex
  164. q = &Vertex{1, 2} // has type * Vertex
  165. r := Vertex{X: 1} // Y:0 is implicit
  166. s := Vertex{} // X:0 and Y:0
  167. fmt.Println(p, q, r, s)
  168. // The expression new(T) allocates a zeroed T value and returns a pointer to it
  169. // var t *T = new(T) or t := new(T)
  170. pv := new(Vertex)
  171. fmt.Println(pv)
  172. pv.X, pv.Y = 11, 9
  173. fmt.Println(pv)
  174. // A slice points to an array of values and also includes a length
  175. // []T is a slice with elements of type T
  176. as := []int{2, 3, 5, 7, 11, 13}
  177. fmt.Println("as ==", as)
  178. for i := 0; i < len(as); i++ {
  179. fmt.Printf("as[%d] == %d\n", i, as[i])
  180. }
  181. // Slices can be re-sliced, creating a new slice value that points to the same array
  182. // The expression: s[lo:hi]
  183. // evaluates to a slice of the elements from lo through hi-1, inclusive
  184. fmt.Println("as[1:4] ==", as[1:4])
  185. // missing low index implies 0
  186. fmt.Println("as[:3] ==", as[:3])
  187. // missing high index implies len(s)
  188. fmt.Println("as[4:] ==", as[4:])
  189. // Slices are created with the make function. It works by allocating a zeroed array and returning a slice that refers to that array
  190. // a := make([]int, 5), note, len(a) = 5
  191. // To specify a capacity, pass a third argument to make
  192. // b := make([]int, 0 , 5), note, len(b) = 0, cap(b) = 5
  193. // b = b[:cap(b)], note, len(b) = 5, cap(b) = 5
  194. // b = b[1:], note, len(b) = 4, cap(b) = 4
  195. s1 := make([]int, 5)
  196. printSlice("s1", s1)
  197. s2 := make([]int, 0, 5)
  198. printSlice("s2", s2)
  199. s3 := s2[:2]
  200. printSlice("s3", s3)
  201. s4 := s3[2:5]
  202. printSlice("s4", s4)
  203. // The zero value of a slice is nil
  204. // A nil slice has a length and capacity of 0
  205. var s5 []int
  206. fmt.Println(s5, len(s5), cap(s5))
  207. if s5 == nil {
  208. fmt.Println("slice is nil")
  209. }
  210. // The range form of the for loop iterates over a slice or map
  211. var s6 = []int{1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024}
  212. for i, v := range s6 {
  213. fmt.Printf("2**%d = %d\n", i, v)
  214. }
  215. // If you only want the index, drop the ", value" entirely
  216. for i := range s6 {
  217. s6[i] = 1 << uint(i)
  218. }
  219. // You can skip the index or value by assigning to _
  220. for _, value := range s6 {
  221. fmt.Printf("%d\n", value)
  222. }
  223. // A map maps keys to values
  224. // Maps must be created with make (not new) before use; the nil map is empty and cannot be assigned to
  225. m = make(map[string]Vertex2)
  226. m["Bell Labs"] = Vertex2{
  227. 40.68433, -74.39967,
  228. }
  229. fmt.Println(m["Bell Labs"])
  230. // Map literals are like struct literals, but the keys are required
  231. fmt.Println(m2)
  232. // If the top-level type is just a type name, you can omit it from the elements of the literal
  233. fmt.Println(m3)
  234. // map
  235. // insert, update, retrieve, delete, test
  236. m4 := make(map[string]int)
  237. m4["date"] = 20131129
  238. fmt.Println("The value:", m4["date"])
  239. m4["date"] = m4["date"] + 1
  240. fmt.Println("The value:", m4["date"])
  241. date, ok := m4["date"]
  242. fmt.Println("The value:", date, "Present?", ok)
  243. delete(m4, "date")
  244. fmt.Println("The value:", m4["date"])
  245. date2, ok := m4["date"]
  246. fmt.Println("The value:", date2, "Present?", ok)
  247. // Function values
  248. // Functions are values too
  249. hypot := func(x, y float64) float64 {
  250. return math.Sqrt(x*x + y*y)
  251. }
  252. fmt.Println(hypot(3, 4))
  253. // Function closures
  254. // For example, the adder function returns a closure. Each closure is bound to its own sum variable
  255. pos, neg := adder(), adder()
  256. for i := 0; i < 10; i++ {
  257. fmt.Println(pos(i), neg(-2*i))
  258. }
  259. // fibonacci
  260. fib := fibonacci()
  261. for i := 0; i < 10; i++ {
  262. fmt.Println(fib())
  263. }
  264. // switch
  265. // A case body breaks automatically, unless it ends with a fallthrough statement
  266. switch os := runtime.GOOS; os {
  267. case"darwin":
  268. fmt.Println("OS X")
  269. case"linux":
  270. fmt.Println("Linux")
  271. default:
  272. // freebsd, openbsd
  273. // plan9, windows...
  274. fmt.Printf("%s", os)
  275. }
  276. // Switch cases evaluate cases from top to bottom, stopping when a case succeeds
  277. // Note: Time in the Go playground always appears to start at 2009年11月10日 23:00:00 UTC
  278. fmt.Println("When's Saturday?")
  279. today := time.Now().Weekday()
  280. switch time.Saturday {
  281. case today + 0:
  282. fmt.Println("Today")
  283. case today + 1:
  284. fmt.Println("Tomorrow")
  285. case today + 2:
  286. fmt.Println("In two days")
  287. case today + 3:
  288. fmt.Println("In three days")
  289. default:
  290. fmt.Println("Too far away")
  291. }
  292. // Switch without a condition is the same as switch true
  293. // This construct can be a clean way to write long if-then-else chains
  294. t_now := time.Now()
  295. switch {
  296. case t_now.Hour() < 12:
  297. fmt.Println("Good morning!")
  298. case t_now.Hour() < 17:
  299. fmt.Println("Good afternoon")
  300. default:
  301. fmt.Println("Good evening")
  302. }
  303. // Go does not have classes. However, you can define methods on struct types
  304. v3 := &Vertex3{3, 4}
  305. fmt.Println(v3.Abs())
  306. // In fact, you can define a method on any type you define in your package, not just structs
  307. // You cannot define a method on a type from another package, or on a basic type
  308. f1 := MyFloat(-math.Sqrt2)
  309. fmt.Println(f1.Abs())
  310. // Methods with pointer receivers
  311. // Methods can be associated with a named type or a pointer to a named type
  312. // We just saw two Abs methods. One on the *Vertex pointer type and the other on the MyFloat value type
  313. // There are two reasons to use a pointer receiver. First, to avoid copying the value on each method call (more efficient if the value type is a large struct). Second, so that the method can modify the value that its receiver points to
  314. v3 = &Vertex3{3, 4}
  315. v3.Scale(5)
  316. fmt.Println(v3, v3.Abs())
  317. // An interface type is defined by a set of methods
  318. // A value of interface type can hold any value that implements those methods
  319. var a_interface Abser
  320. v4 := Vertex3{3, 4}
  321. a_interface = f1 // a MyFloat implements Abser
  322. a_interface = &v4 // a *Vertex3 implements Abser
  323. //a_interface = v4 // a Vertex3, does Not, error!
  324. fmt.Println(a_interface.Abs())
  325. // Interfaces are satisfied implicitly
  326. var w Writer
  327. // os.Stdout implements Writer
  328. w = os.Stdout
  329. fmt.Fprintf(w, "hello, writer\n")
  330. // An error is anything that can describe itself as an error string. The idea is captured by the predefined, built-in interface type, error, with its single method, Error, returning a string: type error interface { Error() string }
  331. if err := run(); err != nil {
  332. fmt.Println(err)
  333. }
  334. // Web servers
  335. //var h Hello
  336. //http.ListenAndServe("localhost:4000", h)
  337. }
  338. /////////////////////////////////////////////
  339. // func can be used before declare
  340. func add(x int, y int) int {
  341. return x + y
  342. }
  343. // When two or more consecutive named function parameters share a type, you can omit the type from all but the last
  344. func add2(x, y int) int {
  345. return x + y
  346. }
  347. // multiple results, a function can return any number of results
  348. func swap(x, y string) (string, string) {
  349. return y, x
  350. }
  351. // In Go, functions can return multiple "result parameters", not just a single value. They can be named and act just like variables
  352. func split(sum int) (x, y int) {
  353. x = sum * 4 / 9
  354. y = sum - x
  355. return y, x
  356. }
  357. // In Go, functions can return multiple "result parameters", not just a single value. They can be named and act just like variables
  358. func split2(sum int) (x, y int) {
  359. x = sum * 4 / 9
  360. y = sum - x
  361. // If the result parameters are named, a return statement without arguments returns the current values of the results
  362. return
  363. }
  364. func needInt(x int) int { return x*10 + 1 }
  365. func needInt64(x int64) int64 { return x*10 + 1 }
  366. func needBigInt(x *big.Int) (result *big.Int) {
  367. result = new(big.Int)
  368. result.Set(x)
  369. result.Mul(result, big.NewInt(10))
  370. return
  371. }
  372. func needFloat(x float64) float64 {
  373. return x * 0.1
  374. }
  375. func sqrt(x float64) string {
  376. if x < 0 {
  377. return sqrt(-x) + "i"
  378. }
  379. return fmt.Sprint(math.Sqrt(x))
  380. }
  381. // Variables declared by the statement are only in scope until the end of the if
  382. func pow(x, n, lim float64) float64 {
  383. if v := math.Pow(x, n); v < lim {
  384. return v
  385. }
  386. return lim
  387. }
  388. // Variables declared inside an if short statement are also available inside any of the else blocks
  389. func pow2(x, n, lim float64) float64 {
  390. if v := math.Pow(x, n); v < lim {
  391. return v
  392. } else {
  393. fmt.Printf("%g >= %g\n", v, lim)
  394. }
  395. // can't use v here, though
  396. return lim
  397. }
  398. func printSlice(s string, x []int) {
  399. fmt.Printf("%s len = %d cap = %d %v\n", s, len(x), cap(x), x)
  400. }
  401. // Go functions may be closures. A closure is a function value that references variables from outside its body. The function may access and assign to the referenced variables; in this sense the function is "bound" to the variables
  402. func adder() func(int) int {
  403. sum := 0
  404. return func(x int) int {
  405. sum += x
  406. return sum
  407. }
  408. }
  409. // fibonacci is a function that returns
  410. // a function that returns an int.
  411. func fibonacci() func() int {
  412. p := 0
  413. q := 1
  414. s := 0
  415. return func() int {
  416. s = p + q
  417. p = q
  418. q = s
  419. return s
  420. }
  421. }
  422. // The method receiver appears in its own argument list between the func keyword and the method name
  423. func (v *Vertex3) Abs() float64 {
  424. return math.Sqrt(v.X*v.X + v.Y*v.Y)
  425. }
  426. func (f MyFloat) Abs() float64 {
  427. if f < 0 {
  428. fmt.Println("f < 0 here")
  429. return float64(-f)
  430. }
  431. return float64(f)
  432. }
  433. // Methods with pointer receivers
  434. func (v *Vertex3) Scale(f float64) {
  435. v.X = v.X * f
  436. v.Y = v.Y * f
  437. }
  438. // Interfaces are satisfied implicitly
  439. type Reader interface {
  440. Read(b []byte) (n int, err error)
  441. }
  442. type Writer interface {
  443. Write(b []byte) (n int, err error)
  444. }
  445. type ReadWriter interface {
  446. Reader
  447. Writer
  448. }
  449. // error control
  450. type MyError struct {
  451. When time.Time
  452. What string
  453. }
  454. func (e *MyError) Error() string {
  455. return fmt.Sprintf("at %v, %s", e.When, e.What)
  456. }
  457. func run() error {
  458. return &MyError{
  459. time.Now(),
  460. "it didn't work",
  461. }
  462. }
  463. // Web servers
  464. type Hello struct{}
  465. func (h Hello) ServeHTTP(
  466. w http.ResponseWriter,
  467. r *http.Request) {
  468. fmt.Fprint(w, "gerryyang")
  469. }
  470. /*
  471. output:
  472. Hello Golang, I'm gerryyang
  473. The time is 2013年12月04日 22:52:01.336562598 +0800 HKT
  474. My favorite number is 6
  475. Now you have 2.0000000000000004 problesms
  476. 3.141592653589793
  477. 55
  478. 55
  479. yang gerry
  480. 10 7
  481. 7 10
  482. 0 0 0 false false false
  483. 1 2 3 true false no!
  484. 1 2 3 true false yes!
  485. bool(false)
  486. uint64(18446744073709551615)
  487. complex128((2+3i))
  488. 3.14
  489. 你好 世界
  490. 世界 和平
  491. Go rules? true
  492. 21
  493. 0.2
  494. 1.2676506002282295e+29
  495. 45
  496. 1024
  497. leave out an infinite loop
  498. 1.4142135623730951 2i
  499. 9 20
  500. 27 >= 20
  501. 9 20
  502. {1 2}
  503. {4 2}
  504. {1000000000 2}
  505. {1 2} &{1 2} {1 0} {0 0}
  506. &{0 0}
  507. &{11 9}
  508. as == [2 3 5 7 11 13]
  509. as[0] == 2
  510. as[1] == 3
  511. as[2] == 5
  512. as[3] == 7
  513. as[4] == 11
  514. as[5] == 13
  515. as[1:4] == [3 5 7]
  516. as[:3] == [2 3 5]
  517. as[4:] == [11 13]
  518. s1 len = 5 cap = 5 [0 0 0 0 0]
  519. s2 len = 0 cap = 5 []
  520. s3 len = 2 cap = 5 [0 0]
  521. s4 len = 3 cap = 3 [0 0 0]
  522. [] 0 0
  523. slice is nil
  524. 2**0 = 1
  525. 2**1 = 2
  526. 2**2 = 4
  527. 2**3 = 8
  528. 2**4 = 16
  529. 2**5 = 32
  530. 2**6 = 64
  531. 2**7 = 128
  532. 2**8 = 256
  533. 2**9 = 512
  534. 2**10 = 1024
  535. 1
  536. 2
  537. 4
  538. 8
  539. 16
  540. 32
  541. 64
  542. 128
  543. 256
  544. 512
  545. 1024
  546. {40.68433 -74.39967}
  547. map[gerryyang:{100 200} wcdj:{-300 500}]
  548. map[math:{20 40} computer:{30 50}]
  549. The value: 20131129
  550. The value: 20131130
  551. The value: 20131130 Present? true
  552. The value: 0
  553. The value: 0 Present? false
  554. 5
  555. 0 0
  556. 1 -2
  557. 3 -6
  558. 6 -12
  559. 10 -20
  560. 15 -30
  561. 21 -42
  562. 28 -56
  563. 36 -72
  564. 45 -90
  565. 1
  566. 2
  567. 3
  568. 5
  569. 8
  570. 13
  571. 21
  572. 34
  573. 55
  574. 89
  575. OS X
  576. When's Saturday?
  577. In three days
  578. Good evening
  579. 5
  580. f < 0 here
  581. 1.4142135623730951
  582. &{15 20} 25
  583. 5
  584. hello, writer
  585. at 2013年12月04日 22:52:01.337206342 +0800 HKT, it didn't work
  586. */
原文出自:http://blog.csdn.net/delphiwcdj/article/details/16903649

有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:newsyoung1

查看原文:Go语言

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
6263 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏