├── .github └── workflows │ └── test.yml ├── .gitignore ├── CONTRIBUTING.md ├── README.md ├── examples.txt ├── examples ├── arrays │ ├── arrays.go │ ├── arrays.hash │ └── arrays.sh ├── atomic-counters │ ├── atomic-counters.go │ ├── atomic-counters.hash │ └── atomic-counters.sh ├── base64-encoding │ ├── base64-encoding.go │ ├── base64-encoding.hash │ └── base64-encoding.sh ├── channel-buffering │ ├── channel-buffering.go │ ├── channel-buffering.hash │ └── channel-buffering.sh ├── channel-directions │ ├── channel-directions.go │ ├── channel-directions.hash │ └── channel-directions.sh ├── channel-synchronization │ ├── channel-synchronization.go │ ├── channel-synchronization.hash │ └── channel-synchronization.sh ├── channels │ ├── channels.go │ ├── channels.hash │ └── channels.sh ├── closing-channels │ ├── closing-channels.go │ ├── closing-channels.hash │ └── closing-channels.sh ├── closures │ ├── closures.go │ ├── closures.hash │ └── closures.sh ├── command-line-arguments │ ├── command-line-arguments.go │ ├── command-line-arguments.hash │ └── command-line-arguments.sh ├── command-line-flags │ ├── command-line-flags.go │ ├── command-line-flags.hash │ └── command-line-flags.sh ├── command-line-subcommands │ ├── command-line-subcommands.go │ ├── command-line-subcommands.hash │ └── command-line-subcommands.sh ├── constants │ ├── constants.go │ ├── constants.hash │ └── constants.sh ├── context │ ├── context.go │ ├── context.hash │ └── context.sh ├── defer │ ├── defer.go │ ├── defer.hash │ └── defer.sh ├── directories │ ├── directories.go │ ├── directories.hash │ └── directories.sh ├── embed-directive │ ├── embed-directive.go │ ├── embed-directive.hash │ ├── embed-directive.sh │ └── folder │ │ ├── file1.hash │ │ ├── file2.hash │ │ └── single_file.txt ├── environment-variables │ ├── environment-variables.go │ ├── environment-variables.hash │ └── environment-variables.sh ├── epoch │ ├── epoch.go │ ├── epoch.hash │ └── epoch.sh ├── errors │ ├── errors.go │ ├── errors.hash │ └── errors.sh ├── execing-processes │ ├── execing-processes.go │ ├── execing-processes.hash │ └── execing-processes.sh ├── exit │ ├── exit.go │ ├── exit.hash │ └── exit.sh ├── file-paths │ ├── file-paths.go │ ├── file-paths.hash │ └── file-paths.sh ├── for │ ├── for.go │ ├── for.hash │ └── for.sh ├── functions │ ├── functions.go │ ├── functions.hash │ └── functions.sh ├── generics │ ├── generics.go │ ├── generics.hash │ └── generics.sh ├── goroutines │ ├── goroutines.go │ ├── goroutines.hash │ └── goroutines.sh ├── hello-world │ ├── hello-world.go │ ├── hello-world.hash │ └── hello-world.sh ├── http-client │ ├── http-client.go │ ├── http-client.hash │ └── http-client.sh ├── http-server │ ├── http-server.go │ ├── http-server.hash │ └── http-server.sh ├── if-else │ ├── if-else.go │ ├── if-else.hash │ └── if-else.sh ├── interfaces │ ├── interfaces.go │ ├── interfaces.hash │ └── interfaces.sh ├── json │ ├── json.go │ ├── json.hash │ └── json.sh ├── line-filters │ ├── line-filters.go │ ├── line-filters.hash │ └── line-filters.sh ├── maps │ ├── maps.go │ ├── maps.hash │ └── maps.sh ├── methods │ ├── methods.go │ ├── methods.hash │ └── methods.sh ├── multiple-return-values │ ├── multiple-return-values.go │ ├── multiple-return-values.hash │ └── multiple-return-values.sh ├── mutexes │ ├── mutexes.go │ ├── mutexes.hash │ └── mutexes.sh ├── non-blocking-channel-operations │ ├── non-blocking-channel-operations.go │ ├── non-blocking-channel-operations.hash │ └── non-blocking-channel-operations.sh ├── number-parsing │ ├── number-parsing.go │ ├── number-parsing.hash │ └── number-parsing.sh ├── panic │ ├── panic.go │ ├── panic.hash │ └── panic.sh ├── pointers │ ├── pointers.go │ ├── pointers.hash │ └── pointers.sh ├── random-numbers │ ├── random-numbers.go │ ├── random-numbers.hash │ └── random-numbers.sh ├── range-over-channels │ ├── range-over-channels.go │ ├── range-over-channels.hash │ └── range-over-channels.sh ├── range │ ├── range.go │ ├── range.hash │ └── range.sh ├── rate-limiting │ ├── rate-limiting.go │ ├── rate-limiting.hash │ └── rate-limiting.sh ├── reading-files │ ├── reading-files.go │ ├── reading-files.hash │ └── reading-files.sh ├── recover │ ├── recover.go │ ├── recover.hash │ └── recover.sh ├── recursion │ ├── recursion.go │ ├── recursion.hash │ └── recursion.sh ├── regular-expressions │ ├── regular-expressions.go │ ├── regular-expressions.hash │ └── regular-expressions.sh ├── select │ ├── select.go │ ├── select.hash │ └── select.sh ├── sha256-hashes │ ├── sha256-hashes.go │ ├── sha256-hashes.hash │ └── sha256-hashes.sh ├── signals │ ├── signals.go │ ├── signals.hash │ └── signals.sh ├── slices │ ├── slices.go │ ├── slices.hash │ └── slices.sh ├── sorting-by-functions │ ├── sorting-by-functions.go │ ├── sorting-by-functions.hash │ └── sorting-by-functions.sh ├── sorting │ ├── sorting.go │ ├── sorting.hash │ └── sorting.sh ├── spawning-processes │ ├── spawning-processes.go │ ├── spawning-processes.hash │ └── spawning-processes.sh ├── stateful-goroutines │ ├── stateful-goroutines.go │ ├── stateful-goroutines.hash │ └── stateful-goroutines.sh ├── string-formatting │ ├── string-formatting.go │ ├── string-formatting.hash │ └── string-formatting.sh ├── string-functions │ ├── string-functions.go │ ├── string-functions.hash │ └── string-functions.sh ├── strings-and-runes │ ├── strings-and-runes.go │ ├── strings-and-runes.hash │ └── strings-and-runes.sh ├── struct-embedding │ ├── struct-embedding.go │ ├── struct-embedding.hash │ └── struct-embedding.sh ├── structs │ ├── structs.go │ ├── structs.hash │ └── structs.sh ├── switch │ ├── switch.go │ ├── switch.hash │ └── switch.sh ├── temporary-files-and-directories │ ├── temporary-files-and-directories.go │ ├── temporary-files-and-directories.hash │ └── temporary-files-and-directories.sh ├── testing-and-benchmarking │ ├── main_test.go │ ├── main_test.sh │ └── testing-and-benchmarking.hash ├── text-templates │ ├── text-templates.go │ ├── text-templates.hash │ └── text-templates.sh ├── tickers │ ├── tickers.go │ ├── tickers.hash │ └── tickers.sh ├── time-formatting-parsing │ ├── time-formatting-parsing.go │ ├── time-formatting-parsing.hash │ └── time-formatting-parsing.sh ├── time │ ├── time.go │ ├── time.hash │ └── time.sh ├── timeouts │ ├── timeouts.go │ ├── timeouts.hash │ └── timeouts.sh ├── timers │ ├── timers.go │ ├── timers.hash │ └── timers.sh ├── url-parsing │ ├── url-parsing.go │ ├── url-parsing.hash │ └── url-parsing.sh ├── values │ ├── values.go │ ├── values.hash │ └── values.sh ├── variables │ ├── variables.go │ ├── variables.hash │ └── variables.sh ├── variadic-functions │ ├── variadic-functions.go │ ├── variadic-functions.hash │ └── variadic-functions.sh ├── waitgroups │ ├── waitgroups.go │ ├── waitgroups.hash │ └── waitgroups.sh ├── worker-pools │ ├── worker-pools.go │ ├── worker-pools.hash │ └── worker-pools.sh ├── writing-files │ ├── writing-files.go │ ├── writing-files.hash │ └── writing-files.sh └── xml │ ├── xml.go │ ├── xml.hash │ └── xml.sh ├── go.mod ├── go.sum ├── public ├── 404.html ├── arrays.html ├── atomic-counters.html ├── base64-encoding.html ├── channel-buffering.html ├── channel-directions.html ├── channel-synchronization.html ├── channels.html ├── clipboard.png ├── closing-channels.html ├── closures.html ├── command-line-arguments.html ├── command-line-flags.html ├── command-line-subcommands.html ├── constants.html ├── context.html ├── defer.html ├── directories.html ├── embed-directive.html ├── environment-variables.html ├── epoch.html ├── errors.html ├── execing-processes.html ├── exit.html ├── favicon.ico ├── file-paths.html ├── for.html ├── functions.html ├── generics.html ├── goroutines.html ├── hello-world.html ├── http-client.html ├── http-server.html ├── if-else.html ├── index.html ├── interfaces.html ├── json.html ├── line-filters.html ├── maps.html ├── methods.html ├── multiple-return-values.html ├── mutexes.html ├── non-blocking-channel-operations.html ├── number-parsing.html ├── panic.html ├── play.png ├── pointers.html ├── random-numbers.html ├── range-over-channels.html ├── range.html ├── rate-limiting.html ├── reading-files.html ├── recover.html ├── recursion.html ├── regular-expressions.html ├── select.html ├── sha256-hashes.html ├── signals.html ├── site.css ├── site.js ├── slices.html ├── sorting-by-functions.html ├── sorting.html ├── spawning-processes.html ├── stateful-goroutines.html ├── string-formatting.html ├── string-functions.html ├── strings-and-runes.html ├── struct-embedding.html ├── structs.html ├── switch.html ├── temporary-files-and-directories.html ├── testing-and-benchmarking.html ├── text-templates.html ├── tickers.html ├── time-formatting-parsing.html ├── time.html ├── timeouts.html ├── timers.html ├── url-parsing.html ├── values.html ├── variables.html ├── variadic-functions.html ├── waitgroups.html ├── worker-pools.html ├── writing-files.html └── xml.html ├── templates ├── 404.tmpl ├── clipboard.png ├── example.tmpl ├── favicon.ico ├── footer.tmpl ├── index.tmpl ├── play.png ├── site.css └── site.js └── tools ├── build ├── build-loop ├── format ├── generate ├── generate.go ├── measure ├── measure.go ├── serve ├── serve.go ├── test ├── upload └── upload.go /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | 11 | build: 12 | strategy: 13 | matrix: 14 | go-version: [1.19.1] 15 | 16 | runs-on: self-hosted 17 | steps: 18 | - uses: actions/checkout@v2 19 | 20 | - name: Set up Go 21 | uses: actions/setup-go@v3 22 | with: 23 | go-version: ${{ matrix.go-version }} 24 | 25 | - name: Test 26 | run: tools/build 27 | env: 28 | VERBOSE: 1 29 | TESTING: 1 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .idea 3 | .vscode 4 | .history -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Đóng góp cho dự án 2 | 3 | Cám ơn bạn vì đã quan tâm đến việc đóng góp bản dịch cho Go by Example! 4 | 5 | Sau đây là một vài hướng dẫn đóng góp bản dịch cho repo này: 6 | 7 | - Hãy đảm bảo rằng bạn hãy fork repository này và tạo PR khi thực hiện các bản dịch. 8 | - Đóng góp được thực hiện thông qua việc dịch dịch comment của các file `*.go` và `*.sh` trong thư mục examples. 9 | - Hãy tạo issue và list các file muốn dịch trước khi tạo PR, khi tạo PR thì hãy link với issue đã tạo bằng cách mô tả trong PR theo cú pháp sau: `Closes: #`. 10 | - Sau khi chủ sở hữu repo và cộng đồng review và approve PR của bạn thì PR sẽ được merge vào main. 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Về repo này 2 | 3 | Phiên bản tiếng Việt của https://gobyexample.com/ - Học lập trình Go thông qua các ví dụ. 4 | 5 | Phiên bản tiếng Anh gốc ở https://github.com/mmcgrana/gobyexample 6 | 7 | # Cách sử dụng repo 8 | 9 | Nội dụng và các công cụ để build [Go by Example](https://gobyexample.com), 10 | trang web hướng dẫn học Go thông qua các ví dụ có chú thích. 11 | 12 | ### Tổng quan 13 | 14 | Trang Go by example được built bằng cách tách phần code và phần 15 | chú thích khỏi các file nguồn trong thư mục `examples` và render ra 16 | các trang tĩnh bên trong thư mục `public` bằng cách sử dụng thư mục `template`. 17 | Các chương trình thực thi quy trình trên nằm ở thư mục `tools`, cùng chỗ với các dependencies 18 | được chỉ định trong file `go.mod` 19 | 20 | Thư mục `public` sau khi build xong có thể được serve bằng các hệ thống static content system. 21 | Ví dụ như trang production có thể sử dụng S3 và CloudFront. 22 | 23 | ### Building 24 | 25 | [![test](https://github.com/mmcgrana/gobyexample/actions/workflows/test.yml/badge.svg)](https://github.com/mmcgrana/gobyexample/actions/workflows/test.yml) 26 | 27 | Để có thể build trang web bạn cần phải cài đặt Go. Sau đó chạy: 28 | 29 | ```console 30 | $ tools/build 31 | ``` 32 | 33 | Để có thể build liên tục: 34 | 35 | ```console 36 | $ tools/build-loop 37 | ``` 38 | 39 | Để xem trang web ở local: 40 | 41 | ``` 42 | $ tools/serve 43 | ``` 44 | 45 | và đi đến `http://127.0.0.1:8000/` ở trong trình duyệt của bạn. 46 | 47 | ### Publishing 48 | 49 | Để upload trang: 50 | 51 | ```console 52 | $ export AWS_ACCESS_KEY_ID=... 53 | $ export AWS_SECRET_ACCESS_KEY=... 54 | $ tools/upload 55 | ``` 56 | 57 | ### Bản quyền 58 | 59 | Bản quyền của công trình này thuộc về Mark McGranaghan và được cấp giấy phép bởi 60 | [Creative Commons Attribution 3.0 Unported License](http://creativecommons.org/licenses/by/3.0/). 61 | 62 | Bản quyền hình ảnh Go Gopher thuộc về [Renée French](https://reneefrench.blogspot.com/) và được cấp giấy phép bởi 63 | [Creative Commons Attribution 3.0 Unported License](http://creativecommons.org/licenses/by/3.0/). 64 | 65 | ### Translations 66 | 67 | Các phiên bản dịch khác của Go by Example có thể tìm tại các trang sau: 68 | 69 | - [Chinese](https://gobyexample-cn.github.io/) dịch bởi [gobyexample-cn](https://github.com/gobyexample-cn) 70 | - [French](http://le-go-par-l-exemple.keiruaprod.fr) dịch bởi [keirua](https://github.com/keirua/gobyexample) 71 | - [Italian](https://gobyexample.it) dịch bởi [Go Italian community](https://github.com/golangit/gobyexample-it) 72 | - [Japanese](http://spinute.org/go-by-example) dịch bởi [spinute](https://github.com/spinute) 73 | - [Korean](https://mingrammer.com/gobyexample/) dịch bởi [mingrammer](https://github.com/mingrammer) 74 | - [Russian](https://gobyexample.com.ru/) dịch bởi [badkaktus](https://github.com/badkaktus) 75 | - [Ukrainian](https://butuzov.github.io/gobyexample/) dịch bởi [butuzov](https://github.com/butuzov/gobyexample) 76 | - [Brazilian Portuguese](https://lcslitx.github.io/GoEmExemplos/) dịch bởi [lcslitx](https://github.com/LCSLITX) 77 | -------------------------------------------------------------------------------- /examples.txt: -------------------------------------------------------------------------------- 1 | Hello World 2 | Values 3 | Variables 4 | Constants 5 | For 6 | If/Else 7 | Switch 8 | Arrays 9 | Slices 10 | Maps 11 | Range 12 | Functions 13 | Multiple Return Values 14 | Variadic Functions 15 | Closures 16 | Recursion 17 | Pointers 18 | Strings and Runes 19 | Structs 20 | Methods 21 | Interfaces 22 | Struct Embedding 23 | Generics 24 | Errors 25 | Goroutines 26 | Channels 27 | Channel Buffering 28 | Channel Synchronization 29 | Channel Directions 30 | Select 31 | Timeouts 32 | Non-Blocking Channel Operations 33 | Closing Channels 34 | Range over Channels 35 | Timers 36 | Tickers 37 | Worker Pools 38 | WaitGroups 39 | Rate Limiting 40 | Atomic Counters 41 | Mutexes 42 | Stateful Goroutines 43 | Sorting 44 | Sorting by Functions 45 | Panic 46 | Defer 47 | Recover 48 | String Functions 49 | String Formatting 50 | Text Templates 51 | Regular Expressions 52 | JSON 53 | XML 54 | Time 55 | Epoch 56 | Time Formatting / Parsing 57 | Random Numbers 58 | Number Parsing 59 | URL Parsing 60 | SHA256 Hashes 61 | Base64 Encoding 62 | Reading Files 63 | Writing Files 64 | Line Filters 65 | File Paths 66 | Directories 67 | Temporary Files and Directories 68 | Embed Directive 69 | Testing and Benchmarking 70 | Command-Line Arguments 71 | Command-Line Flags 72 | Command-Line Subcommands 73 | Environment Variables 74 | HTTP Client 75 | HTTP Server 76 | Context 77 | Spawning Processes 78 | Exec'ing Processes 79 | Signals 80 | Exit 81 | -------------------------------------------------------------------------------- /examples/arrays/arrays.go: -------------------------------------------------------------------------------- 1 | // Trong Go, một _array_ là một chuỗi các phần tử được đánh số của một 2 | // độ dài cụ thể. Trong Go, [slices](còn được gọi là "lát") 3 | // phổ biến hơn nhiều; array rất hữu ích trong một số trường hợp đặc biệt. 4 | package main 5 | 6 | import "fmt" 7 | 8 | func main() { 9 | // Ở đây chúng ta tạo ra một array `a` gồm chính xác 10 | // 5 phần tử có loại int. Loại của các phần tử và độ dài 11 | // đều thuộc về đặc tính của array. Mặc định, một array rỗng 12 | // có giá trị zero (zero-valued), có thể hiểu `int` có nghĩa là `0`. 13 | var a [5]int 14 | fmt.Println("emp:", a) 15 | 16 | // Chúng ta có thể gán một giá trị tại một chỉ mục (index) bằng cách 17 | // sử dụng `array[index] = value` và lấy giá trị tại chỉ mục đó bằng cách 18 | // sử dụng `array[index]`. 19 | a[4] = 100 20 | fmt.Println("set:", a) 21 | fmt.Println("get:", a[4]) 22 | 23 | // Hàm `len` trả lại độ dài array. 24 | fmt.Println("len:", len(a)) 25 | 26 | // Sử dụng cú pháp này để khai báo và khởi tạo một array. 27 | b := [5]int{1, 2, 3, 4, 5} 28 | fmt.Println("dcl:", b) 29 | 30 | // Array là array một chiều, nhưng bạn có thể kết hợp 31 | // để tạo ra một cấu trúc dữ liệu đa chiều. 32 | var twoD [2][3]int 33 | for i := 0; i < 2; i++ { 34 | for j := 0; j < 3; j++ { 35 | twoD[i][j] = i + j 36 | } 37 | } 38 | fmt.Println("2d: ", twoD) 39 | } 40 | -------------------------------------------------------------------------------- /examples/arrays/arrays.hash: -------------------------------------------------------------------------------- 1 | 5a00ef1fe8c788e71c1f42035efb7155ed5bf7ce 2 | WDVuzInvlOf 3 | -------------------------------------------------------------------------------- /examples/arrays/arrays.sh: -------------------------------------------------------------------------------- 1 | # Lưu ý rằng mảng xuất hiện dưới dạng `[v1 v2 v3 ...]` 2 | # khi được in ra với `fmt.Println`. 3 | $ go run arrays.go 4 | emp: [0 0 0 0 0] 5 | set: [0 0 0 0 100] 6 | get: 100 7 | len: 5 8 | dcl: [1 2 3 4 5] 9 | 2d: [[0 1 2] [1 2 3]] 10 | -------------------------------------------------------------------------------- /examples/atomic-counters/atomic-counters.go: -------------------------------------------------------------------------------- 1 | // Cơ chế mặc định để quản lý state trong Go là giao tiếp 2 | // qua channels. Chúng ta đã thấy điều này ở ví dụ với 3 | // [worker pools](worker-pools). Ngoài ra còn có một vài 4 | // lựa chọn khác để quản lý state. Ở đây chúng ta sẽ 5 | // xem xét việc sử dụng package `sync/atomic` cho _các biến đếm 6 | // nguyên tử (atomic counters)_ được truy cập bởi nhiều goroutines. 7 | 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "sync" 13 | "sync/atomic" 14 | ) 15 | 16 | func main() { 17 | 18 | // Ta sẽ dùng một số nguyên không dấu để biểu diễn 19 | // biến đếm (luôn dương). 20 | var ops uint64 21 | 22 | // Một WaitGroup sẽ giúp chúng ta đợi cho tất cả 23 | // các goroutine hoàn thành việc thực thi. 24 | var wg sync.WaitGroup 25 | 26 | // Ta sẽ khởi tạo 50 goroutine mà mỗi goroutine sẽ 27 | // tăng biến đếm lên 1000 lần. 28 | for i := 0; i < 50; i++ { 29 | wg.Add(1) 30 | 31 | go func() { 32 | for c := 0; c < 1000; c++ { 33 | // Để tự động tăng biến đếm, ta sử dụng 34 | // `AddUint64`, truyền vào địa chỉ vùng nhớ 35 | // của biến đếm `ops` với 36 | // cú pháp `&`. 37 | atomic.AddUint64(&ops, 1) 38 | } 39 | wg.Done() 40 | }() 41 | } 42 | 43 | // Chờ cho tất cả các goroutine hoàn thành. 44 | wg.Wait() 45 | 46 | // Bây giờ ta có thể an toàn truy cập vào `ops` vì ta 47 | // biết rằng không có goroutine nào khác đang ghi vào nó. 48 | // Việc đọc các biến đếm nguyên tử một cách an toàn trong 49 | // khi chúng đang được cập nhật cũng khả thi, sử dụng các 50 | // hàm như `atomic.LoadUint64`. 51 | fmt.Println("ops:", ops) 52 | } 53 | -------------------------------------------------------------------------------- /examples/atomic-counters/atomic-counters.hash: -------------------------------------------------------------------------------- 1 | e9a8ad21f777cb767002ae1eed1ae5854390534b 2 | vWJEiOQIZ63 3 | -------------------------------------------------------------------------------- /examples/atomic-counters/atomic-counters.sh: -------------------------------------------------------------------------------- 1 | # Ta mong đợi sẽ nhận được chính xác 50,000 phép tính. 2 | # Nếu chúng ta sử dụng phép tính không đảm bảo 3 | # tính toàn vẹn dữ liệu như `ops++` để tăng biến đếm, 4 | # ta có thể nhận được một kết quả khác, thay đổi giữa 5 | # các lần chạy, bởi vì các goroutines sẽ can thiệp lẫn 6 | # nhau. Hơn nữa, ta sẽ dính các lỗi về chạy đua dữ liệu 7 | # (data race) khi sử dụng flag `-race`. 8 | $ go run atomic-counters.go 9 | ops: 50000 10 | 11 | # Tiếp theo, chúng ta sẽ xem xét mutex, một công cụ 12 | # khác để quản lý state. -------------------------------------------------------------------------------- /examples/base64-encoding/base64-encoding.go: -------------------------------------------------------------------------------- 1 | // Go cung cấp hỗ trợ tích hợp sẵn cho [base64 2 | // encoding (mã hóa)/decoding (giải mã)](https://en.wikipedia.org/wiki/Base64). 3 | 4 | package main 5 | 6 | // Cú pháp này imports `encoding/base64` package (gói) với 7 | // tên `b64` thay vì mặc định `base64`. Nó sẽ 8 | // tiết kiệm cho chúng ta không gian. 9 | import ( 10 | b64 "encoding/base64" 11 | "fmt" 12 | ) 13 | 14 | func main() { 15 | 16 | // Đây là chuỗi chúng ta sẽ mã hóa/giải mã. 17 | data := "abc123!?$*&()'-=@~" 18 | 19 | // Go hỗ trợ của base64 chuẩn và URL-compatible 20 | // Đây là cách mã hóa sử dụng bộ mã hóa chuẩn. 21 | // Bộ mã hóa yêu cầu một `[]byte` nên chúng ta 22 | // chuyển đổi chuỗi của chúng ta thành loại đó. 23 | sEnc := b64.StdEncoding.EncodeToString([]byte(data)) 24 | fmt.Println(sEnc) 25 | 26 | // Giải mã có thể trả về lỗi, bạn có thể kiểm tra 27 | // nếu bạn không biết đầu vào đã được định dạng tốt. 28 | sDec, _ := b64.StdEncoding.DecodeString(sEnc) 29 | fmt.Println(string(sDec)) 30 | fmt.Println() 31 | 32 | // Đây là cách mã hóa/giải mã sử dụng định dạng base64 33 | // tương thích với URL. 34 | uEnc := b64.URLEncoding.EncodeToString([]byte(data)) 35 | fmt.Println(uEnc) 36 | uDec, _ := b64.URLEncoding.DecodeString(uEnc) 37 | fmt.Println(string(uDec)) 38 | } 39 | -------------------------------------------------------------------------------- /examples/base64-encoding/base64-encoding.hash: -------------------------------------------------------------------------------- 1 | 884b0820e939e222c3ba5d778b5667879600d6f2 2 | 0CLX1lK2y16 3 | -------------------------------------------------------------------------------- /examples/base64-encoding/base64-encoding.sh: -------------------------------------------------------------------------------- 1 | # Chuỗi được mã hóa thành các giá trị khác nhau với các 2 | # mã hóa base64 chuẩn và URL (đuôi `+` vs `-`) 3 | # nhưng chúng đều được giải mã thành chuỗi ban đầu 4 | # như mong muốn 5 | $ go run base64-encoding.go 6 | YWJjMTIzIT8kKiYoKSctPUB+ 7 | abc123!?$*&()'-=@~ 8 | 9 | YWJjMTIzIT8kKiYoKSctPUB- 10 | abc123!?$*&()'-=@~ -------------------------------------------------------------------------------- /examples/channel-buffering/channel-buffering.go: -------------------------------------------------------------------------------- 1 | // Mặc định channel là _unbufferred_, có nghĩa là chúng 2 | // sẽ chỉ chấp nhận gửi (`chan <-`) nếu có một channel 3 | // nhận tương ứng (`<- chan`) sẵn sàng để nhận giá trị 4 | // được gửi. _Buffered channels_ chấp nhận một số lượng 5 | // giá trị mà không cần channel nhận tương ứng với 6 | // những giá trị đấy. 7 | 8 | package main 9 | 10 | import "fmt" 11 | 12 | func main() { 13 | // Ở đây, chúng ta `tạo` một channel những chuỗi 14 | // với buffer lên đến 2 giá trị. 15 | messages := make(chan string, 2) 16 | 17 | // Bởi vì channel này là buffered, chúng ta có thể 18 | // gửi những giá trị vào channel mà không cần một 19 | // channel nhận đồng thời tương ứng. 20 | messages <- "buffered" 21 | messages <- "channel" 22 | 23 | // Sau đó chúng ta có thể nhận 2 giá trị như bình 24 | // thường. 25 | fmt.Println(<-messages) 26 | fmt.Println(<-messages) 27 | } 28 | -------------------------------------------------------------------------------- /examples/channel-buffering/channel-buffering.hash: -------------------------------------------------------------------------------- 1 | f7f36d984b42b14a2619114678174526edbad469 2 | Bg17lCZyt1W 3 | -------------------------------------------------------------------------------- /examples/channel-buffering/channel-buffering.sh: -------------------------------------------------------------------------------- 1 | $ go run channel-buffering.go 2 | buffered 3 | channel -------------------------------------------------------------------------------- /examples/channel-directions/channel-directions.go: -------------------------------------------------------------------------------- 1 | // Khi gửi dụng channel như tham số của hàm, bạn có thể 2 | // chỉ định nếu một channel được định nghĩa là chỉ nhận 3 | // hay gửi giá trị. Tính đặc hiệu này làm tăng an toà 4 | // của kiểu dữ liệu của một chương trình. 5 | 6 | package main 7 | 8 | import "fmt" 9 | 10 | // Hàm `ping` này chỉ chấp nhận một channel cho việc gửi 11 | // giá trị. Có thể bị lỗi compile-time khi cố nhận từ 12 | // channel này. 13 | func ping(pings chan<- string, msg string) { 14 | pings <- msg 15 | } 16 | 17 | // Hàm `pong` chấp nhận một channel nhận và channel thứ 18 | // hai cho việc gửi (`pongs`). 19 | func pong(pings <-chan string, pongs chan<- string) { 20 | msg := <-pings 21 | pongs <- msg 22 | } 23 | 24 | func main() { 25 | pings := make(chan string, 1) 26 | pongs := make(chan string, 1) 27 | ping(pings, "passed message") 28 | pong(pings, pongs) 29 | fmt.Println(<-pongs) 30 | } 31 | -------------------------------------------------------------------------------- /examples/channel-directions/channel-directions.hash: -------------------------------------------------------------------------------- 1 | 307ec5469056da930b1f606f7331c34adc739afd 2 | aDmspn2LYnM 3 | -------------------------------------------------------------------------------- /examples/channel-directions/channel-directions.sh: -------------------------------------------------------------------------------- 1 | $ go run channel-directions.go 2 | passed message 3 | -------------------------------------------------------------------------------- /examples/channel-synchronization/channel-synchronization.go: -------------------------------------------------------------------------------- 1 | // Chúng ta có thể sử dụng channel để đồng bộ hóa thực 2 | // thi giữa các goroutine. Đây là một ví dụ sử dụng 3 | // đồng bộ hóa nhận để đợi một goroutine hoàn thành. 4 | // Khi đợi nhiều goroutine hoàn thành, chúng ta có thể 5 | // sử dụng một [WaitGroup](waitgroups). 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "time" 12 | ) 13 | 14 | // Đây là hàm chúng ta sẽ chạy trong một goroutine. 15 | // Channel `done` sẽ được sử dụng để thông báo đến 16 | // một goroutine khác rằng hàm này đã chạy xong. 17 | func worker(done chan bool) { 18 | fmt.Print("working...") 19 | time.Sleep(time.Second) 20 | fmt.Println("done") 21 | 22 | // Gửi một giá trị để thông báo rằng chúng ta 23 | // đã xong 24 | done <- true 25 | } 26 | 27 | func main() { 28 | // Khởi tọa một worker goroutine, cho rằng nó là là 29 | // channel để nhận thông báo. 30 | done := make(chan bool, 1) 31 | go worker(done) 32 | 33 | // Chặn cho đến khi chúng ta nhận được một thông báo 34 | // từ worker trên channel. 35 | <-done 36 | } 37 | -------------------------------------------------------------------------------- /examples/channel-synchronization/channel-synchronization.hash: -------------------------------------------------------------------------------- 1 | 0f19e3c97c35056da8237b7d78be8ba3c71e69f6 2 | ylXnBcck7qe 3 | -------------------------------------------------------------------------------- /examples/channel-synchronization/channel-synchronization.sh: -------------------------------------------------------------------------------- 1 | $ go run channel-synchronization.go 2 | working...done 3 | 4 | # Nếu bạn đã xóa dòng `<-done` khỏi chương trình, 5 | # chương trình sẽ thoát trước khi `worker` mặc dù vẫn 6 | # khởi chạy. 7 | -------------------------------------------------------------------------------- /examples/channels/channels.go: -------------------------------------------------------------------------------- 1 | // _Channels_ là những ống mà kết nối những goroutine 2 | // đồng thời. Bạn có thể gửi những giá trị vào trong 3 | // những channel từ một goroutine và nhận những giá trị 4 | // trong một goroutine khác. 5 | 6 | package main 7 | 8 | import "fmt" 9 | 10 | func main() { 11 | // Tạo một channel mới với `make(chan val-type)`. 12 | // Những channel được nhập theo các giá trị mà 13 | // chúng truyền tải. 14 | messages := make(chan string) 15 | 16 | // _Gửi_ một giá trị vào trong một channel sử dụng 17 | // cú pháp the `channel <-`. Ở đây, chúng ta send 18 | // `"ping"` đến `messages` channel chúng ta tạo ra ở 19 | // trên, từ một goroutine mới. 20 | go func() { messages <- "ping" }() 21 | 22 | // Cú pháp `<-channel` _nhận_ một giá trị từ channel. 23 | // Ở dây, chúng ta sẽ nhận được gói tin `"ping"` mà 24 | // chúng ta đã gửi trước đó và in chúng ra. 25 | msg := <-messages 26 | fmt.Println(msg) 27 | } 28 | -------------------------------------------------------------------------------- /examples/channels/channels.hash: -------------------------------------------------------------------------------- 1 | 12e91f84960e18df830a59cc9705de0da3b2eece 2 | N4paeziCNlS 3 | -------------------------------------------------------------------------------- /examples/channels/channels.sh: -------------------------------------------------------------------------------- 1 | # Khi chúng ra chạy chương trình gói tin `"ping"` 2 | # được truyền thành công từ một goroutine đến một 3 | # goroutine khác thông qua channel của chúng ta. 4 | 5 | $ go run channels.go 6 | ping 7 | 8 | # Mặc định gửi và nhận bị chặn cho đến khi cả 9 | # channel gửi và nhận sẵn sàng. Thuộc tính này cho phép 10 | # chúng ta đợi gói tin `"ping"` ở cuối chương trình mà 11 | # không phải sử dụng bất kì sự đồng bộ hóa nào khác. 12 | -------------------------------------------------------------------------------- /examples/closing-channels/closing-channels.go: -------------------------------------------------------------------------------- 1 | // _Closing_ Đóng một channel nghĩa là sẽ không có giá trị 2 | // nào được gửi vào channel nữa. Điều này có thể dùng để 3 | // xác định việc hoàn thành thao tác nhận giá trị 4 | // từ channel. 5 | 6 | package main 7 | 8 | import "fmt" 9 | 10 | // Trong ví dụ này, chúng ta sẽ sử dụng một channel `jobs` 11 | // để truyền `job` cần thực hiện từ `main()` đến một 12 | // `worker`. Khi không còn `job` nào cho `worker` thực 13 | // hiện thì chúng ta sẽ dùng `close` để đóng 14 | // channel `jobs`. 15 | func main() { 16 | jobs := make(chan int, 5) 17 | done := make(chan bool) 18 | 19 | // Dưới đây là một ví dụ về dùng vòng lặp trong `worker` 20 | // nhận giá trị từ `jobs` với `j, more := <-jobs`. 21 | // Trong trường hợp không còn `job` mới, giá trị `more` 22 | // sẽ là `false` nếu `jobs` đã được `close` và tất cả 23 | // các giá trị trong channel đã được nhận. Chúng ta sử 24 | // dụng điều này để thông báo cho `done` khi `worker` 25 | // đã hoàn thành việc nhận tất cả `job`. 26 | go func() { 27 | for { 28 | j, more := <-jobs 29 | if more { 30 | fmt.Println("received job", j) 31 | } else { 32 | fmt.Println("received all jobs") 33 | done <- true 34 | return 35 | } 36 | } 37 | }() 38 | 39 | // Có 3 `jobs` được gửi đến worker thông qua channel 40 | // `jobs`, sau đó channel `jobs` sẽ được đóng. 41 | for j := 1; j <= 3; j++ { 42 | jobs <- j 43 | fmt.Println("sent job", j) 44 | } 45 | close(jobs) 46 | fmt.Println("sent all jobs") 47 | 48 | // Chúng ta chờ worker thực hiện công việc thông qua 49 | // phương pháp [synchronization](channel-synchronization) 50 | // chúng ta đã có ở ví dụ trước. 51 | <-done 52 | } 53 | -------------------------------------------------------------------------------- /examples/closing-channels/closing-channels.hash: -------------------------------------------------------------------------------- 1 | 218a6c2814fc661c2d343472e9664593ce107b3f 2 | SX2KMj1Ci1y 3 | -------------------------------------------------------------------------------- /examples/closing-channels/closing-channels.sh: -------------------------------------------------------------------------------- 1 | $ go run closing-channels.go 2 | sent job 1 3 | received job 1 4 | sent job 2 5 | received job 2 6 | sent job 3 7 | received job 3 8 | sent all jobs 9 | received all jobs 10 | 11 | # The idea of closed channels leads naturally to our next 12 | # example: `range` over channels. 13 | -------------------------------------------------------------------------------- /examples/closures/closures.go: -------------------------------------------------------------------------------- 1 | // Go hỗ trợ [_anonymous functions_](https://en.wikipedia.org/wiki/Anonymous_function), 2 | // hàm này có thể tạo closures. 3 | // Hàm ẩn danh tiện lợi khi bạn muốn tạo một hàm 4 | // mà không cần đặt tên cho nó. 5 | 6 | package main 7 | 8 | import "fmt" 9 | 10 | // Hàm `intSeq` trả về một hàm khác, hàm đó 11 | // được tạo ra một cách ẩn danh trong body của `intSeq`. Hàm 12 | // được trả về _closes over_ biến `i` để tạo thành một closure. 13 | func intSeq() func() int { 14 | i := 0 15 | return func() int { 16 | i++ 17 | return i 18 | } 19 | } 20 | 21 | func main() { 22 | 23 | // Chúng ta gọi `intSeq`, gán kết quả (một hàm) cho 24 | // `nextInt`. Hàm này giữ lại giá trị `i` của chính nó, 25 | // giá trị này sẽ được update mỗi lần chúng ta gọi 26 | // `nextInt`. 27 | nextInt := intSeq() 28 | 29 | // Bạn có thể thấy tác động của closure bằng cách 30 | // gọi `nextInt` một vài lần. 31 | fmt.Println(nextInt()) 32 | fmt.Println(nextInt()) 33 | fmt.Println(nextInt()) 34 | 35 | // Để xác nhận rằng trạng thái là độc nhất 36 | // cho một hàm nhất định, hãy tạo và thử lại hàm mới. 37 | newInts := intSeq() 38 | fmt.Println(newInts()) 39 | } 40 | -------------------------------------------------------------------------------- /examples/closures/closures.hash: -------------------------------------------------------------------------------- 1 | ffd147820d0662f1fca5a6e9a1dfc7eb0fe73315 2 | LkkvUHeU6js 3 | -------------------------------------------------------------------------------- /examples/closures/closures.sh: -------------------------------------------------------------------------------- 1 | $ go run closures.go 2 | 1 3 | 2 4 | 3 5 | 1 6 | 7 | # Tính năng cuối cùng của hàm chúng ta sẽ xem xét là 8 | # đệ quy (recursion). 9 | -------------------------------------------------------------------------------- /examples/command-line-arguments/command-line-arguments.go: -------------------------------------------------------------------------------- 1 | // [_Command-line arguments_](https://en.wikipedia.org/wiki/Command-line_interface#Arguments) 2 | // Là một cách để tham số hóa việc chạy chương trình. 3 | // Ví dụ, `go run hello.go` sử dụng tham số `run` và 4 | // `hello.go` cho chương trình `go`. 5 | 6 | package main 7 | 8 | import ( 9 | "fmt" 10 | "os" 11 | ) 12 | 13 | func main() { 14 | // `os.Args` cung cấp truy cập đến tham số 15 | // command-line nguyên bản. Lưu ý rằng, giá trị đầu 16 | // tiên trong slice này là đường dẫn chương trình, 17 | // và `os.Args[1:]` giữ tham số của chương trình. 18 | argsWithProg := os.Args 19 | argsWithoutProg := os.Args[1:] 20 | 21 | // Bạn có thể lấy từng thành phần riêng biệt với 22 | // index thông thường. 23 | arg := os.Args[3] 24 | 25 | fmt.Println(argsWithProg) 26 | fmt.Println(argsWithoutProg) 27 | fmt.Println(arg) 28 | } 29 | -------------------------------------------------------------------------------- /examples/command-line-arguments/command-line-arguments.hash: -------------------------------------------------------------------------------- 1 | a73ec479134db13e50277b48244c64e6ebf7fcf8 2 | KkhznDBWpHZ 3 | -------------------------------------------------------------------------------- /examples/command-line-arguments/command-line-arguments.sh: -------------------------------------------------------------------------------- 1 | # Để thử nghiệm với tham số command-line, tốt nhất là 2 | # build binary với `go build` trước. 3 | $ go build command-line-arguments.go 4 | $ ./command-line-arguments a b c d 5 | [./command-line-arguments a b c d] 6 | [a b c d] 7 | c 8 | # Sau đó, chúng ta sẽ đi sâu hơn vào xử lý command-line 9 | # với flag. 10 | -------------------------------------------------------------------------------- /examples/command-line-flags/command-line-flags.go: -------------------------------------------------------------------------------- 1 | // [_Command-line flags_ (cờ dòng lệnh)](https://en.wikipedia.org/wiki/Command-line_interface#Command-line_option) 2 | // là cách thông thường để chỉ định các tuỳ chọn cho các 3 | // chương chình dòng lệnh. Ví dụ, trong lệnh `wc -l`, `-l` 4 | // là một cờ dòng lệnh. 5 | 6 | package main 7 | 8 | // Go cung cấp một package (gói) `flag` hỗ trợ cơ bản cho phân tích 9 | // cú pháp các command-line flag. Chúng ta sẽ sử dụng package để 10 | // hiện thực chương trình dòng lệnh ví dụ của mình. 11 | import ( 12 | "flag" 13 | "fmt" 14 | ) 15 | 16 | func main() { 17 | 18 | // Các khai báo cờ cơ bản có sẵn cho các tùy chọn chuỗi 19 | // số nguyên, và luận lí. Ở đây chúng ta khai báo một 20 | // cờ chuỗi `word` với giá trị mặc định `"foo"` và một 21 | // mô tả ngắn. Hàm `flag.String` nãy trả về một 22 | // con trỏ chuỗi (không phải là một giá trị chuỗi); 23 | // chúng ta sẽ thấy cách sử dụng con trỏ này bên dưới. 24 | wordPtr := flag.String("word", "foo", "a string") 25 | 26 | // Đây là các khai báo cờ `numb` and `fork`, sử dụng một 27 | // cách tiếp cận tương tự với cờ `word`. 28 | numbPtr := flag.Int("numb", 42, "an int") 29 | forkPtr := flag.Bool("fork", false, "a bool") 30 | 31 | // Cũng có thể khai báo một tùy chọn sử dụng một biến 32 | // đã được khai báo ở một nơi khác trong chương trình. 33 | // Lưu ý rằng chúng ta cần truyền vào một con trỏ tới 34 | // hàm khai báo cờ. 35 | var svar string 36 | flag.StringVar(&svar, "svar", "bar", "a string var") 37 | 38 | // Sau khi tất cả các cờ đã được khai báo, gọi `flag.Parse()` 39 | // để thực thi phân tích cú pháp dòng lệnh. 40 | flag.Parse() 41 | 42 | // Ở đây chúng ta chỉ đơn giản là in ra các tùy chọn 43 | // đã được phân tích cú pháp và bất kỳ đối số vị trí 44 | // dư thừa nào. Chú ý rằng chúng ta cần huỷ tham chiếu 45 | // các con trỏ để lấy giá trị tùy chọn thực sự, ví dụ `*wordPtr` 46 | fmt.Println("word:", *wordPtr) 47 | fmt.Println("numb:", *numbPtr) 48 | fmt.Println("fork:", *forkPtr) 49 | fmt.Println("svar:", svar) 50 | fmt.Println("tail:", flag.Args()) 51 | } 52 | -------------------------------------------------------------------------------- /examples/command-line-flags/command-line-flags.hash: -------------------------------------------------------------------------------- 1 | 58b8ac9266e25ec13bcbd2ff51556bfaee0fa00e 2 | zdRrGlxUMno 3 | -------------------------------------------------------------------------------- /examples/command-line-flags/command-line-flags.sh: -------------------------------------------------------------------------------- 1 | # Để thử nghiệm chương trình các cờ dòng lệnh, 2 | # trước tiên là biên dịch nó và chạy tệp nhị phân 3 | # kết quả trực tiếp. 4 | $ go build command-line-flags.go 5 | 6 | # Hãy thử chương trình đã xây dựng bằng cách cung cấp 7 | # giá trị cho tất cả các cờ 8 | $ ./command-line-flags -word=opt -numb=7 -fork -svar=flag 9 | word: opt 10 | numb: 7 11 | fork: true 12 | svar: flag 13 | tail: [] 14 | 15 | # Lưu ý rằng nếu bạn bỏ qua các cờ chúng sẽ tự động 16 | # lấy giá trị mặc định của chúng. 17 | $ ./command-line-flags -word=opt 18 | word: opt 19 | numb: 42 20 | fork: false 21 | svar: bar 22 | tail: [] 23 | 24 | # Đối số vị trí theo sau có thể được cung cấp sau 25 | # bất kỳ cờ nào. 26 | $ ./command-line-flags -word=opt a1 a2 a3 27 | word: opt 28 | ... 29 | tail: [a1 a2 a3] 30 | 31 | # Lưu ý rằng package (gói) `flag` yêu cầu tất cả 32 | # các cờ xuất hiện trước các đối số vị trí (nếu không 33 | # các cờ sẽ được hiểu là các đối số vị trí). 34 | $ ./command-line-flags -word=opt a1 a2 a3 -numb=7 35 | word: opt 36 | numb: 42 37 | fork: false 38 | svar: bar 39 | tail: [a1 a2 a3 -numb=7] 40 | 41 | # Sử dụng cờ `-h` hoặc `--help` để nhân văn bản 42 | # trợ giúp được tạo tự động cho chương trình dòng lệnh. 43 | $ ./command-line-flags -h 44 | Usage of ./command-line-flags: 45 | -fork=false: a bool 46 | -numb=42: an int 47 | -svar="bar": a string var 48 | -word="foo": a string 49 | 50 | # Nếu bạn cung cấp một cờ mà không được chỉ định 51 | # cho package `flag`, chương trình sẽ in thông báo 52 | # lỗi và hiển thị văn bản trợ giúp 53 | $ ./command-line-flags -wat 54 | flag provided but not defined: -wat 55 | Usage of ./command-line-flags: 56 | ... -------------------------------------------------------------------------------- /examples/command-line-subcommands/command-line-subcommands.go: -------------------------------------------------------------------------------- 1 | // Một vài công cụ dòng lệnh, giống như công cụ `go` tool 2 | // hoặc `git` có nhiều *lệnh con*, mõi lệnh con có bộ cờ 3 | // riêng của nó. Ví dụ, `go build` và `go get` là hai 4 | // lệnh con khác nhau của công cụ `go`. Package (gói) `flag` 5 | // cho phép chúng ta dễ dàng xác định các lệnh con 6 | // đơn giản có bộ cờ riêng của chúng. 7 | package main 8 | 9 | import ( 10 | "flag" 11 | "fmt" 12 | "os" 13 | ) 14 | 15 | func main() { 16 | 17 | // Chúng ta khai báo một lệnh con bằng cách 18 | // sử dụng hàm `NewFlagSet` và tiếp tục định nghĩa 19 | // các cờ mới cụ thể cho lệnh con này. 20 | fooCmd := flag.NewFlagSet("foo", flag.ExitOnError) 21 | fooEnable := fooCmd.Bool("enable", false, "enable") 22 | fooName := fooCmd.String("name", "", "name") 23 | 24 | // Đối với một lệnh con khác, chúng ta có thể 25 | // xác định các cờ được hỗ trợ khác nhau. 26 | barCmd := flag.NewFlagSet("bar", flag.ExitOnError) 27 | barLevel := barCmd.Int("level", 0, "level") 28 | 29 | // Lệnh con được mong đợi là đối số đầu tiên 30 | // cho chương trình. 31 | if len(os.Args) < 2 { 32 | fmt.Println("expected 'foo' or 'bar' subcommands") 33 | os.Exit(1) 34 | } 35 | 36 | // Kiểm tra lệnh con được kích hoạt. 37 | switch os.Args[1] { 38 | 39 | // Đối với mỗi lệnh con, chúng tôi phân tích cú pháp 40 | // các cờ của nó và có quyền truy cập vào các 41 | // đối số vị trí cuối cùng. 42 | case "foo": 43 | fooCmd.Parse(os.Args[2:]) 44 | fmt.Println("subcommand 'foo'") 45 | fmt.Println(" enable:", *fooEnable) 46 | fmt.Println(" name:", *fooName) 47 | fmt.Println(" tail:", fooCmd.Args()) 48 | case "bar": 49 | barCmd.Parse(os.Args[2:]) 50 | fmt.Println("subcommand 'bar'") 51 | fmt.Println(" level:", *barLevel) 52 | fmt.Println(" tail:", barCmd.Args()) 53 | default: 54 | fmt.Println("expected 'foo' or 'bar' subcommands") 55 | os.Exit(1) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /examples/command-line-subcommands/command-line-subcommands.hash: -------------------------------------------------------------------------------- 1 | ac5101bdca943aaf3f084ffeaf6df95c273572bb 2 | CiJQ3BHOPPE 3 | -------------------------------------------------------------------------------- /examples/command-line-subcommands/command-line-subcommands.sh: -------------------------------------------------------------------------------- 1 | $ go build command-line-subcommands.go 2 | 3 | # Đầu tiên kích hoạt lệnh con foo. 4 | $ ./command-line-subcommands foo -enable -name=joe a1 a2 5 | subcommand 'foo' 6 | enable: true 7 | name: joe 8 | tail: [a1 a2] 9 | 10 | # Bây giờ thử bar. 11 | $ ./command-line-subcommands bar -level 8 a1 12 | subcommand 'bar' 13 | level: 8 14 | tail: [a1] 15 | 16 | # Nhưng bar sẽ không chấp nhận các cờ của foo. 17 | $ ./command-line-subcommands bar -enable a1 18 | flag provided but not defined: -enable 19 | Usage of bar: 20 | -level int 21 | level 22 | 23 | # Tiếp theo, chúng ta sẽ xem xét biến môi trường, 24 | # một cách phổ biến khác để tham số hóa 25 | # các chương trình. -------------------------------------------------------------------------------- /examples/constants/constants.go: -------------------------------------------------------------------------------- 1 | // Go hỗ trợ _constants_ (hằng) của character (ký tự), string (chuỗi), boolean (kiểu boolean) 2 | // và các loại giá trị số. 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "math" 9 | ) 10 | 11 | // `const` khai báo một giá trị hằng số. 12 | const s string = "constant" 13 | 14 | func main() { 15 | fmt.Println(s) 16 | 17 | // Một khai báo `const` có thể xuất hiện bất cứ nơi nào, 18 | // giống như khai báo `var` 19 | const n = 500000000 20 | 21 | // Biểu thức hằng số thực hiện tính toán số học 22 | // với độ chính xác tuỳ ý. 23 | const d = 3e20 / n 24 | fmt.Println(d) 25 | 26 | // Một hằng số không thuộc loại (type) nào cho tới khi nó được khai báo riêng, 27 | // chẳng hạn bằng một chuyển đổi rõ ràng. 28 | fmt.Println(int64(d)) 29 | 30 | // Một con số có thể được khai báo một loai (type) bằng cách sử dụng nó 31 | // trong một ngữ cảnh (context) yêu cầu, chẳng hạn như gán biến hoặc hàm gọi. 32 | // Ví dụ: ở đây `math.Sin` yêu cầu một loại `float64`. 33 | fmt.Println(math.Sin(n)) 34 | } 35 | -------------------------------------------------------------------------------- /examples/constants/constants.hash: -------------------------------------------------------------------------------- 1 | 472de136b107448e80bc2f281cfd4338a57fa19a 2 | qtBOj5AUIGP 3 | -------------------------------------------------------------------------------- /examples/constants/constants.sh: -------------------------------------------------------------------------------- 1 | $ go run constant.go 2 | constant 3 | 6e+11 4 | 600000000000 5 | -0.28470407323754404 6 | -------------------------------------------------------------------------------- /examples/context/context.go: -------------------------------------------------------------------------------- 1 | // Trong ví dụ trước chúng ta đã xem qua cách thiết lập một 2 | // [HTTP server](http-servers). HTTP servers rất hữu ích để 3 | // minh họa việc sử dụng `context.Context` để điều khiển 4 | // việc hủy bỏ. Một `Context` chứa các deadline, tín hiệu 5 | // hủy bỏ và các giá trị thuộc HTTP request khác 6 | // qua lại các API boundaries và goroutines. 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "net/http" 12 | "time" 13 | ) 14 | 15 | func hello(w http.ResponseWriter, req *http.Request) { 16 | 17 | // Một `context.Context` được tạo cho mỗi request bởi 18 | // `net/http` và thông qua hàm `Context()` của request. 19 | ctx := req.Context() 20 | fmt.Println("server: hello handler started") 21 | defer fmt.Println("server: hello handler ended") 22 | 23 | // Chờ một vài giây trước khi gửi một câu trả lời 24 | // cho client. Điều này có thể giả lập một số công việc 25 | // mà server đang làm. Trong khi đó, hãy quan sát 26 | // channel `Done()` của context để nhận biết có tín hiệu 27 | // hủy bỏ và trả về ngay lập tức. 28 | select { 29 | case <-time.After(10 * time.Second): 30 | fmt.Fprintf(w, "hello\n") 31 | case <-ctx.Done(): 32 | // Hàm `Err()` trong context trả về một lỗi 33 | // tại sao channel `Done()` bị đóng. 34 | err := ctx.Err() 35 | fmt.Println("server:", err) 36 | internalError := http.StatusInternalServerError 37 | http.Error(w, err.Error(), internalError) 38 | } 39 | } 40 | 41 | func main() { 42 | 43 | // Giờ chúng ta chỉ định hàm xử lý cho đường dẫn "/hello" 44 | // và bắt đầu lắng nghe các HTTP request. 45 | http.HandleFunc("/hello", hello) 46 | http.ListenAndServe(":8090", nil) 47 | } 48 | -------------------------------------------------------------------------------- /examples/context/context.hash: -------------------------------------------------------------------------------- 1 | 90be6edaebe254c3755d5c25357ab2938db92000 2 | hKMTnJT0how 3 | -------------------------------------------------------------------------------- /examples/context/context.sh: -------------------------------------------------------------------------------- 1 | # Run the server in the background. 2 | $ go run context-in-http-servers.go & 3 | 4 | # Simulate a client request to `/hello`, hitting 5 | # Ctrl+C shortly after starting to signal 6 | # cancellation. 7 | $ curl localhost:8090/hello 8 | server: hello handler started 9 | ^C 10 | server: context canceled 11 | server: hello handler ended -------------------------------------------------------------------------------- /examples/defer/defer.go: -------------------------------------------------------------------------------- 1 | // _Defer_ được sử dụng để chắc chắn rằng một lời gọi 2 | // hàm được thực thi sau đó trong một thực thi chương 3 | // trình, thường phục vụ cho mục đích cleanup. `defer` 4 | // được sử dụng ở ví dụ, `ensure` và `finally` có thể 5 | // được sử dụng trong ngôn ngữ khác. 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "os" 12 | ) 13 | 14 | // Giả sử chúng ta muốn tạo một file, ghi nó, và sau đó 15 | // đóng nó khi chúng ta thực hiện xong. đây là cách mà 16 | // chúng ta có thể làm với `defer`. 17 | func main() { 18 | // Ngay lập tức sau khi lấy một đối tượng file với 19 | // `createFile`, chúng ta defer với đóng một file 20 | // với `closeFile`. Điều này được thực thi ở cuối 21 | // hàm (`main`), sau khi `writeFile` được thực hiện. 22 | f := createFile("/tmp/defer.txt") 23 | defer closeFile(f) 24 | writeFile(f) 25 | } 26 | 27 | func createFile(p string) *os.File { 28 | fmt.Println("creating") 29 | f, err := os.Create(p) 30 | if err != nil { 31 | panic(err) 32 | } 33 | return f 34 | } 35 | 36 | func writeFile(f *os.File) { 37 | fmt.Println("writing") 38 | fmt.Fprintln(f, "data") 39 | 40 | } 41 | 42 | func closeFile(f *os.File) { 43 | fmt.Println("closing") 44 | err := f.Close() 45 | // Quan trọng là phải kiểm tra lỗi khi đóng một file, 46 | // ngay cả trong hàm đã được defer. 47 | if err != nil { 48 | fmt.Fprintf(os.Stderr, "error: %v\n", err) 49 | os.Exit(1) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /examples/defer/defer.hash: -------------------------------------------------------------------------------- 1 | 9ac58cf7ad9677fd0657887028b3568dffc9151f 2 | uJRkGQSelvl 3 | -------------------------------------------------------------------------------- /examples/defer/defer.sh: -------------------------------------------------------------------------------- 1 | # Chạy chương trình để xác nhận rằng file đã được đóng 2 | # sau khi được ghi. 3 | $ go run defer.go 4 | creating 5 | writing 6 | closing -------------------------------------------------------------------------------- /examples/directories/directories.go: -------------------------------------------------------------------------------- 1 | // Go có một số hàm hữu ích để làm việc với 2 | // *thư mục* trong hệ thống tập tin. 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "os" 9 | "path/filepath" 10 | ) 11 | 12 | func check(e error) { 13 | if e != nil { 14 | panic(e) 15 | } 16 | } 17 | 18 | func main() { 19 | 20 | // Tạo một thư mục con mới trong thư mục 21 | // làm việc hiện tại. 22 | err := os.Mkdir("subdir", 0755) 23 | check(err) 24 | 25 | // Khi tạo thư mục tạm thời, cách tốt nhất là sử dụng 26 | // `defer` để xóa chúng. Hàm `os.RemoveAll` sẽ xóa 27 | // toàn bộ một cây thư mục (tương tự với lệnh 28 | // `rm -rf`). 29 | defer os.RemoveAll("subdir") 30 | 31 | // Hàm hỗ trợ để tạo một tập tin mới. 32 | createEmptyFile := func(name string) { 33 | d := []byte("") 34 | check(os.WriteFile(name, d, 0644)) 35 | } 36 | 37 | createEmptyFile("subdir/file1") 38 | 39 | // Chúng ta có thể tạo một cấu trúc thư mục có các 40 | // thư mục cha với `MkdirAll`. Điều này tương tự 41 | // với lệnh `mkdir -p`. 42 | err = os.MkdirAll("subdir/parent/child", 0755) 43 | check(err) 44 | 45 | createEmptyFile("subdir/parent/file2") 46 | createEmptyFile("subdir/parent/file3") 47 | createEmptyFile("subdir/parent/child/file4") 48 | 49 | // Hàm `ReadDir` liệt kê nội dung của thư mục, 50 | // trả về một slice các đối tượng của `os.DirEntry`. 51 | c, err := os.ReadDir("subdir/parent") 52 | check(err) 53 | 54 | fmt.Println("Listing subdir/parent") 55 | for _, entry := range c { 56 | fmt.Println(" ", entry.Name(), entry.IsDir()) 57 | } 58 | 59 | // `Chdir` cho phép chúng ta thay đổi thư mục 60 | // làm việc hiện tại, tương tự như lệnh `cd`. 61 | err = os.Chdir("subdir/parent/child") 62 | check(err) 63 | 64 | // Bây giờ chúng ta sẽ thấy nội dung của `subdir/parent/child` 65 | // khi liệt kê thư mục *hiện tại*. 66 | c, err = os.ReadDir(".") 67 | check(err) 68 | 69 | fmt.Println("Listing subdir/parent/child") 70 | for _, entry := range c { 71 | fmt.Println(" ", entry.Name(), entry.IsDir()) 72 | } 73 | 74 | // `cd` giúp quay lại thư mục ban đầu. 75 | err = os.Chdir("../../..") 76 | check(err) 77 | 78 | // Chúng ta cũng có thể truy cập thư mục theo *đệ quy*, 79 | // , bao gồm tất cả các thư mục con của nó. Hàm `Walk` 80 | // chấp nhận một callback function để xử lý mọi tập tin 81 | // hoặc thư mục được truy cập. 82 | fmt.Println("Visiting subdir") 83 | err = filepath.Walk("subdir", visit) 84 | } 85 | 86 | // `visit`được gọi cho mỗi tập tin hoặc thư mục được 87 | // tìm thấy đệ quy bởi `filepath.Walk`. 88 | func visit(p string, info os.FileInfo, err error) error { 89 | if err != nil { 90 | return err 91 | } 92 | fmt.Println(" ", p, info.IsDir()) 93 | return nil 94 | } 95 | -------------------------------------------------------------------------------- /examples/directories/directories.hash: -------------------------------------------------------------------------------- 1 | 8fb917bfb0f3179a95b15bdde667405ba5a9c06f 2 | SVDHvRc5hHP 3 | -------------------------------------------------------------------------------- /examples/directories/directories.sh: -------------------------------------------------------------------------------- 1 | $ go run directories.go 2 | Listing subdir/parent 3 | child true 4 | file2 false 5 | file3 false 6 | Listing subdir/parent/child 7 | file4 false 8 | Visiting subdir 9 | subdir true 10 | subdir/file1 false 11 | subdir/parent true 12 | subdir/parent/child true 13 | subdir/parent/child/file4 false 14 | subdir/parent/file2 false 15 | subdir/parent/file3 false -------------------------------------------------------------------------------- /examples/embed-directive/embed-directive.go: -------------------------------------------------------------------------------- 1 | // `//go:embed` là một [compiler directive] 2 | // (https://pkg.go.dev/cmd/compile#hdr-Compiler_Directives) 3 | // cho phép chương trình chứa các file và thư mục tùy ý 4 | // trong Go ở build time. Đọc thêm về embed directive 5 | // [ở đây](https://pkg.go.dev/embed). 6 | package main 7 | 8 | // Import gói `embed`; nếu bạn không dùng bất kì 9 | // export nào từ package này, bạn có thể để trống với 10 | // `_ "embed"`. 11 | import ( 12 | "embed" 13 | ) 14 | 15 | // `embed` directive cho phép đường dẫn tương đối với thư 16 | // mục chứa file source Go. Directive này nhúng nội dung 17 | // của file vào trong biến `string` ngay sau nó. 18 | // 19 | //go:embed folder/single_file.txt 20 | var fileString string 21 | 22 | // Hoặc nhúng nội dung của file vào trong một `[]byte` 23 | // 24 | //go:embed folder/single_file.txt 25 | var fileByte []byte 26 | 27 | // Chúng ta có thể nhúng nhiều file hoặc thậm chí thư mục 28 | // với wildcard. Điều này sử dụng một biến thuộc kiểu 29 | // [embed.FS type](https://pkg.go.dev/embed#FS), thực 30 | // hiện một file system ảo đơn giản. 31 | // 32 | //go:embed folder/single_file.txt 33 | //go:embed folder/*.hash 34 | var folder embed.FS 35 | 36 | func main() { 37 | 38 | // Print out the contents of `single_file.txt`. 39 | // In ra nội dung của `single_file.txt` 40 | print(fileString) 41 | print(string(fileByte)) 42 | 43 | // Truy xuất một số file từ thư mục được nhúng. 44 | content1, _ := folder.ReadFile("folder/file1.hash") 45 | print(string(content1)) 46 | 47 | content2, _ := folder.ReadFile("folder/file2.hash") 48 | print(string(content2)) 49 | } 50 | -------------------------------------------------------------------------------- /examples/embed-directive/embed-directive.hash: -------------------------------------------------------------------------------- 1 | 8b39663faa623758470d9f4eb95f767f9a3b7152 2 | TCexK-80M1F 3 | -------------------------------------------------------------------------------- /examples/embed-directive/embed-directive.sh: -------------------------------------------------------------------------------- 1 | # Sử dụng những lệnh này để chạy example. 2 | # (Lưu ý: bởi vì giới hạn của go payground, 3 | # example này chỉ được chọn trên máy cá nhân). 4 | $ mkdir -p folder 5 | $ echo "hello go" > folder/single_file.txt 6 | $ echo "123" > folder/file1.hash 7 | $ echo "456" > folder/file2.hash 8 | 9 | $ go run embed-directive.go 10 | hello go 11 | hello go 12 | 123 13 | 456 14 | -------------------------------------------------------------------------------- /examples/embed-directive/folder/file1.hash: -------------------------------------------------------------------------------- 1 | 123 2 | -------------------------------------------------------------------------------- /examples/embed-directive/folder/file2.hash: -------------------------------------------------------------------------------- 1 | 456 2 | -------------------------------------------------------------------------------- /examples/embed-directive/folder/single_file.txt: -------------------------------------------------------------------------------- 1 | hello go 2 | -------------------------------------------------------------------------------- /examples/environment-variables/environment-variables.go: -------------------------------------------------------------------------------- 1 | // [Biến môi trường](https://en.wikipedia.org/wiki/Environment_variable) 2 | // là một cơ chế phổ quát để [truyền tải thông tin 3 | // cấu hình cho chương trình](https://www.12factor.net/config). 4 | // Hãy xem cách thiết lập, lấy và liệt kê các biến môi trường. 5 | 6 | package main 7 | 8 | import ( 9 | "fmt" 10 | "os" 11 | "strings" 12 | ) 13 | 14 | func main() { 15 | 16 | // Để thiết lập cặp khoá/giá trị, sử dụng `os.Setenv`. Để lấy 17 | // giá trị cho một khóa, sử dụng `os.Getenv`. Điều này sẽ + 18 | // trả về một chuỗi trống nếu khóa không tồn tại 19 | // trong môi trường. 20 | os.Setenv("FOO", "1") 21 | fmt.Println("FOO:", os.Getenv("FOO")) 22 | fmt.Println("BAR:", os.Getenv("BAR")) 23 | 24 | // Sử dụng `os.Environ` để liệt kê tất cả cặp khoá/giá trị 25 | // trong môi trường. Điều này trả về một slice của chuỗi 26 | // dưới dạng `KEY=value`. Bạn có thể sử dụng `strings.SplitN` 27 | // để lấy khoá và giá trị. Ở đây, chúng ta in tất cả các khoá. 28 | fmt.Println() 29 | for _, e := range os.Environ() { 30 | pair := strings.SplitN(e, "=", 2) 31 | fmt.Println(pair[0]) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/environment-variables/environment-variables.hash: -------------------------------------------------------------------------------- 1 | 83022dba7b09f338966f158137ad7b6f99d9dcd3 2 | CCdwT_A23mY 3 | -------------------------------------------------------------------------------- /examples/environment-variables/environment-variables.sh: -------------------------------------------------------------------------------- 1 | # Chạy chương trình sẽ cho thấy chúng ta lấy được 2 | # giá trị cho `FOO` mà chúng ta thiết lập, nhưng 3 | # `BAR` là rỗng. 4 | $ go run environment-variables.go 5 | FOO: 1 6 | BAR: 7 | 8 | # Danh sách các khoá trong môi trường sẽ phụ thuộc 9 | # vào máy của bạn. 10 | TERM_PROGRAM 11 | PATH 12 | SHELL 13 | ... 14 | FOO 15 | 16 | # Nếu chúng ta thiết lập `BAR` trong môi trường trước, 17 | # chương trình chạy sẽ lấy giá trị đó. 18 | $ BAR=2 go run environment-variables.go 19 | FOO: 1 20 | BAR: 2 21 | ... -------------------------------------------------------------------------------- /examples/epoch/epoch.go: -------------------------------------------------------------------------------- 1 | // Một vài yêu cầu phổ biến khi lập trình là lấy số giây, 2 | // milli giây, hoặc nano giây tính từ thời điểm bắt đầu 3 | // [Unix epoch](https://en.wikipedia.org/wiki/Unix_time). 4 | // Dưới đây là cách làm trong Go. 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "time" 10 | ) 11 | 12 | func main() { 13 | 14 | // Sử dụng `time.Now` với `Unix`, `UnixMilli` hoặc `UnixNano` 15 | // để lấy thời gian kể từ thời điểm bắt đầu Unix epoch 16 | // theo giây, milli giây hoặc nano giây, tương ứng. 17 | now := time.Now() 18 | fmt.Println(now) 19 | 20 | fmt.Println(now.Unix()) 21 | fmt.Println(now.UnixMilli()) 22 | fmt.Println(now.UnixNano()) 23 | 24 | // Bạn cũng có thể chuyển đổi số giây hoặc nano giây 25 | // kể từ thời điểm bắt đầu Unix epoch thành `time` tương ứng. 26 | fmt.Println(time.Unix(now.Unix(), 0)) 27 | fmt.Println(time.Unix(0, now.UnixNano())) 28 | } 29 | -------------------------------------------------------------------------------- /examples/epoch/epoch.hash: -------------------------------------------------------------------------------- 1 | 654e6232085a7db87d5e46a1e8a6940eb5a2ef62 2 | vTR9M849BkO 3 | -------------------------------------------------------------------------------- /examples/epoch/epoch.sh: -------------------------------------------------------------------------------- 1 | $ go run epoch.go 2 | 2012-10-31 16:13:58.292387 +0000 UTC 3 | 1351700038 4 | 1351700038292 5 | 1351700038292387000 6 | 2012-10-31 16:13:58 +0000 UTC 7 | 2012-10-31 16:13:58.292387 +0000 UTC 8 | 9 | # Tiếp theo chúng ta sẽ xem các tác vụ 10 | # xử lý thời gian khác: 11 | # phân tích và định dạng thời gian. -------------------------------------------------------------------------------- /examples/errors/errors.go: -------------------------------------------------------------------------------- 1 | // Trong Go, việc thông báo lỗi qua nội dung rõ ràng 2 | // và giá trị riêng biệt. Điều này khác với việc sử dụng 3 | // các exception trong các ngôn ngữ như Java và Ruby và 4 | // sử dụng giá trị kết quả/lỗi đơn giản được quá tải đôi khi 5 | // được dùng trong C. Cách tiếp cận của Go giúp dễ dàng nhận ra 6 | // các hàm trả về lỗi và xử lý chúng bằng cách sử dụng cùng môt 7 | // cấu trúc ngôn ngữ được sử dụng cho bất kỳ nhiệm vụ nào khác 8 | // không phải là lỗi. 9 | 10 | package main 11 | 12 | import ( 13 | "errors" 14 | "fmt" 15 | ) 16 | 17 | // Theo quy ước, errors (lỗi) là giá trị trả về cuối cùng và 18 | // có kiểu `error`, một interface được tích hợp sẵn. 19 | func f1(arg int) (int, error) { 20 | if arg == 42 { 21 | 22 | // `errors.New` tạo ra một giá trị `error` cơ bản 23 | // thông báo lỗi được cung cấp. 24 | return -1, errors.New("can't work with 42") 25 | 26 | } 27 | 28 | // Một giá trị `nil` value trong error chỉ ra rằng 29 | // không có lỗi nào xảy ra. 30 | return arg + 3, nil 31 | } 32 | 33 | // Có thể sử dụng các kiểu tùy chỉnh như `error`s bằng cách 34 | // hiện thực phương thức `Error()` trên chúng. Đây là một 35 | // biến thể của ví dụ ở trên sử dụng một kiểu tùy chỉnh 36 | // để biểu diễn rõ ràng một lỗi đối số . 37 | type argError struct { 38 | arg int 39 | prob string 40 | } 41 | 42 | func (e *argError) Error() string { 43 | return fmt.Sprintf("%d - %s", e.arg, e.prob) 44 | } 45 | 46 | func f2(arg int) (int, error) { 47 | if arg == 42 { 48 | 49 | // Trong trường hợp này, chúng tôi sử dụng cú pháp `&argError` 50 | // để xây dựng một struct mới, cung cấp giá trị 51 | // cho hai trường `arg` and `prob`. 52 | return -1, &argError{arg, "can't work with it"} 53 | } 54 | return arg + 3, nil 55 | } 56 | 57 | func main() { 58 | 59 | // Hai vòng lặp dưới đây điểm tra từng hàm trả về lỗi 60 | // của chúng tôi. Lưu ý rằng việc sử dụng kiểm tra lỗi 61 | // trực tiếp trên dòng `if` line là một cách diẽn đạt 62 | // phổ biến trong Go. 63 | for _, i := range []int{7, 42} { 64 | if r, e := f1(i); e != nil { 65 | fmt.Println("f1 failed:", e) 66 | } else { 67 | fmt.Println("f1 worked:", r) 68 | } 69 | } 70 | for _, i := range []int{7, 42} { 71 | if r, e := f2(i); e != nil { 72 | fmt.Println("f2 failed:", e) 73 | } else { 74 | fmt.Println("f2 worked:", r) 75 | } 76 | } 77 | 78 | // Nếu bạn muốn sử dụng dữ liêu trong một lỗi tuỳ chỉnh 79 | // theo cách lập trình, bạn sẽ cần lấy lỗi dưới dạng một 80 | // phiên bản của kiểu lỗi tùy chỉnh thông qua type 81 | // assertion (xác nhận kiểu). 82 | _, e := f2(42) 83 | if ae, ok := e.(*argError); ok { 84 | fmt.Println(ae.arg) 85 | fmt.Println(ae.prob) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /examples/errors/errors.hash: -------------------------------------------------------------------------------- 1 | f8f3383be0df7dd7ff989008fbee475b5cbc1438 2 | Z0CMEzcLv-n 3 | -------------------------------------------------------------------------------- /examples/errors/errors.sh: -------------------------------------------------------------------------------- 1 | $ go run errors.go 2 | f1 worked: 10 3 | f1 failed: can't work with 42 4 | f2 worked: 10 5 | f2 failed: 42 - can't work with it 6 | 42 7 | can't work with it 8 | 9 | # Xem [bài đăng](https://go.dev/blog/error-handling-and-go) 10 | # trên Go blog để biết thêm về cách xử lý lỗi. -------------------------------------------------------------------------------- /examples/execing-processes/execing-processes.go: -------------------------------------------------------------------------------- 1 | // Ở ví dụ trước chúng ta đã xem qua [khởi tạo các 2 | // process bên ngoài](spawning-processes). Chúng ta làm 3 | // như thế này khi cần một process bên ngoài có thể 4 | // truy cập từ một process Go đang chạy. Đôi khi chúng ta 5 | // chỉ muốn hoàn toàn thay thế process Go hiện tại bằng 6 | // một process khác (có thể không phải process của Go). 7 | // Để làm điều này chúng ta sẽ sử dụng hàm 8 | // exec 9 | // của Go. 10 | 11 | package main 12 | 13 | import ( 14 | "os" 15 | "os/exec" 16 | "syscall" 17 | ) 18 | 19 | func main() { 20 | 21 | // Ở trong ví dụ này chúng ta sẽ thực thi lệnh `ls`. Go 22 | // cần một đường dẫn tuyệt đối đến file binary mà chúng ta 23 | // muốn thực thi, vì vậy chúng ta sẽ sử dụng hàm `exec.LookPath` 24 | // để tìm nó (có thể là `/bin/ls`). 25 | binary, lookErr := exec.LookPath("ls") 26 | if lookErr != nil { 27 | panic(lookErr) 28 | } 29 | 30 | // `Exec` nhận đối số dưới dạng một slice (không phải 31 | // một string). Chúng ta sẽ truyền một số đối số thông 32 | // dụng cho `ls`. Lưu ý rằng đối số đầu tiên phải là tên 33 | // của chương trình mà ta muốn thực thi. 34 | args := []string{"ls", "-a", "-l", "-h"} 35 | 36 | // `Exec` cũng cần một tập hợp các [biến môi trường](environment-variables) 37 | // để sử dụng. Ở đây chúng ta chỉ cung cấp môi trường 38 | // hiện tại. 39 | env := os.Environ() 40 | 41 | // Đây mới chính là lệnh `syscall.Exec` thực sự. Nếu lệnh này 42 | // thành công, thì quá trình thực thi của chương trình sẽ 43 | // kết thúc ở đây và được thay thế bởi process `/bin/ls -a -l -h`. 44 | // Nếu có lỗi xảy ra, chúng ta sẽ nhận được một giá trị trả về. 45 | execErr := syscall.Exec(binary, args, env) 46 | if execErr != nil { 47 | panic(execErr) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /examples/execing-processes/execing-processes.hash: -------------------------------------------------------------------------------- 1 | e56905cdf317bad23479344ffe05d137772efb9e 2 | kiVNE2VPwcR 3 | -------------------------------------------------------------------------------- /examples/execing-processes/execing-processes.sh: -------------------------------------------------------------------------------- 1 | # Khi chạy chương trình của chúng ta, 2 | # process sẽ được thay thế bởi `ls`. 3 | $ go run execing-processes.go 4 | total 16 5 | drwxr-xr-x 4 mark 136B Oct 3 16:29 . 6 | drwxr-xr-x 91 mark 3.0K Oct 3 12:50 .. 7 | -rw-r--r-- 1 mark 1.3K Oct 3 16:28 execing-processes.go 8 | 9 | # Lưu ý rằng Go không cung cấp một hàm `fork` 10 | # Unix. Thường thì điều này không phải là vấn đề, 11 | # vì khi bắt đầu các goroutine, tạo các process, 12 | # và exec các process đều đáp ứng được hầu hết 13 | # các trường hợp sử dụng của `fork`. -------------------------------------------------------------------------------- /examples/exit/exit.go: -------------------------------------------------------------------------------- 1 | // Sử dụng `os.Exit` để thoát ngay lập tức với một status 2 | // cho trước. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | ) 9 | 10 | func main() { 11 | 12 | // `defer` sẽ _không_ được thực thi khi sử dụng `os.Exit`, 13 | // vì vậy `fmt.Println` sẽ không bao giờ được gọi. 14 | defer fmt.Println("!") 15 | 16 | // Thoát chương trình với status 3. 17 | os.Exit(3) 18 | } 19 | 20 | // Lưu ý rằng khác với C, Go không sử dụng giá trị trả về 21 | // là một số nguyên từ `main` để chỉ ra exit status. Nếu 22 | // bạn muốn thoát chương trình với một status khác không, 23 | // bạn nên sử dụng `os.Exit`. 24 | -------------------------------------------------------------------------------- /examples/exit/exit.hash: -------------------------------------------------------------------------------- 1 | 986fe35d45a5babe762d350d77fccfe74caf74a5 2 | 3xZJ92mb2Kb 3 | -------------------------------------------------------------------------------- /examples/exit/exit.sh: -------------------------------------------------------------------------------- 1 | # Nếu bạn chạy `exit.go` bằng `go run`, 2 | # việc thoát chương trình sẽ được thực thi 3 | # và được in ra bởi `go`. 4 | $ go run exit.go 5 | exit status 3 6 | 7 | # Bằng cách biên dịch và thực thi một file binary 8 | # bạn có thể thấy được status trong terminal. 9 | $ go build exit.go 10 | $ ./exit 11 | $ echo $? 12 | 3 13 | 14 | # Lưu ý rằng ký tự `!` trong chương trình của chúng ta 15 | # không được in ra. -------------------------------------------------------------------------------- /examples/file-paths/file-paths.go: -------------------------------------------------------------------------------- 1 | // Package (gói) `filepath` cung cấp các hàm để phân tích cú pháp 2 | // và xây dựng các *đường dẫn tập tin* theo cách có thể dùng 3 | // trên nhiều hệ điều hành khác nhau; ví dụ: `dir/file` trên Linux 4 | // và `dir\file` trên Windows. 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "path/filepath" 10 | "strings" 11 | ) 12 | 13 | func main() { 14 | 15 | // `Join` nên được sử dụng để xây dựng các đường dẫn 16 | // theo cách có thể dùng trên nhiều hệ điều hành 17 | // khác nhau. Hàm nhận vào bất kì số lượng đối số 18 | // và xây dựng một đường dẫn theo cấu trúc 19 | // phân cấp từ chúng. 20 | p := filepath.Join("dir1", "dir2", "filename") 21 | fmt.Println("p:", p) 22 | 23 | // Bạn nên luôn sử dụng `Join` thay vì nối các kí tự 24 | // `/`s or `\`s thủ công. Ngoài việc cung cấp khả năng 25 | // sử dụng trên nhiều hệ điều hành, `Join` cũng 26 | // chuẩn hóa đường dẫn bằng cách loại bỏ các bộ phận 27 | // phân cách không cần thiết và thay đổi thư mục. 28 | fmt.Println(filepath.Join("dir1//", "filename")) 29 | fmt.Println(filepath.Join("dir1/../dir1", "filename")) 30 | 31 | // `Dir` và `Base` có thể được sử dụng để phân tách 32 | // một đường dẫn thành thư mục và tên tập tin. Ngoài ra 33 | // `Split` cũng trả về cả hai giá trị này trong một lần gọi. 34 | fmt.Println("Dir(p):", filepath.Dir(p)) 35 | fmt.Println("Base(p):", filepath.Base(p)) 36 | 37 | // Chúng ta có thể kiểm tra xem một đường dẫn có phải 38 | // là tuyệt đối hay không. 39 | fmt.Println(filepath.IsAbs("dir/file")) 40 | fmt.Println(filepath.IsAbs("/dir/file")) 41 | 42 | filename := "config.json" 43 | 44 | // Một số tên tập tin có phần mở rộng theo sau dấu chấm. 45 | // Chúng ta có thể tách phần mở rộng ra khỏi các tên đó 46 | // bằng hàm `Ext`. 47 | ext := filepath.Ext(filename) 48 | fmt.Println(ext) 49 | 50 | // Để tìm tên tập tin mà không có phần mở rộng, 51 | // sủ dụng hàm `strings.TrimSuffix`. 52 | fmt.Println(strings.TrimSuffix(filename, ext)) 53 | 54 | // Hàm `Rel` tìm một đường dẫn tương đối giữa một thư mục 55 | // *cơ sở* và một đường dẫn tập tin cần truy cập. Hàm này 56 | // trả về một lỗi nếu đưỡng dẫn *đích* không thể chuyển 57 | // sang đường dẫn tương đối và thư mục cơ sở. 58 | rel, err := filepath.Rel("a/b", "a/b/t/file") 59 | if err != nil { 60 | panic(err) 61 | } 62 | fmt.Println(rel) 63 | 64 | rel, err = filepath.Rel("a/b", "a/c/t/file") 65 | if err != nil { 66 | panic(err) 67 | } 68 | fmt.Println(rel) 69 | } 70 | -------------------------------------------------------------------------------- /examples/file-paths/file-paths.hash: -------------------------------------------------------------------------------- 1 | 14079399fb10fe0f7509bd21b41c004ff624deef 2 | 0XnLzSNhE0C 3 | -------------------------------------------------------------------------------- /examples/file-paths/file-paths.sh: -------------------------------------------------------------------------------- 1 | $ go run file-paths.go 2 | p: dir1/dir2/filename 3 | dir1/filename 4 | dir1/filename 5 | Dir(p): dir1/dir2 6 | Base(p): filename 7 | false 8 | true 9 | .json 10 | config 11 | t/file 12 | ../c/t/file -------------------------------------------------------------------------------- /examples/for/for.go: -------------------------------------------------------------------------------- 1 | // `for` là cấu trúc vòng lặp của Go. 2 | // Dưới đây là một vài loại của vòng lặp `for`. 3 | 4 | package main 5 | 6 | import "fmt" 7 | 8 | func main() { 9 | 10 | // Loại căn bản nhất, với một điều kiện. 11 | i := 1 12 | for i <= 3 { 13 | fmt.Println(i) 14 | i = i + 1 15 | } 16 | 17 | // Một vòng lặp `for` cổ điển (initial/condition/after). 18 | for j := 7; j <= 9; j++ { 19 | fmt.Println(j) 20 | } 21 | 22 | // Vòng lặp `for` khi không có điều kiện sẽ lặp đi lặp lại 23 | // cho tới khi gặp `break` hoặc `return` sẽ thoát khỏi vòng lặp. 24 | for { 25 | fmt.Println("loop") 26 | break 27 | } 28 | 29 | // Bạn có thể sử dụng `continue` để tiếp tục lần lặp lại tiếp theo 30 | // của vòng lặp. 31 | for n := 0; n <= 5; n++ { 32 | if n%2 == 0 { 33 | continue 34 | } 35 | fmt.Println(n) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/for/for.hash: -------------------------------------------------------------------------------- 1 | c649f7b67aec7196f946f374ea8d62d3c42f251b 2 | nt_pGMVVoYj 3 | -------------------------------------------------------------------------------- /examples/for/for.sh: -------------------------------------------------------------------------------- 1 | $ go run for.go 2 | 1 3 | 2 4 | 3 5 | 7 6 | 8 7 | 9 8 | loop 9 | 1 10 | 3 11 | 5 12 | 13 | # Chúng ta sẽ thấy các hình thức `for` 14 | # khác về sau, khi chúng ta làm tới phần 15 | # tuyên bố `range`, channels (kênh), và 16 | # các loại cấu trúc dữ liệu khác -------------------------------------------------------------------------------- /examples/functions/functions.go: -------------------------------------------------------------------------------- 1 | // _Function_ là trung tâm trong Go. Chúng ta sẽ học 2 | // về hàm bằng vài ví dụ dưới. 3 | package main 4 | 5 | import "fmt" 6 | 7 | // Đây là một hàm nhận 2 biến `int` và 8 | // trả về một tổng `int`. 9 | func plus(a int, b int) int { 10 | 11 | // Go yêu cầu khai báo chi tiết trả về, ví dụ 12 | // nó sẽ không tự động trả về giá trị của 13 | // biểu thức gần nhất. 14 | return a + b 15 | } 16 | 17 | // Khi bạn trả về nhiều tham số liên tục của 18 | // cùng một loại, bạn có thể bỏ qua tên loại cho 19 | // các tham số được nhập tương tự cho đến 20 | // tham số cuối cùng khai báo loại. 21 | func plusPlus(a, b, c int) int { 22 | return a + b + c 23 | } 24 | 25 | func main() { 26 | 27 | // Gọi một hàm với `name(args)`. 28 | res := plus(1, 2) 29 | fmt.Println("1+2 =", res) 30 | 31 | res = plusPlus(1, 2, 3) 32 | fmt.Println("1+2+3 =", res) 33 | } 34 | -------------------------------------------------------------------------------- /examples/functions/functions.hash: -------------------------------------------------------------------------------- 1 | 39d0dbf6481b5d00a02483f65c003c7731bd0f63 2 | TWANoPxQYM7 3 | -------------------------------------------------------------------------------- /examples/functions/functions.sh: -------------------------------------------------------------------------------- 1 | $ go run functions.go 2 | 1+2 = 3 3 | 1+2+3 = 6 4 | 5 | # Có nhiều tính năng khác trong hàm của Go. Một trong 6 | # số đó là trả về nhiều giá trị, cái mà 7 | # chúng ta sẽ xem tiếp theo. 8 | -------------------------------------------------------------------------------- /examples/generics/generics.go: -------------------------------------------------------------------------------- 1 | // Bắt đầu từ phiên bản 1.18, Go đã hỗ trợ thêm cho 2 | // _generics_, also known as _type parameters_ 3 | // (tham số kiểu dữ liêu). 4 | 5 | package main 6 | 7 | import "fmt" 8 | 9 | // Một ví dụ về hàm generic, `MapKeys` nhận 10 | // một map với bất kì kiểu nào và trả về một slices các 11 | // key của nó. Hàm này có hai type parameters - `K` và 12 | // `V`; `K` có ràng buộc `comparable` (so sánh), 13 | // nghĩa là chúng ta có thể so sánh các giá trị của 14 | // kiểu này với toán tử `==` và`!=` . Điều này là 15 | // 16 | // để sử dụng key trong map của Go. 17 | // 18 | // `V` có ràng buộc `any` (bất kì), nghĩa là nó không 19 | // bị hạn chết bởi bất kì đièu gì (`any` là một 20 | // định danh khác cho `interface{}`). 21 | func MapKeys[K comparable, V any](m map[K]V) []K { 22 | r := make([]K, 0, len(m)) 23 | for k := range m { 24 | r = append(r, k) 25 | } 26 | return r 27 | } 28 | 29 | // Một ví dụ về type (kiểu) generics là `List`, `List` 30 | // là một danh sách liên kết đơn với các giá trị của 31 | // bất kỳ kiểu dữ liệu nào. 32 | type List[T any] struct { 33 | head, tail *element[T] 34 | } 35 | 36 | type element[T any] struct { 37 | next *element[T] 38 | val T 39 | } 40 | 41 | // Chúng ta có thể định nghĩa các phương thức trên 42 | // generic types giống như các kiểu thông thường, 43 | // nhưng chúng ta phải giữ các type 44 | // parameters đúng vị trí. Kiểu này là `List[T]`, 45 | // không phải `List`. 46 | func (lst *List[T]) Push(v T) { 47 | if lst.tail == nil { 48 | lst.head = &element[T]{val: v} 49 | lst.tail = lst.head 50 | } else { 51 | lst.tail.next = &element[T]{val: v} 52 | lst.tail = lst.tail.next 53 | } 54 | } 55 | 56 | func (lst *List[T]) GetAll() []T { 57 | var elems []T 58 | for e := lst.head; e != nil; e = e.next { 59 | elems = append(elems, e.val) 60 | } 61 | return elems 62 | } 63 | 64 | func main() { 65 | var m = map[int]string{1: "2", 2: "4", 4: "8"} 66 | 67 | // Khi gọi các hàm generic, chúng ta thường 68 | //có thể dựa vào _type inference_. Lưu ý rằng 69 | // chúng ta không cần phải xác định kiểu cho 70 | // `K` and `V` khi gọi `MapKeys` - trình 71 | // biên dịch sẽ tự động suy ra chúng 72 | fmt.Println("keys:", MapKeys(m)) 73 | 74 | // ... tuy nhiên, chúng ta cũng có thể xác định 75 | // chúng một cách rõ ràng. 76 | 77 | lst := List[int]{} 78 | lst.Push(10) 79 | lst.Push(13) 80 | lst.Push(23) 81 | fmt.Println("list:", lst.GetAll()) 82 | } 83 | -------------------------------------------------------------------------------- /examples/generics/generics.hash: -------------------------------------------------------------------------------- 1 | cc9374a9de975978d1f59f42c112d61f30bdfa2e 2 | _eUqdf7Ho3E 3 | -------------------------------------------------------------------------------- /examples/generics/generics.sh: -------------------------------------------------------------------------------- 1 | $ go run generics.go 2 | keys: [4 1 2] 3 | list: [10 13 23] -------------------------------------------------------------------------------- /examples/goroutines/goroutines.go: -------------------------------------------------------------------------------- 1 | // Một _goroutine_ một luông thực thi lightweight (nhẹ). 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "time" 8 | ) 9 | 10 | func f(from string) { 11 | for i := 0; i < 3; i++ { 12 | fmt.Println(from, ":", i) 13 | } 14 | } 15 | 16 | func main() { 17 | 18 | // Giả sử chúng ta gọi một hàm `f(s)`. Dưới đây là cách 19 | // chúng ta gọi hàm đó theo cách thông thường, thực thi 20 | // đồng bộ. 21 | f("direct") 22 | 23 | // Để gọi hàm này trong một goroutine, sử dụng cú pháp 24 | // `go f(s)`. Goroutine mới này sẽ thực thi song song 25 | // với gouroutine gọi hàm gốc. 26 | go f("goroutine") 27 | 28 | // Bạn cũng có thể bắt đầu một goroutine cho một lần 29 | // gọi hàm ẩn danh. 30 | go func(msg string) { 31 | fmt.Println(msg) 32 | }("going") 33 | 34 | // Hai lần gọi hàm của chúng ta đang được thực thi bất đồng bộ 35 | // trong các goroutine riêng biệt. Đợi chúng thực thi xong 36 | // (để có một cách tiếp cận đáng tin cậy hơn, ta có thể sử dụng 37 | // một [WaitGroup](waitgroups)). 38 | time.Sleep(time.Second) 39 | fmt.Println("done") 40 | } 41 | -------------------------------------------------------------------------------- /examples/goroutines/goroutines.hash: -------------------------------------------------------------------------------- 1 | db275688d3be93230b4700eca989e6564ee229b4 2 | hJc1nJGl33J 3 | -------------------------------------------------------------------------------- /examples/goroutines/goroutines.sh: -------------------------------------------------------------------------------- 1 | # Khi chúng ta chạy chương trình này, chúng ta sẽ thấy 2 | # đầu tiên là kết quả của lần gọi đồng bộ, sau đó là 3 | # kết quả của hai goroutines. Kết quả của các 4 | # goroutines' có thể được xen kẽ lẫn nhau vì các 5 | # goroutine đang được thực thi song song bởi 6 | # Go runtime. 7 | $ go run goroutines.go 8 | direct : 0 9 | direct : 1 10 | direct : 2 11 | goroutine : 0 12 | going 13 | goroutine : 1 14 | goroutine : 2 15 | done 16 | 17 | # Tiếp theo, chúng ta sẽ xem xét một phần bổ sung 18 | # cho goroutine trong các concurrent Go program 19 | #(chương trình Go đồng thời): channels (kênh). -------------------------------------------------------------------------------- /examples/hello-world/hello-world.go: -------------------------------------------------------------------------------- 1 | // Chương trình đầu tiên của chúng ta sẽ in ra dòng chữ "hello world" 2 | // Dưới đây là mã nguồn 3 | package main 4 | 5 | import "fmt" 6 | 7 | func main() { 8 | fmt.Println("hello world") 9 | } 10 | -------------------------------------------------------------------------------- /examples/hello-world/hello-world.hash: -------------------------------------------------------------------------------- 1 | 34fad2c016ea2c1ed97e23cd4b430f61a84fab64 2 | 32XkrVcQ_lC 3 | -------------------------------------------------------------------------------- /examples/hello-world/hello-world.sh: -------------------------------------------------------------------------------- 1 | # Để chạy chương trình, đặt code vào 2 | # trong `hello-world.go` và sử dụng 3 | # câu lệnh `go run`. 4 | $ go run hello-world.go 5 | hello world 6 | 7 | # Đôi khi chúng ta muốn build 8 | # chương trình thành tập tin nhị phân 9 | # Chúng ta có thể làm bằng cách 10 | # sử dụng câu lệnh `go build`. 11 | \ 12 | $ go build hello-world.go 13 | $ ls 14 | hello-world hello-world.go 15 | 16 | # Khi đó chúng ta có thể thực thi 17 | # (sử dụng) tập tin nhị phân đó trực tiếp 18 | $ ./hello-world 19 | hello world 20 | 21 | # Bây giờ chúng ta có thể chạy và 22 | # build chương trình Go đơn giản 23 | # Hãy cùng nhau học thêm về ngôn ngữ này nhé. -------------------------------------------------------------------------------- /examples/http-client/http-client.go: -------------------------------------------------------------------------------- 1 | // Thư viện tiêu chuẩn Go có nhiều hỗ trợ tuyệt vời cho 2 | // máy khác và máy chủ HTTP clients trong package (gói) `net/http` 3 | // package. Trong ví dụ này, chúng tôi sẽ sử dụng nó để 4 | // đưa ra các yêu cầu HTTP đơn giản. 5 | package main 6 | 7 | import ( 8 | "bufio" 9 | "fmt" 10 | "net/http" 11 | ) 12 | 13 | func main() { 14 | 15 | // Gửi yêu cầu HTTP GET tới máy chủ. `http.Get` là một 16 | // lối tắt thuận tiện quanh việc tạo đối tượng `http.Client` 17 | // và gọi phương thức `Get`; nó sử dụng đối tượng 18 | // `http.DefaultClient` có các thiết lập mặc định hữu ích. 19 | resp, err := http.Get("https://gobyexample.com") 20 | if err != nil { 21 | panic(err) 22 | } 23 | defer resp.Body.Close() 24 | 25 | // In ra trạng thái phải hồi HTTP. 26 | fmt.Println("Response status:", resp.Status) 27 | 28 | // In ra 5 dòng đầu tiên của phần thân phản hồi. 29 | scanner := bufio.NewScanner(resp.Body) 30 | for i := 0; scanner.Scan() && i < 5; i++ { 31 | fmt.Println(scanner.Text()) 32 | } 33 | 34 | if err := scanner.Err(); err != nil { 35 | panic(err) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /examples/http-client/http-client.hash: -------------------------------------------------------------------------------- 1 | 9552f654a31ce4b6713328389a56ebffbbc40cd9 2 | ykWSLfwioQP 3 | -------------------------------------------------------------------------------- /examples/http-client/http-client.sh: -------------------------------------------------------------------------------- 1 | $ go run http-clients.go 2 | Response status: 200 OK 3 | 4 | 5 | 6 | 7 | Go by Example -------------------------------------------------------------------------------- /examples/http-server/http-server.go: -------------------------------------------------------------------------------- 1 | // Xây dựng HTTP server tương đối dễ 2 | // thông qua gói `net/http` trong Go. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "net/http" 8 | ) 9 | 10 | // Ý tưởng của `net/http` servers là dựa trên các 11 | // hàm xử lý. Mỗi hàm xử lý được xây dựng dựa trên 12 | // cấu trúc của `http.Handler`. 13 | // Cách đơn giản nhất để tạo một hàm xử lý là 14 | // sử dụng hàm `http.HandlerFunc` như ví dụ dưới đây: 15 | func hello(w http.ResponseWriter, req *http.Request) { 16 | 17 | // Hàm xử lý sẽ nhận 2 tham số là 18 | // `http.ResponseWriter` và `http.Request`. 19 | // `http.ResponseWriter` sẽ được dùng để trả về 20 | // HTTP response. Ví dụ đơn giản này chỉ trả về 21 | // "hello\n". 22 | fmt.Fprintf(w, "hello\n") 23 | } 24 | 25 | func headers(w http.ResponseWriter, req *http.Request) { 26 | 27 | // Hàm xử lý này sẽ đọc tất cả các header của 28 | // HTTP request và trả về giá trị chúng 29 | // qua HTTP response. 30 | for name, headers := range req.Header { 31 | for _, h := range headers { 32 | fmt.Fprintf(w, "%v: %v\n", name, h) 33 | } 34 | } 35 | } 36 | 37 | func main() { 38 | 39 | // Chúng ta chỉ định hàm xử lý thông qua 40 | // hàm `http.HandleFunc`. Hàm này sẽ nhận 41 | // 2 tham số là đường dẫn và hàm xử lý 42 | // Vd: /hello sẽ được xử lý bởi hàm hello 43 | // và /headers sẽ được xử lý bởi hàm headers 44 | http.HandleFunc("/hello", hello) 45 | http.HandleFunc("/headers", headers) 46 | 47 | // Bước cuối cùng là dùng `ListenAndServe` để 48 | // lắng nghe các request đến port 8090 và xử lý 49 | // chúng thông qua các hàm xử lý đã định nghĩa 50 | http.ListenAndServe(":8090", nil) 51 | } 52 | -------------------------------------------------------------------------------- /examples/http-server/http-server.hash: -------------------------------------------------------------------------------- 1 | e32f39e8336e9cf2b9d0c40189db9a304dfe551c 2 | 8PGsuYZwNNu 3 | -------------------------------------------------------------------------------- /examples/http-server/http-server.sh: -------------------------------------------------------------------------------- 1 | # Run the server in the background. 2 | $ go run http-servers.go & 3 | 4 | # Access the `/hello` route. 5 | $ curl localhost:8090/hello 6 | hello 7 | -------------------------------------------------------------------------------- /examples/if-else/if-else.go: -------------------------------------------------------------------------------- 1 | // Phân nhánh trường hợp sử dụng `if` và `else` trong Go rất đơn giản. 2 | package main 3 | 4 | import "fmt" 5 | 6 | func main() { 7 | 8 | // Dưới đây là một ví dụ căn bản. 9 | if 7%2 == 0 { 10 | fmt.Println("7 is even") 11 | } else { 12 | fmt.Println("7 is odd") 13 | } 14 | 15 | // Bạn có thể có một câu `if` mà không cần `else`. 16 | if 8%4 == 0 { 17 | fmt.Println("8 is divisible by 4") 18 | } 19 | 20 | // Một câu lệnh có thể đứng trước câu điều kiện; bất kỳ biến 21 | // được khai báo trong câu lệnh này đều có sẵn trong nhánh hiện tại 22 | // và tất cả các nhánh tiếp theo. 23 | if num := 9; num < 0 { 24 | fmt.Println(num, "is negative") 25 | } else if num < 10 { 26 | fmt.Println(num, "has 1 digit") 27 | } else { 28 | fmt.Println(num, "has multiple digits") 29 | } 30 | } 31 | 32 | // Lưu ý rằng bạn không cần dấu ngoặc quanh các điều kiện 33 | // trong Go, nhưng bắt buộc phải có dấu ngoặc nhọn. 34 | -------------------------------------------------------------------------------- /examples/if-else/if-else.hash: -------------------------------------------------------------------------------- 1 | 58ad995cee33fd5a8e45175e73157e234c45ff0d 2 | rkwHsGiIX4r 3 | -------------------------------------------------------------------------------- /examples/if-else/if-else.sh: -------------------------------------------------------------------------------- 1 | $ go run if-else.go 2 | 7 is odd 3 | 8 is divisible by 4 4 | 9 has 1 digit 5 | 6 | # Không có [tenary if](https://vi.wikipedia.org/wiki/%3F:) 7 | # trong Go, vì vậy bạn cần phải sử dụng câu lệnh `if` 8 | # đầy đủ ngay cả cho các điều kiện cơ bản. 9 | -------------------------------------------------------------------------------- /examples/interfaces/interfaces.go: -------------------------------------------------------------------------------- 1 | // _Interfaces_ là tên tập hợp của các 2 | // phương thức đặc trưng. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "math" 8 | ) 9 | 10 | // Đây là một interface cơ bản cho các hình học. 11 | type geometry interface { 12 | area() float64 13 | perim() float64 14 | } 15 | 16 | // Ví dụ của chúng ta sẽ thực hiện interface này trên 17 | // kiểu dữ liệu `rect` and `circle`. 18 | type rect struct { 19 | width, height float64 20 | } 21 | type circle struct { 22 | radius float64 23 | } 24 | 25 | // Để hiện thực một interface trong Go, chúng ta chỉ cần 26 | // thực hiện tất cả các phương thức trong giao diện. Ở đây, 27 | // chúng ta hiện thực `geometry` trên kiểu `rect`s. 28 | func (r rect) area() float64 { 29 | return r.width * r.height 30 | } 31 | func (r rect) perim() float64 { 32 | return 2*r.width + 2*r.height 33 | } 34 | 35 | // Hiện thực cho kiểu `circle`s. 36 | func (c circle) area() float64 { 37 | return math.Pi * c.radius * c.radius 38 | } 39 | func (c circle) perim() float64 { 40 | return 2 * math.Pi * c.radius 41 | } 42 | 43 | // Nếu một biến có kiểu interface, thì chúng ta có thể gọi 44 | // phương thức trong interface. Đây là 45 | // hàm tổng quát `measure` có thể tận dụng tính năng này 46 | // để hoạt động trên bất kì `geometry`. 47 | func measure(g geometry) { 48 | fmt.Println(g) 49 | fmt.Println(g.area()) 50 | fmt.Println(g.perim()) 51 | } 52 | 53 | func main() { 54 | r := rect{width: 3, height: 4} 55 | c := circle{radius: 5} 56 | 57 | // Cả hai kiểu cấu trúc `circle` và `rect` 58 | // đều hiện thực geometry interface nên chúng ta có thể 59 | // sử dụng phiên bản của các cấu trúc này như là 60 | // đối số `measure`. 61 | measure(r) 62 | measure(c) 63 | } 64 | -------------------------------------------------------------------------------- /examples/interfaces/interfaces.hash: -------------------------------------------------------------------------------- 1 | 2f6d94ffad2bfce31d1793f21baff6cd74c757e7 2 | 5RXmTdUeyi5 3 | -------------------------------------------------------------------------------- /examples/interfaces/interfaces.sh: -------------------------------------------------------------------------------- 1 | $ go run interfaces.go 2 | {3 4} 3 | 12 4 | 14 5 | {5} 6 | 78.53981633974483 7 | 31.41592653589793 8 | 9 | # Để học nhiều hơn về Go's interfaces, xem thêm cái này 10 | # [bài đăng hay](https://jordanorelli.tumblr.com/post/32665860244/how-to-use-interfaces-in-go). -------------------------------------------------------------------------------- /examples/json/json.go: -------------------------------------------------------------------------------- 1 | // Go có các gói hỗ trợ sẵn cho việc 2 | // mã hóa và giải mã JSON, bao gồm cả việc 3 | // mã hóa và giải mã từ kiểu dữ liệu có sẵn 4 | // và kiểu dữ liệu tùy chỉnh. 5 | package main 6 | 7 | import ( 8 | "encoding/json" 9 | "fmt" 10 | "os" 11 | ) 12 | 13 | // Chúng ta sẽ sử dụng hai struct để minh họa việc mã hóa 14 | // và giải mã kiểu dữ liệu tùy chỉnh bên dưới. 15 | type response1 struct { 16 | Page int 17 | Fruits []string 18 | } 19 | 20 | // Chỉ có các trường đã được export mới được 21 | // mã hóa/giải mã trong JSON. Các trường phải 22 | // bắt đầu bằng chữ hoa để được export. 23 | type response2 struct { 24 | Page int `json:"page"` 25 | Fruits []string `json:"fruits"` 26 | } 27 | 28 | func main() { 29 | 30 | // First we'll look at encoding basic data types to 31 | // JSON strings. Here are some examples for atomic 32 | // values. 33 | // Đầu tiên ta sẽ xem cách mã hóa các kiểu dữ liệu cơ bản 34 | // thành chuỗi JSON. Đây là một số ví dụ về mã hoá 35 | // các giá trị thuộc kiểu nguyên thuỷ. 36 | bolB, _ := json.Marshal(true) 37 | fmt.Println(string(bolB)) 38 | 39 | intB, _ := json.Marshal(1) 40 | fmt.Println(string(intB)) 41 | 42 | fltB, _ := json.Marshal(2.34) 43 | fmt.Println(string(fltB)) 44 | 45 | strB, _ := json.Marshal("gopher") 46 | fmt.Println(string(strB)) 47 | 48 | // Và dưới đây là một số ví dụ về các mảng và 49 | // các map, chúng sẽ được mã hoá thành các mảng JSON và 50 | // các object như mong đợi. 51 | slcD := []string{"apple", "peach", "pear"} 52 | slcB, _ := json.Marshal(slcD) 53 | fmt.Println(string(slcB)) 54 | 55 | mapD := map[string]int{"apple": 5, "lettuce": 7} 56 | mapB, _ := json.Marshal(mapD) 57 | fmt.Println(string(mapB)) 58 | 59 | // Package JSON có thể tự động mã hoá các 60 | // kiểu dữ liệu tuỳ chỉnh của bạn. Nó sẽ chỉ bao gồm 61 | // các trường được export trong đầu ra đã mã hoá và sẽ 62 | // mặc định sử dụng tên các trường đó cho các khóa (key) 63 | // trong JSON. 64 | res1D := &response1{ 65 | Page: 1, 66 | Fruits: []string{"apple", "peach", "pear"}} 67 | res1B, _ := json.Marshal(res1D) 68 | fmt.Println(string(res1B)) 69 | 70 | // Bạn có thể sử dụng các tag trên các trường của struct 71 | // lúc khai báo để tùy chỉnh các tên khóa (key) trong 72 | // chuỗi JSON đã được mã hoá. Hãy xem khai báo của `response2` 73 | // ở trên để xem ví dụ về các tag như vậy. 74 | res2D := &response2{ 75 | Page: 1, 76 | Fruits: []string{"apple", "peach", "pear"}} 77 | res2B, _ := json.Marshal(res2D) 78 | fmt.Println(string(res2B)) 79 | 80 | // Bây giờ hãy xem cách giải mã dữ liệu JSON thành các 81 | // giá trị trong Go. Đây là một ví dụ về 82 | // một cấu trúc dữ liệu kiểu generic. 83 | byt := []byte(`{"num":6.13,"strs":["a","b"]}`) 84 | 85 | // Chúng ta cần cung cấp một biến mà package JSON có thể 86 | // truyền vào dữ liệu đã được giải mã. Biến `map[string]interface{}` 87 | // ở đây sẽ chứa một map các chuỗi tới các kiểu dữ liệu 88 | // bất kỳ. 89 | var dat map[string]interface{} 90 | 91 | // Dưới đây là đoạn giải mã, và kiểm tra các lỗi 92 | // kèm theo. 93 | if err := json.Unmarshal(byt, &dat); err != nil { 94 | panic(err) 95 | } 96 | fmt.Println(dat) 97 | 98 | // Để dùng các giá trị trong một map đã được giải mã, 99 | // ta cần phải chuyển chúng về kiểu dữ liệu phù hợp. 100 | // Ở ví dụ dưới đây, ta chuyển giá trị của `num` về 101 | // kiểu `float64` mong muốn. 102 | num := dat["num"].(float64) 103 | fmt.Println(num) 104 | 105 | // Để truy cập vào dữ liệu bị lồng nhau cần 106 | // thực hiện một chuỗi các chuyển đổi. 107 | strs := dat["strs"].([]interface{}) 108 | str1 := strs[0].(string) 109 | fmt.Println(str1) 110 | 111 | // Ta cũng có thể giải mã JSON thành các kiểu dữ liệu 112 | // tuỷ chỉnh. Điều này sẽ giúp ta có thêm một lớp 113 | // kiểm tra kiểu dữ liệu cho chương trình của mình và không cần 114 | // phải sử dụng các câu lệnh kiểm tra kiểu dữ liệu khi truy cập 115 | // vào dữ liệu đã được giải mã. 116 | str := `{"page": 1, "fruits": ["apple", "peach"]}` 117 | res := response2{} 118 | json.Unmarshal([]byte(str), &res) 119 | fmt.Println(res) 120 | fmt.Println(res.Fruits[0]) 121 | 122 | // Trong các ví dụ trên, các bytes và các chuỗi thường 123 | // được dùng làm trung gian giữa dữ liệu và biểu diễn JSON 124 | // trên đầu ra chuẩn. Chúng ta cũng có thể stream các 125 | // biểu diễn JSON trực tiếp đến các `os.Writer` như 126 | // `os.Stdout` hoặc thậm chí phần body của các 127 | // HTTP response. 128 | enc := json.NewEncoder(os.Stdout) 129 | d := map[string]int{"apple": 5, "lettuce": 7} 130 | enc.Encode(d) 131 | } 132 | -------------------------------------------------------------------------------- /examples/json/json.hash: -------------------------------------------------------------------------------- 1 | 6fc41f844b11bbaa2ce663a34fef284df74e710f 2 | QK6telOGvrb 3 | -------------------------------------------------------------------------------- /examples/json/json.sh: -------------------------------------------------------------------------------- 1 | $ go run json.go 2 | true 3 | 1 4 | 2.34 5 | "gopher" 6 | ["apple","peach","pear"] 7 | {"apple":5,"lettuce":7} 8 | {"Page":1,"Fruits":["apple","peach","pear"]} 9 | {"page":1,"fruits":["apple","peach","pear"]} 10 | map[num:6.13 strs:[a b]] 11 | 6.13 12 | a 13 | {1 [apple peach]} 14 | apple 15 | {"apple":5,"lettuce":7} 16 | 17 | # Chúng ta đã giới thiệu cơ bản về JSON trong Go, 18 | # nhưng bạn hãy đọc các bài viết 19 | # [JSON and Go](https://go.dev/blog/json) 20 | # và [JSON package docs](https://pkg.go.dev/encoding/json) 21 | # để biết thêm. -------------------------------------------------------------------------------- /examples/line-filters/line-filters.go: -------------------------------------------------------------------------------- 1 | // Một _line filter_ (bộ lọc dòng) là chương trình phổ biến đọc 2 | // đầu vào từ stdin, xử lí và đưa ra kết quả được 3 | // dẫn xuất trên stdout. `grep` and `sed` là các 4 | // line filter phổ biến 5 | 6 | // Dưới đây là một ví dụ line filter trong Go để viết một 7 | // một phiên bản viết hoa của toàn bộ văn bản đầu vào. Bạn 8 | // có thể sử dụng mô hình này để viết các line filter Go 9 | // của riêng bạn. 10 | package main 11 | 12 | import ( 13 | "bufio" 14 | "fmt" 15 | "os" 16 | "strings" 17 | ) 18 | 19 | func main() { 20 | 21 | // Bao bọc `os.Stdin` không bộ đệm với một scanner 22 | // (bộ quét) có bộ đệm đem lại cho chúng ta một 23 | // phương thức quét thuận tiện mà tiến hành scanner đến 24 | // thông tin tiếp theo; mặc định là dòng tiếp theo trong 25 | // scanner. 26 | scanner := bufio.NewScanner(os.Stdin) 27 | 28 | for scanner.Scan() { 29 | // `Text` trả về thông tin hiện tại, ở đây là dòng 30 | // dòng tiếp theo, từ đầu vào. 31 | ucl := strings.ToUpper(scanner.Text()) 32 | 33 | // Ghi ra dòng chữ viết hoa. 34 | fmt.Println(ucl) 35 | } 36 | 37 | // Kiểm tra lối trong quá trình `Scan` (quét). Kết thúc 38 | // tập tin được mong đợi và không được báo lõi bởi `Scan`. 39 | if err := scanner.Err(); err != nil { 40 | fmt.Fprintln(os.Stderr, "error:", err) 41 | os.Exit(1) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /examples/line-filters/line-filters.hash: -------------------------------------------------------------------------------- 1 | 730770afae93b1b11f1e6c4f68b9700ef2bb4e25 2 | N2XyzVaQR-j 3 | -------------------------------------------------------------------------------- /examples/line-filters/line-filters.sh: -------------------------------------------------------------------------------- 1 | # Để thử line filter chúng ta, trước tiên hãy tạo một 2 | # tập tin với vài dòng chữ thường. 3 | $ echo 'hello' > /tmp/lines 4 | $ echo 'filter' >> /tmp/lines 5 | 6 | # Sau đó sử dụng line filter để lấy các dòng chữ in hoa. 7 | $ cat /tmp/lines | go run line-filters.go 8 | HELLO 9 | FILTER -------------------------------------------------------------------------------- /examples/maps/maps.go: -------------------------------------------------------------------------------- 1 | // _Maps_ là [kiểu dữ liệu liên kết] được tích hợp sẵn của Go(https://en.wikipedia.org/wiki/Associative_array) 2 | // (đôi khi còn được gọi là _hashes_ hoặc _dicts_ trong các ngôn ngữ khác). 3 | package main 4 | 5 | import "fmt" 6 | 7 | func main() { 8 | 9 | // Để tạo một map rỗng, sử dụng hàm tích hợp `make`: 10 | // `make(map[key-type]val-type)`. 11 | m := make(map[string]int) 12 | 13 | // Set các cặp key/value sử dụng cú pháp `name[key] = val`. 14 | m["k1"] = 7 15 | m["k2"] = 13 16 | 17 | // In ra một map với `fmt.Println` sẽ thể hiện tất cả 18 | // các cặp key/value. 19 | fmt.Println("map:", m) 20 | 21 | // Lấy một giá trị của một key bằng `name[key]`. 22 | v1 := m["k1"] 23 | fmt.Println("v1:", v1) 24 | 25 | // Nếu key không tồn tại, thì giá trị zero (zero value) 26 | // (https://go.dev/ref/spec#The_zero_value) của loại value đó 27 | // sẽ được trả về. 28 | v3 := m["k3"] 29 | fmt.Println("v3:", v3) 30 | 31 | // Hàm tích hợp `len` trả lại số lượng cặp key/value 32 | // khi sử dụng cho map. 33 | fmt.Println("len:", len(m)) 34 | 35 | // Hàm tích hợp `delete` xóa cặp key/value khỏi 36 | // map. 37 | delete(m, "k2") 38 | fmt.Println("map:", m) 39 | 40 | // Giá trị trả về thứ hai tùy chọn khi nhận được một 41 | // giá trị từ map cho biết liệu key có tồn tại hay không 42 | // trong map. Điều này có thể được sử dụng để phân biệt 43 | // giữa các key bị thiếu và các key có giá trị bằng 0 44 | // như `0` hoặc `""`. Ở đây chúng ta không cần giá trị của 45 | // chính nó, vì vậy chúng ta bỏ qua nó với định danh _blank_ 46 | //`_`. 47 | _, prs := m["k2"] 48 | fmt.Println("prs:", prs) 49 | 50 | // Bạn có thể khai báo và khởi tạo một map mới 51 | // bằng một dòng với cú pháp này. 52 | n := map[string]int{"foo": 1, "bar": 2} 53 | fmt.Println("map:", n) 54 | } 55 | -------------------------------------------------------------------------------- /examples/maps/maps.hash: -------------------------------------------------------------------------------- 1 | 722fa3f447c88677c729aa7f7b7b234247318374 2 | 2h-algxdvBg 3 | -------------------------------------------------------------------------------- /examples/maps/maps.sh: -------------------------------------------------------------------------------- 1 | # Lưu ý rằng map thể hiện dưới dạng `map[k:v k:v]` khi 2 | # được in với `fmt.Println` 3 | $ go run maps.go 4 | map: map[k1:7 k2:13] 5 | v1: 7 6 | v3: 0 7 | len: 2 8 | map: map[k1:7] 9 | prs: false 10 | map: map[bar:2 foo:1] 11 | -------------------------------------------------------------------------------- /examples/methods/methods.go: -------------------------------------------------------------------------------- 1 | // Go hỗ trợ _phương thức_ được khai báo trên các kiểu `struct`. 2 | package main 3 | 4 | import "fmt" 5 | 6 | type rect struct { 7 | width, height int 8 | } 9 | 10 | // Phương thức `area` có _kiểu tham số đầu vào (receiver type)_ là `*rect`. 11 | func (r *rect) area() int { 12 | return r.width * r.height 13 | } 14 | 15 | // Phương thức có thể được khai báo với receiver type là con trỏ 16 | // hoặc giá trị. Dưới đây là một ví dụ về receiver type là giá trị. 17 | func (r rect) perim() int { 18 | return 2*r.width + 2*r.height 19 | } 20 | 21 | func main() { 22 | r := rect{width: 10, height: 5} 23 | 24 | // Ở đây ta gọi 2 phương thức đã được định nghĩa cho kiểu `struct` của ta. 25 | fmt.Println("area: ", r.area()) 26 | fmt.Println("perim:", r.perim()) 27 | 28 | // Go tự động chuyển đổi giữa giá trị và con trỏ khi 29 | // phương thức được gọi. Bạn có thể muốn sử dụng receiver 30 | // type là con trỏ để tránh việc sao chép khi phương thức 31 | // được gọi đến hoặc để cho phép phương thức thay đổi giá 32 | // trị của struct. 33 | rp := &r 34 | fmt.Println("area: ", rp.area()) 35 | fmt.Println("perim:", rp.perim()) 36 | } 37 | -------------------------------------------------------------------------------- /examples/methods/methods.hash: -------------------------------------------------------------------------------- 1 | 7e6753790bc4c798761550b0e38f5c01309ef162 2 | iPSI2FRFs4g 3 | -------------------------------------------------------------------------------- /examples/methods/methods.sh: -------------------------------------------------------------------------------- 1 | $ go run methods.go 2 | area: 50 3 | perim: 30 4 | area: 50 5 | perim: 30 6 | 7 | # Tiếp theo chúng ta sẽ xem xét một cơ chế của Go 8 | # để gom và đặt tên các tập hợp các phương thức 9 | # liên quan đến nhau: interface. -------------------------------------------------------------------------------- /examples/multiple-return-values/multiple-return-values.go: -------------------------------------------------------------------------------- 1 | // Go có hỗ trợ tích hợp cho _multiple return values_. 2 | // Tính năng được sử dụng thuờng xuyên trong Go, ví dụ 3 | // để trả về cả giá trị kết quả và giá trị lỗi của một hàm. 4 | 5 | package main 6 | 7 | import "fmt" 8 | 9 | // Phần `(int, int)` trong hàm này thể hiện rằng hàm 10 | // trả về 2 giá trị `int`. 11 | func vals() (int, int) { 12 | return 3, 7 13 | } 14 | 15 | func main() { 16 | 17 | // Ở đây chúng ta sử dụng 2 giá trị trả về khác nhau từ 18 | // một hàm gọi với _multiple assignment_. 19 | a, b := vals() 20 | fmt.Println(a) 21 | fmt.Println(b) 22 | 23 | // Nếu bạn chỉ muốn một tập hợp con của các giá trị 24 | // được trả về, hãy sử dụng blank identifier `_`. 25 | _, c := vals() 26 | fmt.Println(c) 27 | } 28 | -------------------------------------------------------------------------------- /examples/multiple-return-values/multiple-return-values.hash: -------------------------------------------------------------------------------- 1 | 97af96b7755c04f5ab030b5ede5fc36b84fe72ee 2 | kXPbSa_jKcy 3 | -------------------------------------------------------------------------------- /examples/multiple-return-values/multiple-return-values.sh: -------------------------------------------------------------------------------- 1 | $ go run multiple-return-values.go 2 | 3 3 | 7 4 | 7 5 | 6 | # Chấp nhận một biến số của tham số là tính năng thú vị 7 | # khác của hàm Go; chúng ta sẽ xem qua ở bài tiếp theo. 8 | -------------------------------------------------------------------------------- /examples/mutexes/mutexes.go: -------------------------------------------------------------------------------- 1 | // Trong ví dụ trước, chúng ta đã thấy cách quản lý 2 | // trạng thái bộ đếm đơn giản bằng cách sử dụng [atomic operations](atomic-counters). 3 | // Đối với trạng thái phức tạp hơn, chúng ta có thể sử dụng một [_mutex_](https://en.wikipedia.org/wiki/Mutual_exclusion) 4 | // để truy cập dữ liệu một cách an toàn giữa nhiều goroutines. 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "sync" 10 | ) 11 | 12 | // Struct Container chứa một map các counters; vì chúng ta muốn 13 | // cập nhật nó đồng thời từ nhiều goroutines, chúng ta 14 | // thêm một `Mutex` để đồng bộ hóa truy cập vào nó. 15 | // Lưu ý rằng không được copy mutexes, nên nếu 16 | // `struct` này được truyền đi chỗ khác, ta nên sử dụng 17 | // con trỏ. 18 | type Container struct { 19 | mu sync.Mutex 20 | counters map[string]int 21 | } 22 | 23 | func (c *Container) inc(name string) { 24 | // Khoá mutex trước khi truy cập `counters`; giải phóng 25 | // nó ở cuối hàm bằng cách sử dụng một câu lệnh [defer](defer). 26 | c.mu.Lock() 27 | defer c.mu.Unlock() 28 | c.counters[name]++ 29 | } 30 | 31 | func main() { 32 | c := Container{ 33 | // Lưu ý rằng ta có thể sử dụng giá trị mặc định là không của mutex, do đó 34 | // không cần phải khởi tạo nó. 35 | counters: map[string]int{"a": 0, "b": 0}, 36 | } 37 | 38 | var wg sync.WaitGroup 39 | 40 | // Hàm sau đây làm tăng giá trị của một biến đếm 41 | // trong một vòng lặp. 42 | doIncrement := func(name string, n int) { 43 | for i := 0; i < n; i++ { 44 | c.inc(name) 45 | } 46 | wg.Done() 47 | } 48 | 49 | // Thực thi đồng thời nhiều goroutines; lưu ý rằng 50 | // chúng đều truy cập vào cùng một `Container`, 51 | // và hai trong số chúng truy cập vào cùng một biến đếm. 52 | wg.Add(3) 53 | go doIncrement("a", 10000) 54 | go doIncrement("a", 10000) 55 | go doIncrement("b", 10000) 56 | 57 | // Chờ cho các goroutines chạy xong. 58 | wg.Wait() 59 | fmt.Println(c.counters) 60 | } 61 | -------------------------------------------------------------------------------- /examples/mutexes/mutexes.hash: -------------------------------------------------------------------------------- 1 | 854164eb223fa21b1cae23ea656520db584b9e5e 2 | CrVRYEV4j80 3 | -------------------------------------------------------------------------------- /examples/mutexes/mutexes.sh: -------------------------------------------------------------------------------- 1 | # Chạy chương trình sẽ cho thấy rằng các biến đếm 2 | # được cập nhật như mong đợi. 3 | $ go run mutexes.go 4 | map[a:20000 b:10000] 5 | 6 | # Tiếp theo chúng ta sẽ xem xét 7 | # việc thực hiện cùng một tác vụ 8 | # quản lý trạng thái sử dụng goroutines và channels. -------------------------------------------------------------------------------- /examples/non-blocking-channel-operations/non-blocking-channel-operations.go: -------------------------------------------------------------------------------- 1 | // Thao tác gửi và nhận trên channel là `blocking`. 2 | // [`blocking` trong lập trình là quá trình/thao tác 3 | // khiến chương trình không thể tiếp tục cho đến 4 | // khi hoàn thành] Tuy nhiên, chúng ta có thể sử dụng 5 | // `select` với `default` để thực hiện các thao tác gửi, 6 | // nhận không `blocking`, và cũng có thể thực hiện các 7 | // thao tác `select` không `blocking` trên nhiều channel. 8 | package main 9 | 10 | import "fmt" 11 | 12 | func main() { 13 | messages := make(chan string) 14 | signals := make(chan bool) 15 | 16 | // Đây là một cách nhận giá trị không `blocking` từ 17 | // channel. Nếu một giá trị có sẵn trên `messages` 18 | // thì `select` sẽ chọn trường hợp `<-messages` với 19 | // giá trị đó. Nếu không, sẽ chọn trường hợp `default` 20 | // ngay lập tức. 21 | select { 22 | case msg := <-messages: 23 | fmt.Println("received message", msg) 24 | default: 25 | fmt.Println("no message received") 26 | } 27 | 28 | // Thao tác gửi không `blocking` cũng hoạt động tương tự. 29 | // Ở đây `msg` không thể gửi đến channel `messages`, 30 | // vì channel không có bộ nhớ đệm và không có người nhận. 31 | // Do đó, trường hợp `default` sẽ được chọn. 32 | msg := "hi" 33 | select { 34 | case messages <- msg: 35 | fmt.Println("sent message", msg) 36 | default: 37 | fmt.Println("no message sent") 38 | } 39 | 40 | // Chúng ta có thể sữ dụng nhiều `case` kết hợp `default` 41 | // để thực hiện `select` gửi/nhận không `blocking` trên 42 | // nhiều channel. Chúng ta thử nhận không `blocking` 43 | // cả 2 channel `messages` và `signals`. 44 | select { 45 | case msg := <-messages: 46 | fmt.Println("received message", msg) 47 | case sig := <-signals: 48 | fmt.Println("received signal", sig) 49 | default: 50 | fmt.Println("no activity") 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /examples/non-blocking-channel-operations/non-blocking-channel-operations.hash: -------------------------------------------------------------------------------- 1 | 04dbb312e7835c0f4dcf2c96a59d24cff177a23d 2 | 447s9CZ_sT- 3 | -------------------------------------------------------------------------------- /examples/non-blocking-channel-operations/non-blocking-channel-operations.sh: -------------------------------------------------------------------------------- 1 | $ go run non-blocking-channel-operations.go 2 | no message received 3 | no message sent 4 | no activity 5 | -------------------------------------------------------------------------------- /examples/number-parsing/number-parsing.go: -------------------------------------------------------------------------------- 1 | // Phân tích các số từ chuỗi là một nhiệm vụ cơ bản 2 | // nhưng phổ biến trong nhiều chương trình; 3 | // đây là cách thực hiện nó trong Go. 4 | 5 | package main 6 | 7 | // Package (gói) `strconv` được tích hợp sẵn cung cấp 8 | // phân tích số 9 | import ( 10 | "fmt" 11 | "strconv" 12 | ) 13 | 14 | func main() { 15 | 16 | // Với `ParseFloat`, số `64` cho biết có bao nhiêu bit 17 | // là độ chính xách cần phân tích. 18 | f, _ := strconv.ParseFloat("1.234", 64) 19 | fmt.Println(f) 20 | 21 | // Với `ParseInt`, số `0` có nghĩa là suy ra hệ cơ số 22 | // từ chuỗi. Số `64` yêu cầu kết quả phù hợp trong 64 23 | // bit. 24 | i, _ := strconv.ParseInt("123", 0, 64) 25 | fmt.Println(i) 26 | 27 | // `ParseInt `sẽ nhận ra các số được định dạng hexa. 28 | d, _ := strconv.ParseInt("0x1c8", 0, 64) 29 | fmt.Println(d) 30 | 31 | // `ParseUint` cũng có sẵn. 32 | u, _ := strconv.ParseUint("789", 0, 64) 33 | fmt.Println(u) 34 | 35 | // `Atoi` một hàm hữu ích để phân tích các số nguyên 36 | // cơ bản base-10 37 | k, _ := strconv.Atoi("135") 38 | fmt.Println(k) 39 | 40 | // Các hàm phân tích trả về một lỗi khi gặp đầu vào không hợp lệ. 41 | _, e := strconv.Atoi("wat") 42 | fmt.Println(e) 43 | } 44 | -------------------------------------------------------------------------------- /examples/number-parsing/number-parsing.hash: -------------------------------------------------------------------------------- 1 | ec9a3eda0642e9ee54859517162c3cbfc44fb924 2 | J5IwrWEjh3Q 3 | -------------------------------------------------------------------------------- /examples/number-parsing/number-parsing.sh: -------------------------------------------------------------------------------- 1 | $ go run number-parsing.go 2 | 1.234 3 | 123 4 | 456 5 | 789 6 | 135 7 | strconv.ParseInt: parsing "wat": invalid syntax 8 | 9 | # Tiếp theo, chúng ta sẽ xem một tác vụ phân tích 10 | # phổ biến khác: URLs. -------------------------------------------------------------------------------- /examples/panic/panic.go: -------------------------------------------------------------------------------- 1 | // Một `panic` thương có nghĩa là một cái gì đó xảy ra 2 | // không như mong đợi. Hầu hết chúng ta sử dụng nó để 3 | // fail fast với các lỗi không nên xảy ra trong quá 4 | // trình hoạt động bình thường, hoặc rằng chúng ta không 5 | // được chuẩn bị để xử lý một cách tốt. 6 | 7 | package main 8 | 9 | import "os" 10 | 11 | func main() { 12 | // Chúng ta sẽ dùng panic trên khắp site để kiểm tra 13 | // những lỗi không mong đợi. Đây là chương trình duy 14 | // nhất trên site được thiết kể để panic. 15 | panic("a problem") 16 | 17 | // Một trường hợp thông thường của panic là để hủy 18 | // bỏ nếu một hàm trả về một giá trị error mà chúng 19 | // ta không biết làm thế nào (hoặc muốn) để xử lý. 20 | // Đây là ví dụ của `panic` nếu chúng ta lấy 21 | // một lỗi xảy ra không mong đợi khi tạo file. 22 | _, err := os.Create("/tmp/file") 23 | if err != nil { 24 | panic(err) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /examples/panic/panic.hash: -------------------------------------------------------------------------------- 1 | f745dcaae5da9ef3175cb95c3b9df4cdee317376 2 | CJkK02EpQqs 3 | -------------------------------------------------------------------------------- /examples/panic/panic.sh: -------------------------------------------------------------------------------- 1 | # Chạy chương trình sẽ gây ra panic, in một thông báo 2 | # lỗi và dấu vết goroutine, và thoát với một trạng 3 | # thái non-zero. 4 | 5 | 6 | # Khi panic đầu tiên trong `main` được bắn ra, chương 7 | # trình thoát mà không đạt đến phần còn lại của code. 8 | # Nếu bạn muốn để xem chương trình hãy thửu tạo một 9 | # tệp tạm thời, comment lại cái panic đầu tiên. 10 | 11 | $ go run panic.go 12 | panic: a problem 13 | 14 | goroutine 1 [running]: 15 | main.main() 16 | /.../panic.go:12 +0x47 17 | ... 18 | exit status 2 19 | 20 | # Lưu ý rằng không giống như một số ngôn ngữ sử dụng 21 | # exception để xử lý nhiều lỗi, trong Go nó là idiomatic 22 | # để sử dụng các giá trị trả về chỉ báo lỗi bất cứ 23 | # khi nào có thể. -------------------------------------------------------------------------------- /examples/pointers/pointers.go: -------------------------------------------------------------------------------- 1 | // Go hỗ trợ con trỏ, 2 | // cho phép bạn truyền tham chiếu đến các giá trị và bản ghi 3 | // trong chương trình của bạn. 4 | package main 5 | 6 | import "fmt" 7 | 8 | // Ta sẽ biểu diễn cách mà các con trỏ hoat động so với các giá trị 9 | // với 2 hàm: `zeroval` và `zeroptr`. `zeroval` có một tham số 10 | // kiểu `int`, nên các đối số sẽ được truyền vào bằng giá trị. 11 | // `zeroval` sẽ nhận được một bản sao của `ival` khác biệt 12 | // so với cái trong hàm gọi nó. 13 | func zeroval(ival int) { 14 | ival = 0 15 | } 16 | 17 | // `zeroptr` thì có một tham số kiểu `*int`, nghĩa là nó 18 | // nhận vào đối số là một con trỏ kiểu `int`. Phần `*iptr` trong 19 | // thân hàm sẽ _giải tham chiếu_ con trỏ từ địa chỉ bộ nhớ 20 | // của nó đến giá trị hiện tại tại địa chỉ đó. Gán một giá trị 21 | // cho một con trỏ đã giải tham chiếu sẽ thay đổi giá trị tại 22 | // địa chỉ mà con trỏ tham chiếu đến. 23 | func zeroptr(iptr *int) { 24 | *iptr = 0 25 | } 26 | 27 | func main() { 28 | i := 1 29 | fmt.Println("initial:", i) 30 | 31 | zeroval(i) 32 | fmt.Println("zeroval:", i) 33 | 34 | // Cú pháp `&i` trả về địa chỉ bộ nhớ của `i`, 35 | // tức là một con trỏ đến `i`. 36 | zeroptr(&i) 37 | fmt.Println("zeroptr:", i) 38 | 39 | // Ta cũng có thể in ra các con trỏ. 40 | fmt.Println("pointer:", &i) 41 | } 42 | -------------------------------------------------------------------------------- /examples/pointers/pointers.hash: -------------------------------------------------------------------------------- 1 | c6bc652d9e450e984725a2f84fd2c531d748cff3 2 | n4982n1CsOS 3 | -------------------------------------------------------------------------------- /examples/pointers/pointers.sh: -------------------------------------------------------------------------------- 1 | # `zeroval` không thay đổi `i` trong `main`, nhưng 2 | # `zeroptr` thì có vì nó có một tham chiếu đến 3 | # địa chỉ bộ nhớ của biến `i` đó. 4 | $ go run pointers.go 5 | initial: 1 6 | zeroval: 1 7 | zeroptr: 0 8 | pointer: 0x42131100 -------------------------------------------------------------------------------- /examples/random-numbers/random-numbers.go: -------------------------------------------------------------------------------- 1 | // Go's `math/rand` package (gói) cung cấp tạo 2 | // [số giả ngẫu nhiên](https://en.wikipedia.org/wiki/Pseudorandom_number_generator). 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "math/rand" 9 | "time" 10 | ) 11 | 12 | func main() { 13 | 14 | // Ví dụ, `rand.Intn` trả về một `int` (số nguyên) ngẫu nhiên n, 15 | // `0 <= n < 100`. 16 | fmt.Print(rand.Intn(100), ",") 17 | fmt.Print(rand.Intn(100)) 18 | fmt.Println() 19 | 20 | // `rand.Float64` trả về một `float64` (số thực) ngẫu nhiên `f`, 21 | // `0.0 <= f < 1.0`. 22 | fmt.Println(rand.Float64()) 23 | 24 | // Điều này có thể được sử dụng để tạo ra các số thực 25 | // ngẫu nhiên trong các phạm vi khác, ví dụ `5.0 <= f' < 10.0`. 26 | fmt.Print((rand.Float64()*5)+5, ",") 27 | fmt.Print((rand.Float64() * 5) + 5) 28 | fmt.Println() 29 | 30 | // Bộ tạo số mặc định là xác định được, vì thế mặc định nó sẽ 31 | // tạo ra cùng một chuỗi số mỗi lần. Để tạo ra các chuỗi khác nhau, 32 | // hãy cung cấp cho nó một seed thay đổi. Lưu ý rằng điều này 33 | // không an toàn để sử dụng cho các số ngẫu nhiên mà bạn dự định 34 | // là bí mật; hãy sử dụng `crypto/rand` cho những số đó. 35 | s1 := rand.NewSource(time.Now().UnixNano()) 36 | r1 := rand.New(s1) 37 | 38 | // Kết quả gọi `rand.Rand` giống như nhũng hàm 39 | // trong `rand` package. 40 | fmt.Print(r1.Intn(100), ",") 41 | fmt.Print(r1.Intn(100)) 42 | fmt.Println() 43 | 44 | // Nếu bạn khởi tạo một nguồn với cùng một số, nó 45 | // sẽ tạo ra cùng một chuỗi số ngẫu nhiên. 46 | s2 := rand.NewSource(42) 47 | r2 := rand.New(s2) 48 | fmt.Print(r2.Intn(100), ",") 49 | fmt.Print(r2.Intn(100)) 50 | fmt.Println() 51 | s3 := rand.NewSource(42) 52 | r3 := rand.New(s3) 53 | fmt.Print(r3.Intn(100), ",") 54 | fmt.Print(r3.Intn(100)) 55 | } 56 | -------------------------------------------------------------------------------- /examples/random-numbers/random-numbers.hash: -------------------------------------------------------------------------------- 1 | ec3304a837465a8f85a3bfa6194081f05d4f64de 2 | JXRl6_l44Ww 3 | -------------------------------------------------------------------------------- /examples/random-numbers/random-numbers.sh: -------------------------------------------------------------------------------- 1 | # Tùy thuộc vào môi trường bạn chạy mẫu này, một số 2 | # các số được tạo ra có thể khác nhau. Lưu ý rằng 3 | # trên Go playground, việc khởi tạo với time.Now() 4 | # vẫn cho kết quả xác định được do cách thức do 5 | # cách thức playground được hiện thực. 6 | $ go run random-numbers.go 7 | 81,87 8 | 0.6645600532184904 9 | 7.123187485356329,8.434115364335547 10 | 0,28 11 | 5,87 12 | 5,87 13 | 14 | 15 | # Xem tài liệu [`math/rand`](https://pkg.go.dev/math/rand) 16 | # package để tham khảo về các lượng ngẫu nhiên khác 17 | # mà Go có thể cung cấp. -------------------------------------------------------------------------------- /examples/range-over-channels/range-over-channels.go: -------------------------------------------------------------------------------- 1 | // Trong ví dụ sử dụng (range) trước đó chúng ta đã 2 | // thấy cách dùng `for` và `range` để lặp qua các 3 | // cấu trúc dữ liệu cơ bản. Trong ví dụ này, 4 | // chúng ta cũng có thể sử dụng cú pháp tương tự 5 | // để lặp qua các giá trị được nhận từ một channel. 6 | 7 | package main 8 | 9 | import "fmt" 10 | 11 | func main() { 12 | 13 | // Chúng tôi sẽ lặp qua 2 giá trị trong channel `queue` 14 | queue := make(chan string, 2) 15 | queue <- "one" 16 | queue <- "two" 17 | close(queue) 18 | 19 | // Sử dụng `range` lặp qua từng phần tử khi nó được nhận 20 | // từ `queue`. Vì chúng tôi đã `close` channel ở trên, 21 | // vòng lặp sẽ kết thúc sau khi nhận được 2 phần tử. 22 | for elem := range queue { 23 | fmt.Println(elem) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/range-over-channels/range-over-channels.hash: -------------------------------------------------------------------------------- 1 | f6d01dae87a76b2551101115770b20ab0531670d 2 | 6sZxVfuYxVd 3 | -------------------------------------------------------------------------------- /examples/range-over-channels/range-over-channels.sh: -------------------------------------------------------------------------------- 1 | $ go run range-over-channels.go 2 | one 3 | two 4 | 5 | # This example also showed that it's possible to close 6 | # a non-empty channel but still have the remaining 7 | # values be received. 8 | -------------------------------------------------------------------------------- /examples/range/range.go: -------------------------------------------------------------------------------- 1 | // _range_ lặp qua các phần tử trong nhiều cấu trúc dữ liệu. 2 | // Hãy xem cách sử dụng `range` với một số 3 | // cấu trúc dữ liệu mà chúng ta đã học. 4 | 5 | package main 6 | 7 | import "fmt" 8 | 9 | func main() { 10 | 11 | // Ở đây chúng ta sử dụng `range` để tính tổng số 12 | // trong một slice. 13 | // Array cũng hoạt động giống như vậy. 14 | nums := []int{2, 3, 4} 15 | sum := 0 16 | for _, num := range nums { 17 | sum += num 18 | } 19 | fmt.Println("sum:", sum) 20 | 21 | // `range` trong arrays và slices cấp cả 2 index và 22 | // value cho mỗi phần tử. Ở trên chúng ta không cần 23 | // index, nên chúng ta không quan tâm tới 24 | // blank identifier `_`. Nhưng tuỳ vào tình 25 | // huống khác thì có thể chúng ta sẽ cần index. 26 | for i, num := range nums { 27 | if num == 3 { 28 | fmt.Println("index:", i) 29 | } 30 | } 31 | 32 | // Sử dụng `range` trên map lặp qua các cặp key/value. 33 | kvs := map[string]string{"a": "apple", "b": "banana"} 34 | for k, v := range kvs { 35 | fmt.Printf("%s -> %s\n", k, v) 36 | } 37 | 38 | // `range` cũng có thể lặp qua các key của một map. 39 | for k := range kvs { 40 | fmt.Println("key:", k) 41 | } 42 | 43 | // Sử dụng `range` trên chuỗi các ký tự Unicode. 44 | // Giá trị đầu tiên là chỉ mục byte bắt đầu của `rune` 45 | // và thứ hai của chính `rune`. 46 | // Xem qua [Strings and Runes](strings-and-runes) để 47 | // biết thêm chi tiết. 48 | for i, c := range "go" { 49 | fmt.Println(i, c) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /examples/range/range.hash: -------------------------------------------------------------------------------- 1 | 0f32b4e1d02620967f5595ed35ec545929b001cb 2 | iBFQWRoL0WQ 3 | -------------------------------------------------------------------------------- /examples/range/range.sh: -------------------------------------------------------------------------------- 1 | $ go run range.go 2 | sum: 9 3 | index: 1 4 | a -> apple 5 | b -> banana 6 | key: a 7 | key: b 8 | 0 103 9 | 1 111 10 | -------------------------------------------------------------------------------- /examples/rate-limiting/rate-limiting.go: -------------------------------------------------------------------------------- 1 | // [_Rate limiting_](https://en.wikipedia.org/wiki/Rate_limiting) 2 | // là một cơ chế quan trọng để kiểm soát việc sử dụng tài nguyên 3 | // và duy trì chất lượng của một service. Go 4 | // hỗ trợ rate limiting một cách rất tinh tế với goroutines, 5 | // channels, và [tickers](tickers). 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "time" 12 | ) 13 | 14 | func main() { 15 | 16 | // Đầu tiên, chúng ta sẽ xem xét một cách cơ bản để thực hiện rate limiting. 17 | // Giả sử chúng ta muốn giới hạn việc xử lý các request gửi đến. 18 | // Chúng ta sẽ truyền các request này qua một channel 19 | // có cùng tên. 20 | requests := make(chan int, 5) 21 | for i := 1; i <= 5; i++ { 22 | requests <- i 23 | } 24 | close(requests) 25 | 26 | // Đây là channel `limiter` sẽ nhận giá trị 27 | // mỗi 200 mili giây. Đây là một cơ chế kiểm soát tốc độ 28 | // trong việc thực hiện rate limiting của chúng ta. 29 | limiter := time.Tick(200 * time.Millisecond) 30 | 31 | // Bằng cách chặn việc nhận giá trị từ channel `limiter` 32 | // trước khi xử lý mỗi request, chúng ta sẽ giới hạn việc 33 | // xử lý một request mỗi 200 mili giây. 34 | for req := range requests { 35 | <-limiter 36 | fmt.Println("request", req, time.Now()) 37 | } 38 | 39 | // Chúng ta có thể muốn cho phép thực hiến một số lượng lớn request trong 40 | // một khoảng thời gian ngắn trong khi thực hiện nguyên tắc rate limiting 41 | // mà ta đã đặt ra mà vẫn đảm bảo được rate limit của hệ thống. Ta có thể làm được điều này 42 | // bằng cách sử dụng một buffered channel `burstyLimiter`. Channel `burstyLimiter` này 43 | // sẽ cho phép thực hiện một số lượng lớn request lên đến 3 request trong 1 thời điểm. 44 | 45 | burstyLimiter := make(chan time.Time, 3) 46 | 47 | // Hãy lấp đầy channel để tái hiện việc thực hiện một số lượng lớn request. 48 | for i := 0; i < 3; i++ { 49 | burstyLimiter <- time.Now() 50 | } 51 | 52 | // Mỗi 200 mili giây, ta sẽ thêm một giá trị mới 53 | // vào channel `burstyLimiter`, cho đến khi nó đạt đến giới hạn là 3. 54 | go func() { 55 | for t := range time.Tick(200 * time.Millisecond) { 56 | burstyLimiter <- t 57 | } 58 | }() 59 | 60 | // Bây giờ, ta sẽ giả định thêm 5 request được gửi đến. 61 | // Ba request đầu tiên sẽ hưởng lợi từ khả năng tăng tốc 62 | // của channel `burstyLimiter`. 63 | burstyRequests := make(chan int, 5) 64 | for i := 1; i <= 5; i++ { 65 | burstyRequests <- i 66 | } 67 | close(burstyRequests) 68 | for req := range burstyRequests { 69 | <-burstyLimiter 70 | fmt.Println("request", req, time.Now()) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /examples/rate-limiting/rate-limiting.hash: -------------------------------------------------------------------------------- 1 | b304529a4ae27deded475e2fec9d0c64ec1a98bd 2 | fzWTS2iii1W 3 | -------------------------------------------------------------------------------- /examples/rate-limiting/rate-limiting.sh: -------------------------------------------------------------------------------- 1 | # Khi thực thi chương trình, 2 | # chúng ta thấy rằng các yêu cầu 3 | # của batch đầu tiên được xử lý 4 | # một lần mỗi 200 mili giây như mong muốn. 5 | $ go run rate-limiting.go 6 | request 1 2012-10-19 00:38:18.687438 +0000 UTC 7 | request 2 2012-10-19 00:38:18.887471 +0000 UTC 8 | request 3 2012-10-19 00:38:19.087238 +0000 UTC 9 | request 4 2012-10-19 00:38:19.287338 +0000 UTC 10 | request 5 2012-10-19 00:38:19.487331 +0000 UTC 11 | 12 | # Đối với batch request thứ hai, 13 | # chúng ta xử lý 3 request đầu tiên 14 | # ngay lập tức nhờ vào khả năng 15 | # tăng tốc của rate limiting, sau đó xử lý 2 request 16 | # còn lại với khoảng thời gian trễ ~200ms mỗi request. 17 | request 1 2012-10-19 00:38:20.487578 +0000 UTC 18 | request 2 2012-10-19 00:38:20.487645 +0000 UTC 19 | request 3 2012-10-19 00:38:20.487676 +0000 UTC 20 | request 4 2012-10-19 00:38:20.687483 +0000 UTC 21 | request 5 2012-10-19 00:38:20.887542 +0000 UTC -------------------------------------------------------------------------------- /examples/reading-files/reading-files.go: -------------------------------------------------------------------------------- 1 | // Đọc và ghi tập tin là các nhiệm vụ cơ bản cần thiết 2 | // cho nhiều chương trình Go. Đầu tiên, chúng ta sẽ xem 3 | // một số ví dụ về đọc tập tin. 4 | 5 | package main 6 | 7 | import ( 8 | "bufio" 9 | "fmt" 10 | "io" 11 | "os" 12 | ) 13 | 14 | // Đọc tập tin yêu cầu kiểm tra hầu hết các lần gọi hàm 15 | // để tìm lỗi. Hàm hỗ trợ này sẽ giúp việc tìm lỗi của 16 | // chúng ta dễ dàng hơn. 17 | func check(e error) { 18 | if e != nil { 19 | panic(e) 20 | } 21 | } 22 | 23 | func main() { 24 | 25 | // Có lẽ nhiệm vụ cơ bản nhất của đọc tập tin là 26 | // đọc toàn bộ nội dung tập tin. 27 | dat, err := os.ReadFile("/tmp/dat") 28 | check(err) 29 | fmt.Print(string(dat)) 30 | 31 | // Bạn thường muốn kiểm soát hơn về cách và những phần 32 | // của tập tin được đọc. Về những nhiệm vụ này, trước hết 33 | // bạn cần `mở` tập tin để lấy giá trị `os.File`. 34 | f, err := os.Open("/tmp/dat") 35 | check(err) 36 | 37 | // Đọc một số byte từ đầu tập tin. Cho phép đọc tối đa 38 | // 5 byte nhưng cũng ghi nhận số byte thực sự đã được đọc. 39 | b1 := make([]byte, 5) 40 | n1, err := f.Read(b1) 41 | check(err) 42 | fmt.Printf("%d bytes: %s\n", n1, string(b1[:n1])) 43 | 44 | // Bạn cũng có thể sử dụng `Seek` để tìm vị trí trong tập tin 45 | // và `đọc` từ đó. 46 | o2, err := f.Seek(6, 0) 47 | check(err) 48 | b2 := make([]byte, 2) 49 | n2, err := f.Read(b2) 50 | check(err) 51 | fmt.Printf("%d bytes @ %d: ", n2, o2) 52 | fmt.Printf("%v\n", string(b2[:n2])) 53 | 54 | // Package (gói) `io` cung cấp một số hàm có thể hữu ích 55 | // cho việc đọc tập tin. Ví dụ, các tác vụ đọc như trên 56 | // có thể được hiện thực một cách đáng tin cậy hơn 57 | // với hàm `ReadAtLeast`. 58 | o3, err := f.Seek(6, 0) 59 | check(err) 60 | b3 := make([]byte, 2) 61 | n3, err := io.ReadAtLeast(f, b3, 2) 62 | check(err) 63 | fmt.Printf("%d bytes @ %d: %s\n", n3, o3, string(b3)) 64 | 65 | // Không có chức năng quay lại tích hợp sẵn, 66 | // nhưng hàm`Seek(0, 0)` có thể thực hiện điều này. 67 | _, err = f.Seek(0, 0) 68 | check(err) 69 | 70 | // Package `bufio` triển khai một bộ đệm đọc có thể 71 | // hữu ích vừa vì tính hiệu quả với nhiều lần đọc nhỏ, 72 | // vừa vì các phương thức đọc bổ sung mà nó cung cấp 73 | r4 := bufio.NewReader(f) 74 | b4, err := r4.Peek(5) 75 | check(err) 76 | fmt.Printf("5 bytes: %s\n", string(b4)) 77 | 78 | // Đóng tập tin khi bạn hoàn thành (thông thường 79 | // điều này sẽ được lên kế hoạch ngay sau khi `mở` 80 | // tập tin với `defer`). 81 | f.Close() 82 | } 83 | -------------------------------------------------------------------------------- /examples/reading-files/reading-files.hash: -------------------------------------------------------------------------------- 1 | 155ce72efba0a23397b452329c031a7f6e6f9819 2 | EXhXECaVdF5 3 | -------------------------------------------------------------------------------- /examples/reading-files/reading-files.sh: -------------------------------------------------------------------------------- 1 | $ echo "hello" > /tmp/dat 2 | $ echo "go" >> /tmp/dat 3 | $ go run reading-files.go 4 | hello 5 | go 6 | 5 bytes: hello 7 | 2 bytes @ 6: go 8 | 2 bytes @ 6: go 9 | 5 bytes: hello 10 | 11 | # Tiếp theo chúng ta tìm hiểu về ghi tập tin. -------------------------------------------------------------------------------- /examples/recover/recover.go: -------------------------------------------------------------------------------- 1 | // Go có thể khiến cho việc _khôi phục_ từ một panic là khả thi, 2 | // bằng cách sử dụng hàm `recover` được tích hợp sẵn. Hàm `recover` có thể 3 | // ngăn không cho `panic` ngắt chương trình và cho phép chương trình 4 | // tiếp tục thực thi. 5 | 6 | // Một ví dụ về việc áp dụng vào thực tế: khi một server 7 | // không muốn bị sập khi một trong những kết nối từ client 8 | // bị lỗi nghiêm trọng. Thay vào đó, server sẽ 9 | // muốn đóng kết nối đó và tiếp tục phục vụ các client khác. 10 | // Thực tế, đây là cách mặc định mà thư viện `net/http` của Go 11 | // thực hiện cho các HTTP server. 12 | 13 | package main 14 | 15 | import "fmt" 16 | 17 | // Hàm này sẽ gây ra một panic. 18 | func mayPanic() { 19 | panic("a problem") 20 | } 21 | 22 | func main() { 23 | // `recover` phải được gọi bên trong một hàm `defer`. 24 | // Khi hàm bên ngoài gây ra một panic, `defer` sẽ được 25 | // kích hoạt và một lệnh `recover` bên trong nó sẽ bắt được 26 | // và xử lý panic đó. 27 | defer func() { 28 | if r := recover(); r != nil { 29 | // Giá trị trả về của `recover` là lỗi được đề cập đến 30 | // trong lệnh `panic`. 31 | fmt.Println("Recovered. Error:\n", r) 32 | } 33 | }() 34 | 35 | mayPanic() 36 | 37 | // Đoạn code sau sẽ không được thực thi, vì `mayPanic` sẽ panic. 38 | // Việc thực thi của hàm `main` sẽ dừng lại tại điểm panic 39 | // và tiếp tục thực thi các đoạn mã trong closure defer. 40 | fmt.Println("After mayPanic()") 41 | } 42 | -------------------------------------------------------------------------------- /examples/recover/recover.hash: -------------------------------------------------------------------------------- 1 | 0b84dfd0a1784e61d33fcc8cc2833456a63c54ca 2 | qqrk4ipyIT1 3 | -------------------------------------------------------------------------------- /examples/recover/recover.sh: -------------------------------------------------------------------------------- 1 | $ go run recover.go 2 | Recovered. Error: 3 | a problem -------------------------------------------------------------------------------- /examples/recursion/recursion.go: -------------------------------------------------------------------------------- 1 | // Go hỗ trợ 2 | // các hàm đệ quy. 3 | // Dưới đây là một ví dụ kinh điển. 4 | package main 5 | 6 | import "fmt" 7 | 8 | // Hàm `fact` này tự gọi chính nó cho đến khi nó rơi vào 9 | // trường hợp cơ sở là `fact(0)`. 10 | func fact(n int) int { 11 | if n == 0 { 12 | return 1 13 | } 14 | return n * fact(n-1) 15 | } 16 | 17 | func main() { 18 | fmt.Println(fact(7)) 19 | 20 | // Closures cũng có thể đệ quy, nhưng điều này yêu cầu 21 | // closure được khai báo với một `var` có kiểu dữ liệu 22 | // cụ thể trước khi nó được định nghĩa. 23 | var fib func(n int) int 24 | 25 | fib = func(n int) int { 26 | if n < 2 { 27 | return n 28 | } 29 | 30 | // Since `fib` was previously declared in `main`, Go 31 | // knows which function to call with `fib` here. 32 | // Bởi vì `fib` đã được khai báo trước đó trong `main`, 33 | // Go biết sẽ gọi được hàm `fib` ở đây. 34 | return fib(n-1) + fib(n-2) 35 | } 36 | 37 | fmt.Println(fib(7)) 38 | } 39 | -------------------------------------------------------------------------------- /examples/recursion/recursion.hash: -------------------------------------------------------------------------------- 1 | d3ce10585912be2eed5b9a2342cffa78712b39d3 2 | IZAI82M7G9C 3 | -------------------------------------------------------------------------------- /examples/recursion/recursion.sh: -------------------------------------------------------------------------------- 1 | $ go run recursion.go 2 | 5040 3 | 13 -------------------------------------------------------------------------------- /examples/regular-expressions/regular-expressions.go: -------------------------------------------------------------------------------- 1 | // Go hỗ trợ sẵn các thư viện về [biểu thức chính quy (regular expressions)](https://en.wikipedia.org/wiki/Regular_expression). 2 | // Dưới đây là một số ví dụ về các tác vụ thường gặp trong Go 3 | // liên quan đến biểu thức chính quy. 4 | package main 5 | 6 | import ( 7 | "bytes" 8 | "fmt" 9 | "regexp" 10 | ) 11 | 12 | func main() { 13 | 14 | // Đoạn dưới đây kiểm tra xem một chuỗi có khớp với một mẫu (pattern) hay không. 15 | match, _ := regexp.MatchString("p([a-z]+)ch", "peach") 16 | fmt.Println(match) 17 | 18 | // Ở phía trên, ta đã sử dụng một chuỗi mẫu trực tiếp, nhưng 19 | // cho các tác vụ khác với biểu thức chính quy, ta cần phải 20 | // thực hiện `Compile` một struct `Regexp` đã được tối ưu. 21 | r, _ := regexp.Compile("p([a-z]+)ch") 22 | 23 | // Nhiều phương thức khác có sẵn trên các struct này. Dưới đây là 24 | // một ví dụ về việc kiểm tra khớp mẫu như ta đã thấy ở trên. 25 | fmt.Println(r.MatchString("peach")) 26 | 27 | // Dòng dưới đây sẽ tìm kiếm chuỗi khớp với pattern ở trên 28 | fmt.Println(r.FindString("peach punch")) 29 | 30 | // Dòng dưới đây cũng tìm kiếm chuỗi đầu tiên khớp với pattern ở trên 31 | // nhưng trả về chỉ số bắt đầu và kết thúc của chuỗi khớp thay vì 32 | // trả về một chuỗi khớp. 33 | fmt.Println("idx:", r.FindStringIndex("peach punch")) 34 | 35 | // Biến thể `Submatch` cũng trả về thông tin về cả các 36 | // kết quả phù hợp với toàn bộ biểu thức chính quy (whole-pattern matches, matches) và 37 | // các kết quả phù hợp với các mẫu phụ (submatches) trong các kết quả đó. Ví dụ, 38 | // đoạn mã này sẽ trả về thông tin cho cả `p([a-z]+)ch` và `([a-z]+)`. 39 | 40 | fmt.Println(r.FindStringSubmatch("peach punch")) 41 | 42 | // Tương tự như trên, đoạn mã này sẽ trả về thông tin 43 | // về chỉ số của các matches và các submatches. 44 | fmt.Println(r.FindStringSubmatchIndex("peach punch")) 45 | 46 | // Các biến thể `All` của các hàm trên sẽ áp dụng cho tất cả 47 | // các matches từ giá trị truyền vào, chứ không chỉ áp dụng riêng 48 | // cho kết quả match đầu tiên. Ví dụ, để tìm tất cả các matches 49 | // cho một biểu thức chính quy. 50 | fmt.Println(r.FindAllString("peach punch pinch", -1)) 51 | 52 | // Các biến thể `All` này cũng có sẵn cho các hàm khác 53 | // mà ta đã thấy ở trên. 54 | fmt.Println("all:", r.FindAllStringSubmatchIndex( 55 | "peach punch pinch", -1)) 56 | 57 | // Truyền một số nguyên không âm ở đối số thứ hai 58 | // vào các hàm này sẽ giới hạn số lượng 59 | // kết quả matches trả về. 60 | fmt.Println(r.FindAllString("peach punch pinch", 2)) 61 | 62 | // Các ví dụ ở trên của chúng ta sử dụng các chuỗi kí tự 63 | // dưới dạng đối số và sử dụng tên hàm có dạng như `MatchString`. 64 | // Ta cũng có thể sử dụng các đối số kiểu `[]byte` và bỏ đi 65 | // phần `String` trong tên hàm. 66 | fmt.Println(r.Match([]byte("peach"))) 67 | 68 | // When creating global variables with regular 69 | // expressions you can use the `MustCompile` variation 70 | // of `Compile`. `MustCompile` panics instead of 71 | // returning an error, which makes it safer to use for 72 | // global variables. 73 | // Khi khởi tạo các biến toàn cục với biểu thức chính quy, 74 | // ta có thể sử dụng biến thể `MustCompile` của `Compile`. 75 | // `MustCompile` sẽ panic thay vì trả về lỗi, điều này 76 | // khiến cho việc sử dụng nó với các biến toàn cục trở nên an toàn hơn. 77 | r = regexp.MustCompile("p([a-z]+)ch") 78 | fmt.Println("regexp:", r) 79 | 80 | // Package `regexp` cũng có thể được sử dụng để thay thế 81 | // các chuỗi con khớp với một pattern bằng các chuỗi khác. 82 | fmt.Println(r.ReplaceAllString("a peach", "")) 83 | 84 | // Biến thể `Func` cho phép bạn biến đổi chuỗi kí tự đã khớp 85 | // với một hàm đã có. 86 | in := []byte("a peach") 87 | out := r.ReplaceAllFunc(in, bytes.ToUpper) 88 | fmt.Println(string(out)) 89 | } 90 | -------------------------------------------------------------------------------- /examples/regular-expressions/regular-expressions.hash: -------------------------------------------------------------------------------- 1 | 1f10eb0b291ce4500a90531e20a1d9fec60e9682 2 | RnNXhFgXFia 3 | -------------------------------------------------------------------------------- /examples/regular-expressions/regular-expressions.sh: -------------------------------------------------------------------------------- 1 | $ go run regular-expressions.go 2 | true 3 | true 4 | peach 5 | idx: [0 5]\ 6 | [peach ea] 7 | [0 5 1 3] 8 | [peach punch pinch] 9 | all: [[0 5 1 3] [6 11 7 9] [12 17 13 15]] 10 | [peach punch] 11 | true 12 | regexp: p([a-z]+)ch 13 | a 14 | a PEACH 15 | 16 | # Để có một tham khảo đầy đủ về 17 | # các biểu thức chính quy của Go, hãy 18 | # đọc tài liệu của package [`regexp`](https://pkg.go.dev/regexp). -------------------------------------------------------------------------------- /examples/select/select.go: -------------------------------------------------------------------------------- 1 | // Go _select_ cho phép bạn đợi nhiều channel hoạt động. 2 | // Kết hợp giữa goroutine và channel với select là 3 | // một tính năng mạnh mẽ của Go. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "time" 10 | ) 11 | 12 | func main() { 13 | // Cho ví dụ chúng ta, chúng ta sẽ select 2 channel. 14 | c1 := make(chan string) 15 | c2 := make(chan string) 16 | 17 | // Mỗi channel sẽ nhận một giá trị sau một khoảng thời gian, 18 | // để giả lập, ví dụ chặn RPC hoạt động thực thi trong 19 | // những goroutine chạy đồng thời. 20 | go func() { 21 | time.Sleep(1 * time.Second) 22 | c1 <- "one" 23 | }() 24 | go func() { 25 | time.Sleep(2 * time.Second) 26 | c2 <- "two" 27 | }() 28 | 29 | // Chúng ta sẽ sử dụng `select` để đợi cả 2 giá trị này 30 | // một cách đồng thời, in chúng ra khi chúng nhận được. 31 | for i := 0; i < 2; i++ { 32 | select { 33 | case msg1 := <-c1: 34 | fmt.Println("received", msg1) 35 | case msg2 := <-c2: 36 | fmt.Println("received", msg2) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/select/select.hash: -------------------------------------------------------------------------------- 1 | 669c9a7174ebaff78ddcd7b9688595bc4cd540fe 2 | if-ZMLJsMuv 3 | -------------------------------------------------------------------------------- /examples/select/select.sh: -------------------------------------------------------------------------------- 1 | # Chúng ta nhận giá trị `"one"` và sau đó `"two"` như 2 | # mong đợi. 3 | $ time go run select.go 4 | received one 5 | received two 6 | 7 | # Luư ý rằng tổng thời gian thực thi chỉ xấp xỉ 2 giây 8 | # kể từ khi cả giây 1 và giây 2 `Sleeps` thực thi đồng 9 | # thời. 10 | real 0m2.245s 11 | -------------------------------------------------------------------------------- /examples/sha256-hashes/sha256-hashes.go: -------------------------------------------------------------------------------- 1 | // [_Giá trị băm SHA256 hashes_](https://en.wikipedia.org/wiki/SHA-2) 2 | // thường được sử dụng để tính các định danh ngắn cho các nhị phân 3 | // hoặc văn bản. Ví dụ, chứng chỉ TLS/SSL sử dụng SHA256 4 | // để tính chữ ký của chứng chỉ. Sau đây là cách tính giá trị băm 5 | // SHA256 trong Go. 6 | 7 | package main 8 | 9 | //Go hiên thực một số hàm băm trong nhiều packages (gói) 10 | // `crypto/*` khác nhau. 11 | import ( 12 | "crypto/sha256" 13 | "fmt" 14 | ) 15 | 16 | func main() { 17 | s := "sha256 this string" 18 | 19 | // Ở đây, chúng ta bắt đầu với một đối tượng băm mới. 20 | h := sha256.New() 21 | 22 | // `Write` yêu cầu bytes. Nếu bạn có một chuỗi `s`, 23 | // sử dụng `[]byte(s)` để ép nó thành byte. 24 | h.Write([]byte(s)) 25 | 26 | // Đây là kết quả băm đã hoàn thành dưới dạng byte slice. 27 | // Tham số của `Sum` có thể được sử dụng để thêm 28 | // vào một byte slice hiện có, thường không cần thiết. 29 | bs := h.Sum(nil) 30 | 31 | fmt.Println(s) 32 | fmt.Printf("%x\n", bs) 33 | } 34 | -------------------------------------------------------------------------------- /examples/sha256-hashes/sha256-hashes.hash: -------------------------------------------------------------------------------- 1 | fe06e24e001c2dcbbdf9ca14adde8cf55b3f600a 2 | DUjxdXk3nCp 3 | -------------------------------------------------------------------------------- /examples/sha256-hashes/sha256-hashes.sh: -------------------------------------------------------------------------------- 1 | # Chạy chương trình tính toán giá trị băm và in nó 2 | # dưới dạng hex có thể đọc được bởi con người. 3 | $ go run sha256-hashes.go 4 | sha256 this string 5 | 1af1dfa857bf1d8814fe1af8983c18080019922e557f15a8a... 6 | 7 | 8 | # Bạn có thể tính toán các băm khác sử dụng một 9 | # mẫu tương tự như mẫu được hiển thị ở trên. Ví dụ, 10 | # để tính toán các giá trị băm SHA512, import 11 | # `crypto/sha512` và sử dụng `sha512.New()`. 12 | 13 | # Lưu ý rằng nếu bạn cần các giá trị băm an toàn 14 | # bằng mật mã, bạn nên nghiên cứu kỹ 15 | # [sức mạnh băm](https://en.wikipedia.org/wiki/Cryptographic_hash_function)! 16 | -------------------------------------------------------------------------------- /examples/signals/signals.go: -------------------------------------------------------------------------------- 1 | // Đôi khi chúng ta muốn các chương trình Go của mình có thể 2 | // xử lý các [Unix signals](https://en.wikipedia.org/wiki/Unix_signal) 3 | // một cách thông minh. Ví dụ, chúng ta có thể muốn một server 4 | // có thể tự tắt khi nó nhận được một tín hiệu `SIGTERM`, hoặc một 5 | // tool command-line có thể dừng xử lý input nếu nó nhận được một 6 | // tín hiệu `SIGINT`. Đây là cách xử lý signal trong Go sử dụng channels. 7 | 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "os" 13 | "os/signal" 14 | "syscall" 15 | ) 16 | 17 | func main() { 18 | 19 | // Go sẽ thông báo các tín hiệu bằng cách gửi giá trị 20 | // `os.Signal` vào một channel. Chúng ta sẽ tạo một channel 21 | // để nhận các thông báo này. Lưu ý rằng channel này nên 22 | // được buffer. 23 | sigs := make(chan os.Signal, 1) 24 | 25 | // `signal.Notify` đăng ký channel đã cho để nhận các 26 | // thông báo của các tín hiệu đã được chỉ định. 27 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 28 | 29 | // Ta có thể nhận các thông báo từ `sigs` ở bên trong 30 | // hàm `main`, nhưng hãy xem cách khiến cho điều này 31 | // cũng có thể trong một goroutine riêng biệt, để minh 32 | // hoạ cho một trường hợp thực tế hơn về shutdown. 33 | done := make(chan bool, 1) 34 | 35 | go func() { 36 | // Goroutine này thực hiện một blocking receive (chặn việc 37 | // nhận thêm dữ liệu đến channel) trong khi đợi các tín hiệu. 38 | // Khi nó nhận được một tín hiệu, nó sẽ in ra và sau đó 39 | // thông báo cho chương trình rằng nó có thể kết thúc. 40 | sig := <-sigs 41 | fmt.Println() 42 | fmt.Println(sig) 43 | done <- true 44 | }() 45 | 46 | // Chương trình sẽ đợi ở đây cho đến khi nó nhận được 47 | // tín hiệu mong muốn (được chỉ định bởi goroutine ở trên 48 | // khi nó gửi một giá trị vào `done`) và sau đó thoát chương trình. 49 | fmt.Println("awaiting signal") 50 | <-done 51 | fmt.Println("exiting") 52 | } 53 | -------------------------------------------------------------------------------- /examples/signals/signals.hash: -------------------------------------------------------------------------------- 1 | 063657f9026580d9bcf2174199fbb267a7e98dd8 2 | Dg0rBr3NGc7 3 | -------------------------------------------------------------------------------- /examples/signals/signals.sh: -------------------------------------------------------------------------------- 1 | # Khi ta chạy chương trình này, nó sẽ chờ 2 | # một tín hiệu. Bằng cách gõ `ctrl-C` 3 | # (terminal sẽ hiển thị là `^C`) 4 | # ta có thể gửi một tín hiệu `SIGINT`, 5 | # khiến cho chương trình in ra `interrupt` 6 | # và sau đó thoát. 7 | $ go run signals.go 8 | awaiting signal 9 | ^C 10 | interrupt 11 | exiting 12 | -------------------------------------------------------------------------------- /examples/slices/slices.go: -------------------------------------------------------------------------------- 1 | // _Slices_ là một kiểu dữ liệu quan trọng trong Go, cho 2 | // một giao diện mạnh mẽ hơn cho các chuỗi so với các array. 3 | 4 | package main 5 | 6 | import "fmt" 7 | 8 | func main() { 9 | 10 | // Không giống với array, slices được nhập theo các phần tử 11 | // mà chúng chứa (không phải số lượng phần tử). 12 | // Để tạo một slice trống với độ dài khác 0, sử dụng hàm tích hợp 13 | // `make`. Ở đây chúng ta tạo một slice gồm 14 | // `string` của độ dài `3` (khởi tạo với giá trị 0) 15 | s := make([]string, 3) 16 | fmt.Println("emp:", s) 17 | 18 | // Chúng ta có thể set và get tương tự với arrays. 19 | s[0] = "a" 20 | s[1] = "b" 21 | s[2] = "c" 22 | fmt.Println("set:", s) 23 | fmt.Println("get:", s[2]) 24 | 25 | // `len` trả về độ dài của slice. 26 | fmt.Println("len:", len(s)) 27 | 28 | // Ngoài các thao tác cơ bản, slices hỗ trợ thêm 29 | // vài cái giúp cho phong phú hơn array. Một là hàm 30 | // tích hợp `append`, giúp trả về slice chứa một hoặc nhiều giá trị hơn. 31 | // Lưu ý rằng chúng ta cần đồng ý giá trị trả về từ `append` bởi vì 32 | // chúng ta có thể nhận một giá trị slice mới. 33 | s = append(s, "d") 34 | s = append(s, "e", "f") 35 | fmt.Println("apd:", s) 36 | 37 | // Chúng ta cũng có thể sử dụng `copy` với slices. Ở đây 38 | // chúng ta tạo một slice `c` với độ dài cùng với `s` và copy 39 | // từ `s` vào `c`. 40 | c := make([]string, len(s)) 41 | copy(c, s) 42 | fmt.Println("cpy:", c) 43 | 44 | // Slices hỗ trợ toán tử "slice" với cú pháp 45 | // `slice[low:high]`. Ví dụ, cú pháp này giúp lấy 46 | // những phần tử `s[2]`, `s[3]`, `s[4]`. 47 | l := s[2:5] 48 | fmt.Println("sl1:", l) 49 | 50 | // Cú pháp này lấy tới trước `s[5]` (không tính s[5]). 51 | l = s[:5] 52 | fmt.Println("sl2:", l) 53 | 54 | // Cú pháp này lấy từ `s[2]` (tính cả s[2]). 55 | l = s[2:] 56 | fmt.Println("sl3:", l) 57 | 58 | // Chúng ta có thể khai báo và khởi tạo một biến cho slice 59 | // bằng cú pháp này trong một dòng. 60 | t := []string{"g", "h", "i"} 61 | fmt.Println("dcl:", t) 62 | 63 | // Slice có thể được kết hợp thành cấu trúc dữ liệu đa chiều. 64 | // Độ dài của slice bên trong đa dạng, không tương tự 65 | // với array đa chiều. 66 | twoD := make([][]int, 3) 67 | for i := 0; i < 3; i++ { 68 | innerLen := i + 1 69 | twoD[i] = make([]int, innerLen) 70 | for j := 0; j < innerLen; j++ { 71 | twoD[i][j] = i + j 72 | } 73 | } 74 | fmt.Println("2d: ", twoD) 75 | } 76 | -------------------------------------------------------------------------------- /examples/slices/slices.hash: -------------------------------------------------------------------------------- 1 | 20890f2d7b077997a7d38ac88e6a851a48967eca 2 | THGpZl-X39y 3 | -------------------------------------------------------------------------------- /examples/slices/slices.sh: -------------------------------------------------------------------------------- 1 | # Lưu ý rằng dù slice khác với array, nhưng được 2 | # thể hiện cùng dạng với array khi sử dụng `fmt.Println`. 3 | $ go run slices.go 4 | emp: [ ] 5 | set: [a b c] 6 | get: c 7 | len: 3 8 | apd: [a b c d e f] 9 | cpy: [a b c d e f] 10 | sl1: [c d e] 11 | sl2: [a b c d e] 12 | sl3: [c d e f] 13 | dcl: [g h i] 14 | 2d: [[0] [1 2] [2 3 4]] 15 | 16 | # Hãy xem qua blog này của team Go (https://go.dev/blog/slices-intro) 17 | # để biết thêm nhiều chi tiết về thiết kế và 18 | # thực hiện mảng trong Go. 19 | 20 | # Bây giờ chúng ta đã xem qua arrays và slices, 21 | # chúng ta sẽ xem qua cấu trúc dữ liệu 22 | # quan trọng khác của Go: maps. -------------------------------------------------------------------------------- /examples/sorting-by-functions/sorting-by-functions.go: -------------------------------------------------------------------------------- 1 | // Thi thoảng chúng ta sẽ muốn sắp xếp một collection 2 | // theo một thứ gì đó ngoài thứ tự tự nhiên. Ví dụ, giả 3 | // sử chúng ta muốn sắp xếp các chuỗi theo độ dài thay 4 | // vì theo thứ tự bảng chữ cái. Đây là một ví dụ về 5 | // các loại sắp xếp tùy chỉnh trong Go. 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "sort" 12 | ) 13 | 14 | // Để sắp xếp theo hàm tùy chỉnh trong Go, chúng ta 15 | // cần type tương ứng. Ở đây, chúng ta đã tạo một kiểu 16 | // `byLength` chỉ là một alias cho kiểu builtin 17 | // `[string]`. 18 | type byLength []string 19 | 20 | // Chúng ta triển khai `sort.Interface` - `Len`, `Less` 21 | // và `Swap` - Trên các loại chúng ta có thể sử dụng 22 | // gói `sort` hàm `Sort` chung. `Len` và `Swap` sẽ 23 | // thường sẽ giống nhau giữa các type và `Less` sẽ 24 | // giữ logic sắp xếp tùy chỉnh thực tế. Trong trường hợp 25 | // chúng ta muốn sắp xếp theo thứ tự độ dài của chuỗi, 26 | // vậy chúng ta dùng `len(s[i])` và `len(s[j])` ở đây. 27 | func (s byLength) Len() int { 28 | return len(s) 29 | } 30 | func (s byLength) Swap(i, j int) { 31 | s[i], s[j] = s[j], s[i] 32 | } 33 | func (s byLength) Less(i, j int) bool { 34 | return len(s[i]) < len(s[j]) 35 | } 36 | 37 | // Với tất cả những điều trong này, giờ chúng ta có thể 38 | // thực hiện sắp xếp tùy chỉnh bằng cách chuyển đổi 39 | // slice `fruits` to `byLength`, và sau đó sử dụng 40 | // `sort.Sort` trên kiểu slice. 41 | func main() { 42 | fruits := []string{"peach", "banana", "kiwi"} 43 | sort.Sort(byLength(fruits)) 44 | fmt.Println(fruits) 45 | } 46 | -------------------------------------------------------------------------------- /examples/sorting-by-functions/sorting-by-functions.hash: -------------------------------------------------------------------------------- 1 | e29047e36c613108713695415d051cda5174a97f 2 | aVMRjl0YUua 3 | -------------------------------------------------------------------------------- /examples/sorting-by-functions/sorting-by-functions.sh: -------------------------------------------------------------------------------- 1 | # Chạy chương trình bày ra một danh sách đã được sắp xếp 2 | # theo độ dài chuỗi, như mong muốn. 3 | $ go run sorting-by-functions.go 4 | [kiwi peach banana] 5 | 6 | # Bằng cách làm theo cùng một mô hình tạo tùy chỉnh này 7 | # gõ, triển khai ba phương thức `Interface` trên đó 8 | # gõ và sau đó gọi sort.Sort trên một collection của loại 9 | # tùy chỉnh đó, chúng ta có thể sắp xếp Go slice tùy ý 10 | # chức năng. 11 | -------------------------------------------------------------------------------- /examples/sorting/sorting.go: -------------------------------------------------------------------------------- 1 | // Package Go `sort` thực hiện sắp xếp cho builin và 2 | // kiểu dữ liệu được định nghĩa sẵn. Chúng ta sẽ nhìn 3 | // vào kết quả sử dụng sắp xếp builtin trước. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "sort" 10 | ) 11 | 12 | func main() { 13 | // Phương thức sort được đặc tả với kiểu dữ liệu 14 | // builtin; đây là một ví dụ cho strings. Nhớ rằng 15 | // việc sắp xếp được thực hiện tại chỗ, do đó, nó 16 | // thay đổi slice đã cho và không trả về giá trị mới. 17 | strs := []string{"c", "a", "b"} 18 | sort.Strings(strs) 19 | fmt.Println("Strings:", strs) 20 | 21 | // Một ví dụ của việc sắp xếp kiểu `int`. 22 | ints := []int{7, 2, 4} 23 | sort.Ints(ints) 24 | fmt.Println("Ints: ", ints) 25 | 26 | // Chúng ta cũng có thể sử dụng `sort` để check rằng 27 | // một slice đã được sắp xếp theo thứ tự hay chưa. 28 | s := sort.IntsAreSorted(ints) 29 | fmt.Println("Sorted: ", s) 30 | } 31 | -------------------------------------------------------------------------------- /examples/sorting/sorting.hash: -------------------------------------------------------------------------------- 1 | 6576072668cee262c5b059ce4f926cb0e47c266d 2 | 0UAsRSxcbWn 3 | -------------------------------------------------------------------------------- /examples/sorting/sorting.sh: -------------------------------------------------------------------------------- 1 | # Chạy chương trình in ra chuỗi và số nguyên slices đã 2 | # được sắp xếp và `true`là kết quả test của `AreSorted`. 3 | $ go run sorting.go 4 | Strings: [a b c] 5 | Ints: [2 4 7] 6 | Sorted: true 7 | -------------------------------------------------------------------------------- /examples/spawning-processes/spawning-processes.go: -------------------------------------------------------------------------------- 1 | // Đôi lúc, chúng ta cần tạo ra các tiến trình 2 | // không phải là code Go. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "io" 8 | "os/exec" 9 | ) 10 | 11 | func main() { 12 | 13 | // Chúng ta sẽ bắt đầu với một lệnh đơn giản không 14 | // có tham số và chỉ in ra một thông báo đơn giản. 15 | // Hàm `exec.Command` sẽ tạo ra một đối tượng 16 | // để thực thi tiến trình bên ngoài giúp chúng ta. 17 | dateCmd := exec.Command("date") 18 | 19 | // `Output` sẽ chạy lệnh, đợi cho đến khi nó hoàn 20 | // thành và thu thập kết quả của nó. Nếu không có lỗi, 21 | // `dateOut` sẽ chứa các thông tin ngày giờ. 22 | dateOut, err := dateCmd.Output() 23 | if err != nil { 24 | panic(err) 25 | } 26 | fmt.Println("> date") 27 | fmt.Println(string(dateOut)) 28 | 29 | // `Output` và các phương thức khác của `Command` sẽ 30 | // trả về `*exec.Error` nếu có lỗi khi thực thi lệnh 31 | // (vd: đường dẫn sai), và `*exec.ExitError` nếu lệnh 32 | // chạy nhưng trả về một mã lỗi khác 0. 33 | _, err = exec.Command("date", "-x").Output() 34 | if err != nil { 35 | switch e := err.(type) { 36 | case *exec.Error: 37 | fmt.Println("failed executing:", err) 38 | case *exec.ExitError: 39 | fmt.Println("command exit rc =", e.ExitCode()) 40 | default: 41 | panic(err) 42 | } 43 | } 44 | 45 | // Tiếp theo chúng ta sẽ xem một ví dụ phức tạp hơn 46 | // thực thi lệnh `grep` để tìm các dòng chứa từ 47 | // "hello" 48 | grepCmd := exec.Command("grep", "hello") 49 | 50 | // Ví dụ dưới đây chúng ta sẽ dùng StdinPipe và 51 | // StdoutPipe để truyền dữ liệu vào và lấy dữ liệu 52 | // Truyền dữ liệu vào grepIn để ghi vào tiến trình 53 | // grep, và lấy dữ liệu từ grepOut để đọc kết quả 54 | grepIn, _ := grepCmd.StdinPipe() 55 | grepOut, _ := grepCmd.StdoutPipe() 56 | grepCmd.Start() 57 | grepIn.Write([]byte("hello grep\ngoodbye grep")) 58 | grepIn.Close() 59 | grepBytes, _ := io.ReadAll(grepOut) 60 | grepCmd.Wait() 61 | 62 | // Chúng ta bỏ qua kiểm tra lỗi trong ví dụ dưới đây, 63 | // nhưng bạn có thể sử dụng cú pháp `if err != nil` 64 | // cho tất cả chúng. Chúng ta cũng chỉ thu thập kết quả 65 | // `StdoutPipe`, nhưng bạn có thể thu thập `StderrPipe` 66 | // theo cùng một cách. 67 | fmt.Println("> grep hello") 68 | fmt.Println(string(grepBytes)) 69 | 70 | // Chú ý khi thực thi lệnh chúng ta cần cung cấp 71 | // một mảng các tham số, thay vì chỉ cần truyền vào 72 | // một chuỗi lệnh. Nếu bạn muốn thực thi một đầy đủ 73 | // lệnh, bạn có thể sử dụng `-c` của `bash` 74 | // để thực thi. 75 | lsCmd := exec.Command("bash", "-c", "ls -a -l -h") 76 | lsOut, err := lsCmd.Output() 77 | if err != nil { 78 | panic(err) 79 | } 80 | fmt.Println("> ls -a -l -h") 81 | fmt.Println(string(lsOut)) 82 | } 83 | -------------------------------------------------------------------------------- /examples/spawning-processes/spawning-processes.hash: -------------------------------------------------------------------------------- 1 | 114672ce17bd14a5e90db8c8ce67538c76885c50 2 | GDRhySksMYs 3 | -------------------------------------------------------------------------------- /examples/spawning-processes/spawning-processes.sh: -------------------------------------------------------------------------------- 1 | # The spawned programs return output that is the same 2 | # as if we had run them directly from the command-line. 3 | $ go run spawning-processes.go 4 | > date 5 | Thu 05 May 2022 10:10:12 PM PDT 6 | 7 | # date doesn't have a `-x` flag so it will exit with 8 | # an error message and non-zero return code. 9 | command exited with rc = 1 10 | > grep hello 11 | hello grep 12 | 13 | > ls -a -l -h 14 | drwxr-xr-x 4 mark 136B Oct 3 16:29 . 15 | drwxr-xr-x 91 mark 3.0K Oct 3 12:50 .. 16 | -rw-r--r-- 1 mark 1.3K Oct 3 16:28 spawning-processes.go 17 | -------------------------------------------------------------------------------- /examples/stateful-goroutines/stateful-goroutines.go: -------------------------------------------------------------------------------- 1 | // Trong ví dụ trước chúng ta sử dụng explicit locking 2 | // với [mutexes](mutexes) để đồng bộ hóa truy cập chia 3 | // sẻ trạng thái giữa nhiều goroutine. Một lựa chọn khác 4 | // là sử dụng tính năng đồng bộ hóa built-in của 5 | // goroutine và channel để đạt được kết quả tương tự. 6 | // Hướng tiếp cận dựa trên channel này phù hợp với ý 7 | // tưởng chia sẻ bộ nhớ của Go bằng cách giao tiếp và sở 8 | // hữu từng phần dữ liệu bởi chính xác một goroutine. 9 | 10 | package main 11 | 12 | import ( 13 | "fmt" 14 | "math/rand" 15 | "sync/atomic" 16 | "time" 17 | ) 18 | 19 | // Trong ví dụ này trạng thái sẽ được sử hữu bởi một 20 | // goroutine duy nhất. Điều này sẽ đảm bảo rằng dữ liệu 21 | // sẻ không bao giờ bị hỏng hoặc truy cập đồng thời. Để 22 | // đọc hoặc khi trạng thái đó, những goroutine khác sẽ 23 | // gửi thông tin đến goroutine đang sử hữu và nhận trả 24 | // lời tương ứng. Những `readop` và `writeOp` `struct` 25 | // này gói gọn những yêu cầu đó và một cách để goroutine 26 | // sở hữu để đáp ứng. 27 | type readOp struct { 28 | key int 29 | resp chan int 30 | } 31 | type writeOp struct { 32 | key int 33 | val int 34 | resp chan bool 35 | } 36 | 37 | func main() { 38 | 39 | // Như trước đó, chúng ta sẽ đếm số lượng toán tử 40 | // được thực thi. 41 | var readOps uint64 42 | var writeOps uint64 43 | 44 | // Các channel `reads` và `writes` sẽ được sử dụng 45 | // bởi các goroutine khác để đưa ra các yêu cầu đọc 46 | // và ghi một cách tương ứng. 47 | reads := make(chan readOp) 48 | writes := make(chan writeOp) 49 | 50 | // Đây là goroutine mà chiếm quyền `state`, là một 51 | // map như trong ví dụ trước nhưng bây giờ là riêng 52 | // tư với stateful goroutine. Goroutine này lặp đi lặp 53 | // lại chọn trên channel `reads` and `writes`, đáp ứng 54 | // yêu cầu khi chúng đến. Một response được thực 55 | // hiện bằng cách thực hiện đầu tiên yêu càu hoạt 56 | // động và sau đó gửi giá trị trên channel response 57 | // `resp` để biểu thị thành công (và giá trị mong muốn 58 | // trong trường hợp `reads`). 59 | go func() { 60 | var state = make(map[int]int) 61 | for { 62 | select { 63 | case read := <-reads: 64 | read.resp <- state[read.key] 65 | case write := <-writes: 66 | state[write.key] = write.val 67 | write.resp <- true 68 | } 69 | } 70 | }() 71 | 72 | // Chạy 100 goroutine để thực hiện tháo tác reads đến 73 | // goroutine sở hữu trạng thái qua channel `reads`. 74 | // MỖi lần đọc yêu cầu xây dựng một `readOp`, gửi 75 | // nó qua channel `reads`, and sau đó nhận về kết quả 76 | // trên channel `resp` được cung cấp. 77 | for r := 0; r < 100; r++ { 78 | go func() { 79 | for { 80 | read := readOp{ 81 | key: rand.Intn(5), 82 | resp: make(chan int)} 83 | reads <- read 84 | <-read.resp 85 | atomic.AddUint64(&readOps, 1) 86 | time.Sleep(time.Millisecond) 87 | } 88 | }() 89 | } 90 | 91 | // Chúng ta cho chạy 10 toán tử writes, sử dụng 92 | // cách tiếp cận tương tự. 93 | for w := 0; w < 10; w++ { 94 | go func() { 95 | for { 96 | write := writeOp{ 97 | key: rand.Intn(5), 98 | val: rand.Intn(100), 99 | resp: make(chan bool)} 100 | writes <- write 101 | <-write.resp 102 | atomic.AddUint64(&writeOps, 1) 103 | time.Sleep(time.Millisecond) 104 | } 105 | }() 106 | } 107 | 108 | // Để goroutine hoạt động trong một giây. 109 | time.Sleep(time.Second) 110 | 111 | // Cuối cùng, capture và báo cáo số lượng toán tử. 112 | readOpsFinal := atomic.LoadUint64(&readOps) 113 | fmt.Println("readOps:", readOpsFinal) 114 | writeOpsFinal := atomic.LoadUint64(&writeOps) 115 | fmt.Println("writeOps:", writeOpsFinal) 116 | } 117 | -------------------------------------------------------------------------------- /examples/stateful-goroutines/stateful-goroutines.hash: -------------------------------------------------------------------------------- 1 | 0fe4598dee0db83589374f801c33d589af505f78 2 | bnnknCegpb- 3 | -------------------------------------------------------------------------------- /examples/stateful-goroutines/stateful-goroutines.sh: -------------------------------------------------------------------------------- 1 | # Running our program shows that the goroutine-based 2 | # state management example completes about 80,000 3 | # total operations. 4 | # Khi chạy chương trình chỉ ra rằng ví dụ của sự quản 5 | # lý trạng thái dựa trên goroutine hoàn thành tổng cộng 6 | # khoảng 80,000 phép tính. 7 | $ go run stateful-goroutines.go 8 | readOps: 71708 9 | writeOps: 7177 10 | 11 | # Đối với trường hợp cụ thể này, hướng tiếp cận dựa trên 12 | # goroutine liên quan nhiều hơn đến cái dựa trên mutex. 13 | # Nó có thể hữu ích trong một số trường hợp cụ thể, ví dụ 14 | # khi bạn có nhiều channel tham gia hoặc khi quản lý 15 | # nhiều mutex như vậy sẽ gây lỗi. Bạn nên sử dụng bất kì 16 | # phương pháp nào cảm thấy tự nhiên nhất, đặc biệt là 17 | # đối với sự hiểu biết đúng đắn của chương trình. -------------------------------------------------------------------------------- /examples/string-formatting/string-formatting.go: -------------------------------------------------------------------------------- 1 | // Go hỗ trợ rất tốt cho việc định dạng chuỗi trong 2 | // hàm `printf`. Dưới đây là một số ví dụ về các 3 | // tác vụ định dạng chuỗi phổ biến. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | ) 11 | 12 | type point struct { 13 | x, y int 14 | } 15 | 16 | func main() { 17 | 18 | // Go cung cấp một số "động từ" được thiết kế để 19 | // định dạng các giá trị chung trong Go. Ví dụ, đoạn dưới đây 20 | // in ra một đối tượng của struct `point` được định nghĩa ở trên. 21 | p := point{1, 2} 22 | fmt.Printf("struct1: %v\n", p) 23 | 24 | // Nếu giá trị là một struct, biến thể `%+v` sẽ 25 | // in ra thêm tên các trường của struct. 26 | fmt.Printf("struct2: %+v\n", p) 27 | 28 | // Biến thể `%#v` sẽ in ra một cú pháp Go đại diện 29 | // cho giá trị, ví dụ, một đoạn mã nguồn để 30 | // tạo ra giá trị đó. 31 | fmt.Printf("struct3: %#v\n", p) 32 | 33 | // Để in ra kiểu của một giá trị, sử dụng `%T`. 34 | fmt.Printf("type: %T\n", p) 35 | 36 | // Định dạng giá trị boolean thì làm như sau. 37 | fmt.Printf("bool: %t\n", true) 38 | 39 | // Có rất nhiều tùy chọn để định dạng số nguyên. 40 | // Sử dụng `%d` để định dạng chuẩn, dùng hệ cơ số 10. 41 | fmt.Printf("int: %d\n", 123) 42 | 43 | // Dưới đây sẽ in ra dạng nhị phân của số nguyên. 44 | fmt.Printf("bin: %b\n", 14) 45 | 46 | // Dưới đây sẽ in ra kí tự tương ứng với 47 | // giá trị số nguyên được truyền vào. 48 | fmt.Printf("char: %c\n", 33) 49 | 50 | // `%x` hỗ trợ mã hóa hex. 51 | fmt.Printf("hex: %x\n", 456) 52 | 53 | // Có rất nhiều tùy chọn để định dạng số thực. 54 | // Để định dạng số thực theo cách cơ bản, sử dụng `%f`. 55 | fmt.Printf("float1: %f\n", 78.9) 56 | 57 | // `%e` và `%E` định dạng số thực theo (một số 58 | // phiên bản khác nhau của) ký hiệu khoa học. 59 | fmt.Printf("float2: %e\n", 123400000.0) 60 | fmt.Printf("float3: %E\n", 123400000.0) 61 | 62 | // Để in ra một chuỗi một cách cơ bản, sử dụng `%s`. 63 | fmt.Printf("str1: %s\n", "\"string\"") 64 | 65 | // Để đặt chuỗi trong dấu ngoặc kép, sử dụng `%q`. 66 | fmt.Printf("str2: %q\n", "\"string\"") 67 | 68 | // Đối với các giá trị nguyên ở phía trên, `%x` sẽ 69 | // in ra chuỗi ở cơ số thập lục phân, với hai kí tự 70 | // đầu ra cho mỗi byte của giá trị đầu vào. 71 | fmt.Printf("str3: %x\n", "hex this") 72 | 73 | // Để in ra một giá trị con trỏ đại diện cho biến đó, sử dụng `%p`. 74 | fmt.Printf("pointer: %p\n", &p) 75 | 76 | // Khi định dạng số, bạn sẽ thường muốn kiểm soát 77 | // độ rộng và số các chữ số thập phân của kết quả. 78 | // Để chỉ định độ rộng của một số nguyên, sử dụng một 79 | // số sau dấu `%` trong biểu thức. Mặc định, kết quả 80 | // sẽ được căn phải và đệm bằng khoảng trắng. 81 | fmt.Printf("width1: |%6d|%6d|\n", 12, 345) 82 | 83 | // Bạn cũng có thể chỉ định độ rộng của số thực được in ra, 84 | // tuy nhiên thường bạn sẽ muốn giới hạn số chữ số thập phân 85 | // cùng lúc với độ rộng với chung 1 cú pháp duy nhất 86 | fmt.Printf("width2: |%6.2f|%6.2f|\n", 1.2, 3.45) 87 | 88 | // Để căn trái, sử dụng dấu `-`. 89 | fmt.Printf("width3: |%-6.2f|%-6.2f|\n", 1.2, 3.45) 90 | 91 | // Bạn cũng có thể muốn kiểm soát độ rộng khi định dạng 92 | // chuỗi, đặc biệt là để đảm bảo rằng chúng căn chỉnh 93 | // dưới dạng bảng khi in ra. Để căn phải với một độ rộng 94 | // một cách cơ bản ta làm như sau. 95 | fmt.Printf("width4: |%6s|%6s|\n", "foo", "b") 96 | 97 | // Để căn trái, sử dụng dấu `-` tương tự như với số. 98 | fmt.Printf("width5: |%-6s|%-6s|\n", "foo", "b") 99 | 100 | // Đến giờ chúng ta đã được xem về `Printf`, hàm in 101 | // chuỗi đã được định dạng ra `os.Stdout`. `Sprintf` định 102 | // dạng và trả về một chuỗi mà không in ra bất cứ đâu. 103 | s := fmt.Sprintf("sprintf: a %s", "string") 104 | fmt.Println(s) 105 | 106 | // Bạn có thể định dạng và in ra `io.Writers` khác ngoài 107 | // `os.Stdout` bằng cách sử dụng `Fprintf`. 108 | fmt.Fprintf(os.Stderr, "io: an %s\n", "error") 109 | } 110 | -------------------------------------------------------------------------------- /examples/string-formatting/string-formatting.hash: -------------------------------------------------------------------------------- 1 | 4a9ad2b39841c5e486f23aca5be15b90c29575bb 2 | QLnzR7-xhDF 3 | -------------------------------------------------------------------------------- /examples/string-formatting/string-formatting.sh: -------------------------------------------------------------------------------- 1 | $ go run string-formatting.go 2 | struct1: {1 2} 3 | struct2: {x:1 y:2} 4 | struct3: main.point{x:1, y:2} 5 | type: main.point 6 | bool: true 7 | int: 123 8 | bin: 1110 9 | char: ! 10 | hex: 1c8 11 | float1: 78.900000 12 | float2: 1.234000e+08 13 | float3: 1.234000E+08 14 | str1: "string" 15 | str2: "\"string\"" 16 | str3: 6865782074686973 17 | pointer: 0xc0000ba000 18 | width1: | 12| 345| 19 | width2: | 1.20| 3.45| 20 | width3: |1.20 |3.45 | 21 | width4: | foo| b| 22 | width5: |foo |b | 23 | sprintf: a string 24 | io: an error -------------------------------------------------------------------------------- /examples/string-functions/string-functions.go: -------------------------------------------------------------------------------- 1 | // Thư viện tiêu chuẩn `strings` cung cấp nhiều hàm hữu ích 2 | // liên quan đến xử lý chuỗi. Dưới đây là một số ví dụ 3 | // để bạn có cái nhìn tổng quan về thư viện này. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | s "strings" 10 | ) 11 | 12 | // Chúng ta sẽ dùng một cái tên khác ngắn gọn hơn để alias 13 | // cho hàm `fmt.Println` vì chúng ta sẽ sử dụng nó nhiều lần dưới đây. 14 | var p = fmt.Println 15 | 16 | func main() { 17 | 18 | // Dưới đây là một ví dụ về các hàm có sẵn trong thư viện 19 | // `strings`. Vì đây là các hàm của thư viện, chứ không phải 20 | // là các phương thức của đối tượng string, nên chúng ta cần 21 | // truyền vào chuỗi cần truy vấn vào vị trí đối số đầu tiên của hàm. 22 | // Bạn có thể tìm thêm các hàm trong thư viện 23 | // [`strings`](https://pkg.go.dev/strings) từ tài liệu của nó. 24 | p("Contains: ", s.Contains("test", "es")) 25 | p("Count: ", s.Count("test", "t")) 26 | p("HasPrefix: ", s.HasPrefix("test", "te")) 27 | p("HasSuffix: ", s.HasSuffix("test", "st")) 28 | p("Index: ", s.Index("test", "e")) 29 | p("Join: ", s.Join([]string{"a", "b"}, "-")) 30 | p("Repeat: ", s.Repeat("a", 5)) 31 | p("Replace: ", s.Replace("foo", "o", "0", -1)) 32 | p("Replace: ", s.Replace("foo", "o", "0", 1)) 33 | p("Split: ", s.Split("a-b-c-d-e", "-")) 34 | p("ToLower: ", s.ToLower("TEST")) 35 | p("ToUpper: ", s.ToUpper("test")) 36 | } 37 | -------------------------------------------------------------------------------- /examples/string-functions/string-functions.hash: -------------------------------------------------------------------------------- 1 | 9d4dc8efffab64deac61dca3093582f6286349e9 2 | ZIF7aTZWjOO 3 | -------------------------------------------------------------------------------- /examples/string-functions/string-functions.sh: -------------------------------------------------------------------------------- 1 | $ go run string-functions.go 2 | Contains: true 3 | Count: 2 4 | HasPrefix: true 5 | HasSuffix: true 6 | Index: 1 7 | Join: a-b 8 | Repeat: aaaaa 9 | Replace: f00 10 | Replace: f0o 11 | Split: [a b c d e] 12 | ToLower: test 13 | ToUpper: TEST -------------------------------------------------------------------------------- /examples/strings-and-runes/strings-and-runes.go: -------------------------------------------------------------------------------- 1 | // Một chuỗi trong Go là một read-only slice của các byte. Ngôn ngữ Go 2 | // và thư viện chuẩn trong Go xử lý chuỗi một cách rất độc đáo - coi nó 3 | // như một container chứa các ký tự được mã hóa theo [UTF-8](https://en.wikipedia.org/wiki/UTF-8). 4 | // Trong các ngôn ngữ khác, chuỗi được tạo thành từ các "ký tự". 5 | // Trong Go, khái niệm ký tự được gọi là `rune` - nó là một số nguyên 6 | // biểu diễn một code point của Unicode. 7 | // [Bài viết trên blog Go](https://go.dev/blog/strings) là một bài viết 8 | // khá hay để giới thiệu về chủ đề này. 9 | package main 10 | 11 | import ( 12 | "fmt" 13 | "unicode/utf8" 14 | ) 15 | 16 | func main() { 17 | 18 | // `s` là một `string` được gán một giá trị literal 19 | // biểu diễn từ "hello" trong tiếng Thái Lan. Chuỗi 20 | // literal trong Go được mã hóa theo UTF-8. 21 | const s = "สวัสดี" 22 | 23 | // Bởi vì chuỗi tương đương với `[]byte`, nên đoạn code 24 | // dưới đây sẽ in ra độ dài của các bytes chứa trong chuỗi `s` 25 | fmt.Println("Len:", len(s)) 26 | 27 | // Truy cập vào một chuỗi sẽ cho ra các giá trị byte tại 28 | // từng chỉ số. Vòng lặp dưới đây sẽ in ra các giá trị hex 29 | // của các byte mà tạo thành các code point trong chuỗi `s`. 30 | for i := 0; i < len(s); i++ { 31 | fmt.Printf("%x ", s[i]) 32 | } 33 | fmt.Println() 34 | 35 | // Để đếm xem có bao nhiêu _runes_ bên trong một chuỗi, chúng 36 | // ta có thể sử dụng package `utf8`. Lưu ý rằng thời gian chạy 37 | // của `RuneCountInString` phụ thuộc vào kích thước của chuỗi, 38 | // bởi vì nó phải giải mã từng UTF-8 rune tuần tự. Một số ký tự 39 | // trong tiếng Thái Lan được biểu diễn bởi nhiều điểm mã UTF-8, 40 | // cho nên kết quả của việc đếm này có thể gây bất ngờ. 41 | fmt.Println("Rune count:", utf8.RuneCountInString(s)) 42 | 43 | // Vòng lặp `range` xử lý chuỗi một cách rất đặc biệt và 44 | // giải mã từng `rune` cùng với vị trí của nó trong chuỗi. 45 | for idx, runeValue := range s { 46 | fmt.Printf("%#U starts at %d\n", runeValue, idx) 47 | } 48 | 49 | // Ta có thể đạt được kết quả tương tự bằng cách sử dụng 50 | // hàm `utf8.DecodeRuneInString`. 51 | fmt.Println("\nUsing DecodeRuneInString") 52 | for i, w := 0, 0; i < len(s); i += w { 53 | runeValue, width := utf8.DecodeRuneInString(s[i:]) 54 | fmt.Printf("%#U starts at %d\n", runeValue, i) 55 | w = width 56 | 57 | // Đoạn code dưới đây minh họa việc truyền một giá trị `rune` 58 | // vào một hàm. 59 | examineRune(runeValue) 60 | } 61 | } 62 | 63 | func examineRune(r rune) { 64 | 65 | // Các giá trị được chứa trong dấu nháy đơn là _rune literals_. 66 | // Chúng ta có thể so sánh trực tiếp một giá trị `rune` với một rune literal 67 | if r == 't' { 68 | fmt.Println("found tee") 69 | } else if r == 'ส' { 70 | fmt.Println("found so sua") 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /examples/strings-and-runes/strings-and-runes.hash: -------------------------------------------------------------------------------- 1 | 615d192bb48431b4878444f3c8b2a870db8264ee 2 | jIcp9YZbFXF 3 | -------------------------------------------------------------------------------- /examples/strings-and-runes/strings-and-runes.sh: -------------------------------------------------------------------------------- 1 | $ go run strings-and-runes.go 2 | Len: 18 3 | e0 b8 aa e0 b8 a7 e0 b8 b1 e0 b8 aa e0 b8 94 e0 b8 b5 4 | Rune count: 6 5 | U+0E2A 'ส' starts at 0 6 | U+0E27 'ว' starts at 3 7 | U+0E31 'ั' starts at 6 8 | U+0E2A 'ส' starts at 9 9 | U+0E14 'ด' starts at 12 10 | U+0E35 'ี' starts at 15 11 | 12 | Using DecodeRuneInString 13 | U+0E2A 'ส' starts at 0 14 | found so sua 15 | U+0E27 'ว' starts at 3 16 | U+0E31 'ั' starts at 6 17 | U+0E2A 'ส' starts at 9 18 | found so sua 19 | U+0E14 'ด' starts at 12 20 | U+0E35 'ี' starts at 15 -------------------------------------------------------------------------------- /examples/struct-embedding/struct-embedding.go: -------------------------------------------------------------------------------- 1 | // Go hỗ trợ _embedding_ của structs and interfaces 2 | // để thể hiện liền mạch hơn __thành phần_ của kiểu. 3 | // Điều này không nên nhầm với `//go:embed`, đây là 4 | // lệnh Go được giới thiệu trực tiếp trong bản Go 1.16+ 5 | // để nhúng các tệp và thư mục vào tệp nhị phân của ứng dụng. 6 | 7 | package main 8 | 9 | import "fmt" 10 | 11 | type base struct { 12 | num int 13 | } 14 | 15 | func (b base) describe() string { 16 | return fmt.Sprintf("base with num=%v", b.num) 17 | } 18 | 19 | // Một `container` được _embeds_ (nhúng) a `base`. Một embedding giống như 20 | // một trường không có tên. 21 | type container struct { 22 | base 23 | str string 24 | } 25 | 26 | func main() { 27 | 28 | // Khi tạo structs với literals,chúng ta phải 29 | // khởi tạo embedding rõ ràng; ở dây 30 | // kiểu embedded struct đóng vai trò là tên trường. 31 | co := container{ 32 | base: base{ 33 | num: 1, 34 | }, 35 | str: "some name", 36 | } 37 | 38 | //Chúng ta có thể truy cập trực tiếp vào các trường cơ sở của `co`, 39 | // ví dụ `co.num`. 40 | fmt.Printf("co={num: %v, str: %v}\n", co.num, co.str) 41 | 42 | // Ngoài ra, chúng ta có thể định nghĩa đường dẫn đầy đủ 43 | // bằng cách sử dụng tên của kiểu embedded struct. 44 | fmt.Println("also num:", co.base.num) 45 | 46 | // Vì `container` embeds `base`, các phương thức 47 | // của `base` cũng trở thành phương thức của `container`. 48 | // Ở đây, chúng ta gọi một phương thức được nhúng từ `base` 49 | // trực tiếp từ `co`. 50 | fmt.Println("describe:", co.describe()) 51 | 52 | type describer interface { 53 | describe() string 54 | } 55 | 56 | // Embedding structs với các phương thức có thể được sử dụng 57 | // để cấp cho hiện thực của interface dựa trên structs khác. 58 | // Điều này cho chúng ta thấy một `container` hiện thực 59 | // `describer` interface bời vì nó embeds `base`. 60 | var d describer = co 61 | fmt.Println("describer:", d.describe()) 62 | } 63 | -------------------------------------------------------------------------------- /examples/struct-embedding/struct-embedding.hash: -------------------------------------------------------------------------------- 1 | 507125c300f9e25c48acfbbd49fb6803b99f56ba 2 | a7GWoQiJtzu 3 | -------------------------------------------------------------------------------- /examples/struct-embedding/struct-embedding.sh: -------------------------------------------------------------------------------- 1 | $ go run struct-embedding.go 2 | co={num: 1, str: some name} 3 | also num: 1 4 | describe: base with num=1 5 | describer: base with num=1 -------------------------------------------------------------------------------- /examples/structs/structs.go: -------------------------------------------------------------------------------- 1 | // _Struct_ của Go là tập hợp các trường dữ liệu được khai báo kiểu. 2 | // Chúng được sử dụng để nhóm các dữ liệu lại với nhau 3 | // để tạo thành các bản ghi. 4 | package main 5 | 6 | import "fmt" 7 | 8 | // Kiểu struct `person` này có các trường `name` và `age`. 9 | type person struct { 10 | name string 11 | age int 12 | } 13 | 14 | // `newPerson` tạo một struct `person` mới với tên được truyền vào. 15 | func newPerson(name string) *person { 16 | // Bạn có thể trả về một cách an toàn một con trỏ của biến cục bộ 17 | // vì biến cục bộ sẽ tồn tại trong phạm vi của hàm. 18 | p := person{name: name} 19 | p.age = 42 20 | return &p 21 | } 22 | 23 | func main() { 24 | 25 | // Cú pháp này tạo một struct mới. 26 | fmt.Println(person{"Bob", 20}) 27 | 28 | // Bạn có thể đặt tên cho các trường khi khởi tạo một struct. 29 | fmt.Println(person{name: "Alice", age: 30}) 30 | 31 | // Các trường bị bỏ qua sẽ được gán giá trị mặc định. 32 | fmt.Println(person{name: "Fred"}) 33 | 34 | // Kí tự `&` trước tên struct sẽ trả về một con trỏ đến struct đó. 35 | fmt.Println(&person{name: "Ann", age: 40}) 36 | 37 | // Một cách viết phổ biến là đóng gói việc tạo một struct mới trong các hàm khởi tạo. 38 | fmt.Println(newPerson("Jon")) 39 | 40 | // Truy cập vào các trường của struct bằng dấu chấm. 41 | s := person{name: "Sean", age: 50} 42 | fmt.Println(s.name) 43 | 44 | // Bạn có thể sử dụng dấu chấm với con trỏ struct - các 45 | // con trỏ sẽ được tự động giải tham chiếu. 46 | sp := &s 47 | fmt.Println(sp.age) 48 | 49 | // Các struct có thể thay đổi được. 50 | sp.age = 51 51 | fmt.Println(sp.age) 52 | } 53 | -------------------------------------------------------------------------------- /examples/structs/structs.hash: -------------------------------------------------------------------------------- 1 | 019971bf62152c59021013b13b511b40c10dcc84 2 | FKBAjv9w7wq 3 | -------------------------------------------------------------------------------- /examples/structs/structs.sh: -------------------------------------------------------------------------------- 1 | $ go run structs.go 2 | {Bob 20} 3 | {Alice 30} 4 | {Fred 0} 5 | &{Ann 40} 6 | &{Jon 42} 7 | Sean 8 | 50 9 | 51 -------------------------------------------------------------------------------- /examples/switch/switch.go: -------------------------------------------------------------------------------- 1 | // Câu lệnh _Switch_ thể hiện điều kiện qua nhiều nhánh. 2 | package main 3 | 4 | import ( 5 | "fmt" 6 | "time" 7 | ) 8 | 9 | func main() { 10 | 11 | // Đây là một `switch` căn bản. 12 | i := 2 13 | fmt.Print("Write ", i, " as ") 14 | switch i { 15 | case 1: 16 | fmt.Println("one") 17 | case 2: 18 | fmt.Println("two") 19 | case 3: 20 | fmt.Println("three") 21 | } 22 | 23 | // Bạn có thể sử dụng dấu phẩy `,` để sử dụng nhiều biểu thức 24 | // trong cùng một câu lệnh `case`. Chúng ta cũng có thể sử dụng 25 | // `default` trong ví dụ này. 26 | switch time.Now().Weekday() { 27 | case time.Saturday, time.Sunday: 28 | fmt.Println("It's the weekend") 29 | default: 30 | fmt.Println("It's a weekday") 31 | } 32 | 33 | // `switch` khi không có biểu thức sẽ tương tự với việc 34 | // sử dụng logic if/else. Ở đây chúng ta thể hiện cách 35 | // biểu thức `case` có thể không phải là hằng số. 36 | t := time.Now() 37 | switch { 38 | case t.Hour() < 12: 39 | fmt.Println("It's before noon") 40 | default: 41 | fmt.Println("It's after noon") 42 | } 43 | 44 | // Loại của `switch` so sánh các loại với nhau, thay vì so sánh giá trị. 45 | // Bạn có thể dùng việc đó để phát hiện loại của giá trị interface. 46 | // Trong ví dụ này, biến `t` sẽ có loại tương ứng với mệnh đề của nó. 47 | whatAmI := func(i interface{}) { 48 | switch t := i.(type) { 49 | case bool: 50 | fmt.Println("I'm a bool") 51 | case int: 52 | fmt.Println("I'm an int") 53 | default: 54 | fmt.Printf("Don't know type %T\n", t) 55 | } 56 | } 57 | whatAmI(true) 58 | whatAmI(1) 59 | whatAmI("hey") 60 | } 61 | -------------------------------------------------------------------------------- /examples/switch/switch.hash: -------------------------------------------------------------------------------- 1 | 8117a502323001046b84ab4bcc2315c4e502cd95 2 | alHLv5naT9d 3 | -------------------------------------------------------------------------------- /examples/switch/switch.sh: -------------------------------------------------------------------------------- 1 | $ go run switch.go 2 | Write 2 as two 3 | It's a weekday 4 | It's after noon 5 | I'm a bool 6 | I'm an int 7 | Don't know type string 8 | -------------------------------------------------------------------------------- /examples/temporary-files-and-directories/temporary-files-and-directories.go: -------------------------------------------------------------------------------- 1 | // Trong suốt quá trình thực thi chương trình, ta thường muốn tạo 2 | // dữ liệu tạm mà không cần thiết sau khi chương trình kết thúc. 3 | // *Tập tin và thư mục tạm thời* rất hữu ích cho mục đích này 4 | // vì về lâu dài chúng không khiến cho hệ thống tập tin 5 | // chứa nhiều tập tin rác. 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "os" 12 | "path/filepath" 13 | ) 14 | 15 | func check(e error) { 16 | if e != nil { 17 | panic(e) 18 | } 19 | } 20 | 21 | func main() { 22 | 23 | // Cách dễ nhất để tạo một tập tin tạm thời là bằng cách 24 | // gọi `os.CreateTemp`. Nó tạo một tập tin *và* 25 | // mở nó để đọc và ghi. Chúng ta dùng `""` làm tham số đầu tiên, 26 | // vì vậy `os.CreateTemp` sẽ tạo tập tin trong vị trí mặc định 27 | // của hệ điều hành. 28 | f, err := os.CreateTemp("", "sample") 29 | check(err) 30 | 31 | // Hiển thị tên của tập tin tạm thời. Trên các hệ điều hành 32 | // dựa trên Unix, thư mục có thể là `/tmp`. Tên tập tin bắt đầu 33 | // bằng tiền tố được truyền vào làm tham số thứ hai của `os.CreateTemp` 34 | // và các tham số còn lại được chọn tự động để đảm bảo rằng 35 | // việc thực thi đồng thời sẽ luôn tạo ra các tên tập tin khác nhau. 36 | fmt.Println("Temp file name:", f.Name()) 37 | 38 | // Sau khi chúng ta hoàn thành, tập tin tạm thời sẽ được xóa. 39 | // Hệ điều hành có thể sẽ tự động xóa các tập tin tạm thời sau 40 | // một khoảng thời gian nhất định, nhưng tốt hơn là chúng ta 41 | // nên làm điều này một cách tường minh. 42 | defer os.Remove(f.Name()) 43 | 44 | // Ta có thể ghi một số dữ liệu vào tập tin. 45 | _, err = f.Write([]byte{1, 2, 3, 4}) 46 | check(err) 47 | 48 | // Nếu chúng ta có ý định ghi nhiều tập tin tạm thời, 49 | // ta nên ưu tiên tạo một *thư mục tạm thời*. 50 | // Các tham số của `os.MkdirTemp` cũng tương tự như `os.CreateTemp`, 51 | // nhưng nó trả về một tên thư mục thay vì một tập tin đang mở. 52 | dname, err := os.MkdirTemp("", "sampledir") 53 | check(err) 54 | fmt.Println("Temp dir name:", dname) 55 | 56 | defer os.RemoveAll(dname) 57 | 58 | // Giờ thì ta có thể tạo tên tập tin tạm thời bằng cách 59 | // thêm tiền tố vào tên thư mục tạm thời. 60 | fname := filepath.Join(dname, "file1") 61 | err = os.WriteFile(fname, []byte{1, 2}, 0666) 62 | check(err) 63 | } 64 | -------------------------------------------------------------------------------- /examples/temporary-files-and-directories/temporary-files-and-directories.hash: -------------------------------------------------------------------------------- 1 | 20d94c34d04c81ba50cac02bada8be1b094160f2 2 | gai1Q5ktv99 3 | -------------------------------------------------------------------------------- /examples/temporary-files-and-directories/temporary-files-and-directories.sh: -------------------------------------------------------------------------------- 1 | $ go run temporary-files-and-directories.go 2 | Temp file name: /tmp/sample610887201 3 | Temp dir name: /tmp/sampledir898854668 -------------------------------------------------------------------------------- /examples/testing-and-benchmarking/main_test.go: -------------------------------------------------------------------------------- 1 | // Unit test là một phần quan trọng khi viết Go. 2 | // Package `testing` cung cấp những công cụ chúng ta cần 3 | // để viết unit test and lệnh `go test` để chạy test. 4 | 5 | // Để trình diễn, đoạn code này nằm trong package `main`, 6 | // nhưng nó có thể nằm trong bất cứ package nào. Code 7 | // testing thường nằm trong cùng package với code nó 8 | // test. 9 | 10 | package main 11 | 12 | import ( 13 | "fmt" 14 | "testing" 15 | ) 16 | 17 | // Chúng ta sẽ test implementation đơn giản của số 18 | // nguyên nhỏ nhất. Thông thường, code chúng ta test 19 | // sẽ nằm trong source file được đặt tên như là 20 | // `intutils.go`, và file test cho nó thường được đặt 21 | // tên `inutils_test.go`. 22 | func IntMin(a, b int) int { 23 | if a < b { 24 | return a 25 | } 26 | return b 27 | } 28 | 29 | // Test dược tạo bằng viết một hàm với tên bắt đầu với 30 | // `Test`. 31 | func TestIntMinBasic(t *testing.T) { 32 | ans := IntMin(2, -2) 33 | if ans != -2 { 34 | // `t.Error*` sẽ report test fail nhưng tiếp tục 35 | // chạy test. `t.Fatal*` sẽ report test fail 36 | // nhưng sẽ dừng test ngay lập tức. 37 | t.Errorf("IntMin(2, -2) = %d; want -2", ans) 38 | } 39 | } 40 | 41 | // Viết test có thể lặp đi lặp lại, bởi thế người ta 42 | // hay dùng *table-driven style*, khi đầu vào test và 43 | // đầu ra mong đợi được list trong một bảng và một 44 | // vòng lặp đơn đi qua chúng và thực hiện logic test. 45 | func TestIntMinTableDriven(t *testing.T) { 46 | var tests = []struct { 47 | a, b int 48 | want int 49 | }{ 50 | {0, 1, 0}, 51 | {1, 0, 0}, 52 | {2, -2, -2}, 53 | {0, -1, -1}, 54 | {-1, 0, -1}, 55 | } 56 | 57 | for _, tt := range tests { 58 | // t.Run cho phép chạy "subtest", mỗi cái cho 59 | // một mục của bảng. Chúng được hiển thị riêng 60 | // rẽ khi thực hiện `go test -v`. 61 | testname := fmt.Sprintf("%d,%d", tt.a, tt.b) 62 | t.Run(testname, func(t *testing.T) { 63 | ans := IntMin(tt.a, tt.b) 64 | if ans != tt.want { 65 | t.Errorf("got %d, want %d", ans, tt.want) 66 | } 67 | }) 68 | } 69 | } 70 | 71 | // Benchmark test thường nằm trong các file `_test.go` 72 | // và tên bắt đầu với `Benchmark`. `testing` runner 73 | // thực thi mỗi hàm benchmark một vài lần, tăng `bn.N` 74 | // trên mỗi lần chạy cho đến khi đo được chính xác. 75 | func BenchmarkIntMin(b *testing.B) { 76 | // Thường benchmark chạy một hàm mà chúng ta 77 | // benchmark trong vòng lặp `b.N` lần. 78 | for i := 0; i < b.N; i++ { 79 | IntMin(1, 2) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /examples/testing-and-benchmarking/main_test.sh: -------------------------------------------------------------------------------- 1 | # Chạy tất cả các test trong project hiện tại ở chế độ 2 | # dài. 3 | $ go test -v 4 | == RUN TestIntMinBasic 5 | --- PASS: TestIntMinBasic (0.00s) 6 | === RUN TestIntMinTableDriven 7 | === RUN TestIntMinTableDriven/0,1 8 | === RUN TestIntMinTableDriven/1,0 9 | === RUN TestIntMinTableDriven/2,-2 10 | === RUN TestIntMinTableDriven/0,-1 11 | === RUN TestIntMinTableDriven/-1,0 12 | --- PASS: TestIntMinTableDriven (0.00s) 13 | --- PASS: TestIntMinTableDriven/0,1 (0.00s) 14 | --- PASS: TestIntMinTableDriven/1,0 (0.00s) 15 | --- PASS: TestIntMinTableDriven/2,-2 (0.00s) 16 | --- PASS: TestIntMinTableDriven/0,-1 (0.00s) 17 | --- PASS: TestIntMinTableDriven/-1,0 (0.00s) 18 | PASS 19 | ok examples/testing-and-benchmarking 0.023s 20 | 21 | # Chạy tất cả các benchmark trong project hiện tại. Tất 22 | # cả các test chạy độ ưu tiên để benchmark. Cờ `bench` 23 | # lọc các tên hàm benchmark với regexp. 24 | $ go test -bench=. 25 | goos: darwin 26 | goarch: arm64 27 | pkg: examples/testing 28 | BenchmarkIntMin-8 1000000000 0.3136 ns/op 29 | PASS 30 | ok examples/testing-and-benchmarking 0.351s -------------------------------------------------------------------------------- /examples/testing-and-benchmarking/testing-and-benchmarking.hash: -------------------------------------------------------------------------------- 1 | 1654554dcf5b061ef9b381efe233d63ba89857f1 2 | xUQB8KOr5hn 3 | -------------------------------------------------------------------------------- /examples/text-templates/text-templates.go: -------------------------------------------------------------------------------- 1 | // Go hỗ trợ các công cụ tích hợp sẵn cho việc tạo nội dung động hoặc hiển thị 2 | // nội dung tùy chỉnh ra cho người dùng với package `text/template`. Một package tương tự 3 | // tên là `html/template` cũng cung cấp các API tương tự nhưng có thêm các 4 | // tính năng bảo mật và khuyến khích được sử dụng để tạo nội dung có định dạng HTML. 5 | package main 6 | 7 | import ( 8 | "os" 9 | "text/template" 10 | ) 11 | 12 | func main() { 13 | 14 | // Ta có thể tạo ra một template mới và truyền vào nội dung của nó 15 | // từ một chuỗi. 16 | // Template là một sự kết hợp của văn bản tĩnh và các "hành động" (action) được gói trong dấu 17 | // `{{...}}`, thứ thường được sử dụng để chèn nội dung động. 18 | t1 := template.New("t1") 19 | t1, err := t1.Parse("Value is {{.}}\n") 20 | if err != nil { 21 | panic(err) 22 | } 23 | 24 | // Ngoài ra, ta có thể sử dụng hàm `template.Must` để 25 | // panic trong trường hợp `Parse` trả về một lỗi. Điều này 26 | // đặc biệt hữu ích cho các template được khởi tạo 27 | // với phạm vi toàn cục (global scope). 28 | t1 = template.Must(t1.Parse("Value: {{.}}\n")) 29 | 30 | // Bằng việc "thực thi" template, ta tạo ra nội dung của nó 31 | // với các giá trị cụ thể của các action. Action được đặt trong `{{.}}` 32 | // được thay thế bằng giá trị được truyền vào `Execute` dưới dạng tham số. 33 | t1.Execute(os.Stdout, "some text") 34 | t1.Execute(os.Stdout, 5) 35 | t1.Execute(os.Stdout, []string{ 36 | "Go", 37 | "Rust", 38 | "C++", 39 | "C#", 40 | }) 41 | 42 | // Hàm trợ giúp mà ta sẽ sử dụng bên dưới. 43 | Create := func(name, t string) *template.Template { 44 | return template.Must(template.New(name).Parse(t)) 45 | } 46 | 47 | // Nếu dữ liệu là một struct, ta có thể sử dụng action `{{.FieldName}}` để truy cập 48 | // các trường của struct đó. Các trường phải được export (được khai báo với chữ cái đầu tiên viết hoa) 49 | // để có thể truy cập bởi template khi template đang được thực thi. 50 | t2 := Create("t2", "Name: {{.Name}}\n") 51 | 52 | t2.Execute(os.Stdout, struct { 53 | Name string 54 | }{"Jane Doe"}) 55 | 56 | // Tương tự với maps; với maps thì không có hạn chế về chữ hoa/chữ thường 57 | // đối với tên khóa (key names). 58 | 59 | t2.Execute(os.Stdout, map[string]string{ 60 | "Name": "Mickey Mouse", 61 | }) 62 | 63 | // if/else hỗ trợ việc thực thi có điều kiện trong template. Một giá trị được coi là 64 | // false nếu nó là giá trị mặc định của một kiểu dữ liệu, ví dụ như 0, một chuỗi rỗng, 65 | // con trỏ nil, v.v. 66 | // Ví dụ dưới đây minh họa một tính năng khác 67 | // của template: loại bỏ các khoảng trắng bằng cách sử dụng `-` trong các action. 68 | t3 := Create("t3", 69 | "{{if . -}} yes {{else -}} no {{end}}\n") 70 | t3.Execute(os.Stdout, "not empty") 71 | t3.Execute(os.Stdout, "") 72 | 73 | // Những khối vòng lặp bằng range (range blocks) cho phép ta lặp qua các 74 | // slices, arrays, maps hoặc channels. Trong range block, phần `{{.}}` được gán cho 75 | // giá trị phần tử hiện tại của vòng lặp. 76 | t4 := Create("t4", 77 | "Range: {{range .}}{{.}} {{end}}\n") 78 | t4.Execute(os.Stdout, 79 | []string{ 80 | "Go", 81 | "Rust", 82 | "C++", 83 | "C#", 84 | }) 85 | } 86 | -------------------------------------------------------------------------------- /examples/text-templates/text-templates.hash: -------------------------------------------------------------------------------- 1 | e32988c2ba8573c96144760016750b106c672326 2 | VqT7hd7x951 3 | -------------------------------------------------------------------------------- /examples/text-templates/text-templates.sh: -------------------------------------------------------------------------------- 1 | $ go run templates.go 2 | Value: some text 3 | Value: 5 4 | Value: [Go Rust C++ C#] 5 | Name: Jane Doe 6 | Name: Mickey Mouse 7 | yes 8 | no 9 | Range: Go Rust C++ C# -------------------------------------------------------------------------------- /examples/tickers/tickers.go: -------------------------------------------------------------------------------- 1 | // [Timers](timers) được dùng khi bạn muốn thực hiện một 2 | // việc một lần duy nhất ở tương lai - _tickers_ được dùng khi 3 | // bạn muốn thực hiện một việc lặp đi lặp lại vào các khoảng thời 4 | // gian đều đặn. Dưới đây là một ví dụ của một ticker thực hiện một 5 | // việc lặp lại định kì cho đến khi chúng ta dừng nó lại. 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "time" 12 | ) 13 | 14 | func main() { 15 | 16 | // Tickers sử dụng một cơ chế tương tự như timers: một 17 | // channel được gửi các giá trị. Ở đây chúng ta sẽ sử dụng hàm 18 | // `select` có sẵn trên channel để chờ các giá trị được 19 | // gửi đến mỗi 500ms. 20 | ticker := time.NewTicker(500 * time.Millisecond) 21 | done := make(chan bool) 22 | 23 | go func() { 24 | for { 25 | select { 26 | case <-done: 27 | return 28 | case t := <-ticker.C: 29 | fmt.Println("Tick at", t) 30 | } 31 | } 32 | }() 33 | 34 | // Tickers cũng có thể bị dừng giống như timers. Một khi mà ticker bị 35 | // dừng thì nó sẽ không nhận bất kì giá trị nào gửi đến channel của 36 | // nó nữa. Chúng ta sẽ dừng tickers của mình sau 1600ms. 37 | time.Sleep(1600 * time.Millisecond) 38 | ticker.Stop() 39 | done <- true 40 | fmt.Println("Ticker stopped") 41 | } 42 | -------------------------------------------------------------------------------- /examples/tickers/tickers.hash: -------------------------------------------------------------------------------- 1 | 9c7d28dd4efc066bba2bca12ab74bb5cd9f8d5dc 2 | D_1HwgSjm9W 3 | -------------------------------------------------------------------------------- /examples/tickers/tickers.sh: -------------------------------------------------------------------------------- 1 | # Khi chúng ta chạy chương trình này 2 | # thì ticker sẽ lặp lại 3 lần 3 | # trước khi chúng ta dừng nó. 4 | $ go run tickers.go 5 | Tick at 2012-09-23 11:29:56.487625 -0700 PDT 6 | Tick at 2012-09-23 11:29:56.988063 -0700 PDT 7 | Tick at 2012-09-23 11:29:57.488076 -0700 PDT 8 | Ticker stopped -------------------------------------------------------------------------------- /examples/time-formatting-parsing/time-formatting-parsing.go: -------------------------------------------------------------------------------- 1 | // Go hỗ trợ định dạng và phân tích thời gian 2 | // thông qua các layout dựa trên patterns. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | p := fmt.Println 12 | 13 | // Dưới đây là một ví dụ cơ bản về định dạng thời gian 14 | // theo RFC3339, sử dụng hằng số layout tương ứng. 15 | t := time.Now() 16 | p(t.Format(time.RFC3339)) 17 | 18 | // Phân tích thời gian sử dụng cùng các giá trị layout 19 | // với `Format`. 20 | t1, e := time.Parse( 21 | time.RFC3339, 22 | "2012-11-01T22:08:41+00:00") 23 | p(t1) 24 | 25 | // `Format` và `Parse` sử dụng các layout dựa trên ví dụ. 26 | // Thông thường bạn sẽ sử dụng một hằng số từ package `time` cho 27 | // các layout này, nhưng bạn cũng có thể cung cấp các layout 28 | // tùy chỉnh. Các layout phải sử dụng thời gian tham chiếu 29 | // `Mon Jan 2 15:04:05 MST 2006` để hiển thị pattern dùng để 30 | // định dạng/ phân tích một đối tượng time/chuỗi thời gian cho trước. 31 | // Thời gian được dùng làm ví dụ phải chính xác như sau: năm 2006, 32 | // 15 cho giờ, Monday cho ngày trong tuần, v.v. 33 | p(t.Format("3:04PM")) 34 | p(t.Format("Mon Jan _2 15:04:05 2006")) 35 | p(t.Format("2006-01-02T15:04:05.999999-07:00")) 36 | form := "3 04 PM" 37 | t2, e := time.Parse(form, "8 41 PM") 38 | p(t2) 39 | 40 | // Để biểu diễn theo dạng số, bạn cũng có thể sử dụng 41 | // chuỗi định dạng chuẩn với các thành phần được trích xuất 42 | // từ giá trị thời gian. 43 | fmt.Printf("%d-%02d-%02dT%02d:%02d:%02d-00:00\n", 44 | t.Year(), t.Month(), t.Day(), 45 | t.Hour(), t.Minute(), t.Second()) 46 | 47 | // `Parse` sẽ trả về một lỗi khi đầu vào bị lỗi 48 | // để giải thích vấn đề khi phân tích thời gian. 49 | ansic := "Mon Jan _2 15:04:05 2006" 50 | _, e = time.Parse(ansic, "8:41PM") 51 | p(e) 52 | } 53 | -------------------------------------------------------------------------------- /examples/time-formatting-parsing/time-formatting-parsing.hash: -------------------------------------------------------------------------------- 1 | a2b94d0220616a324e246d210b89a9b00455f809 2 | FpDe57aPyVx 3 | -------------------------------------------------------------------------------- /examples/time-formatting-parsing/time-formatting-parsing.sh: -------------------------------------------------------------------------------- 1 | $ go run time-formatting-parsing.go 2 | 2014-04-15T18:00:15-07:00 3 | 2012-11-01 22:08:41 +0000 +0000 4 | 6:00PM 5 | Tue Apr 15 18:00:15 2014 6 | 2014-04-15T18:00:15.161182-07:00 7 | 0000-01-01 20:41:00 +0000 UTC 8 | 2014-04-15T18:00:15-00:00 9 | parsing time "8:41PM" as "Mon Jan _2 15:04:05 2006": ... -------------------------------------------------------------------------------- /examples/time/time.go: -------------------------------------------------------------------------------- 1 | // Go hỗ trợ rất nhiều về tính toán thời gian và khoảng thời gian; 2 | // đây là một số ví dụ. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "time" 8 | ) 9 | 10 | func main() { 11 | p := fmt.Println 12 | 13 | // Ta sẽ bắt đầu bằng việc tìm thời gian hiện tại. 14 | now := time.Now() 15 | p(now) 16 | 17 | // Ta có thể tạo một struct `time` bằng cách cung cấp năm, 18 | // tháng, ngày, v.v. Thời gian luôn được liên kết với một 19 | // `Location`, ví dụ như múi giờ. 20 | then := time.Date( 21 | 2009, 11, 17, 20, 34, 58, 651387237, time.UTC) 22 | p(then) 23 | 24 | // Ta có thể tách các thành phần khác nhau 25 | // của thời gian ra như mong muốn. 26 | p(then.Year()) 27 | p(then.Month()) 28 | p(then.Day()) 29 | p(then.Hour()) 30 | p(then.Minute()) 31 | p(then.Second()) 32 | p(then.Nanosecond()) 33 | p(then.Location()) 34 | 35 | // Các ngày trong tuần từ thứ 2 đến Chủ Nhật có thể được tách ra. 36 | p(then.Weekday()) 37 | 38 | // Các phương thức sau so sánh hai thời điểm, kiểm tra 39 | // xem thời điểm đầu tiên xảy ra trước, sau, hoặc cùng 40 | // thời điểm với thời điểm thứ hai. 41 | p(then.Before(now)) 42 | p(then.After(now)) 43 | p(then.Equal(now)) 44 | 45 | // Phương thức `Sub` trả về một `Duration` biểu diễn 46 | // khoảng thời gian giữa hai thời điểm. 47 | diff := now.Sub(then) 48 | p(diff) 49 | 50 | // Ta có thể tính toán độ dài của khoảng thời gian 51 | // theo các đơn vị khác nhau. 52 | p(diff.Hours()) 53 | p(diff.Minutes()) 54 | p(diff.Seconds()) 55 | p(diff.Nanoseconds()) 56 | 57 | // Ta có thể sử dụng `Add` để tăng thời gian lên một khoảng 58 | // thời gian cho trước, hoặc sử dụng cùng với `-` để 59 | // giảm thời gian đi một khoảng thời gian cho trước. 60 | p(then.Add(diff)) 61 | p(then.Add(-diff)) 62 | } 63 | -------------------------------------------------------------------------------- /examples/time/time.hash: -------------------------------------------------------------------------------- 1 | 61e3acfade8f5834ba39ad71778cef506d39a150 2 | P2_ZIjprnJq 3 | -------------------------------------------------------------------------------- /examples/time/time.sh: -------------------------------------------------------------------------------- 1 | $ go run time.go 2 | 2012-10-31 15:50:13.793654 +0000 UTC 3 | 2009-11-17 20:34:58.651387237 +0000 UTC 4 | 2009 5 | November 6 | 17 7 | 20 8 | 34 9 | 58 10 | 651387237 11 | UTC 12 | Tuesday 13 | true 14 | false 15 | false 16 | 25891h15m15.142266763s 17 | 25891.25420618521 18 | 1.5534752523711128e+06 19 | 9.320851514226677e+07 20 | 93208515142266763 21 | 2012-10-31 15:50:13.793654 +0000 UTC 22 | 2006-12-05 01:19:43.509120474 +0000 UTC 23 | 24 | # Tiếp theo chúng ta sẽ xem các cách xử lý về 25 | # thời gian liên quan đến Unix epoch. -------------------------------------------------------------------------------- /examples/timeouts/timeouts.go: -------------------------------------------------------------------------------- 1 | // _Timeouts_ là rất quan trọng đối với các chương trình 2 | // kết nối đến tài nguyên bên ngoài hoặc cần giới hạn thời 3 | // gian thực thi. Việc triển khai timeouts trong Go rất 4 | // dễ dàng và hiệu quả nhờ vào channels và `select`. 5 | 6 | package main 7 | 8 | import ( 9 | "fmt" 10 | "time" 11 | ) 12 | 13 | func main() { 14 | 15 | // Ví dụ, giả sử chúng ta đang thực hiện gọi một hàm 16 | // bên ngoài và trả về kết quả của nó trên channel `c1` 17 | // sau 2 giây. Lưu ý rằng channel được lưu vào bộ nhớ đệm, 18 | // vì vậy gửi/nhận trong goroutine là không chặn. Đây 19 | // là cách phổ biến để ngăn chặn rò rỉ goroutine trong 20 | // trường hợp channel không bao giờ được đọc. 21 | c1 := make(chan string, 1) 22 | go func() { 23 | time.Sleep(2 * time.Second) 24 | c1 <- "result 1" 25 | }() 26 | 27 | // Đây là cách dùng 'select' để quản lý timeout. 28 | // `res := <-c1` chờ giá trị từ c1 và `<-time.After` 29 | // chờ giá trị trả về sau 1 giây. 30 | // Vì `select` sẽ lựa chọn trường hợp đầu tiên 31 | // nhận được giá trị trả về, Chúng ta sẽ nhận 32 | // trường hợp timeout nếu các thao tác khác thực thi 33 | // lâu hơn 1 giây cho phép. 34 | // (mất 2 giây để nhận được giá trị từ `c1`) 35 | select { 36 | // Mất 2 giây để nhận được giá trị từ `c1` 37 | case res := <-c1: 38 | fmt.Println(res) 39 | // Mất 1 giây để nhận được giá trị từ `time.After` 40 | case <-time.After(1 * time.Second): 41 | fmt.Println("timeout 1") 42 | } 43 | 44 | // Nếu chúng ta nâng timeout lên 3 giây, thì 45 | // chúng ta sẽ nhận được giá trị từ `c2` trước và 46 | // in ra `result 2`. 47 | // [mất 3 giây để nhận được giá trị từ 48 | // `<-time.After(3 * time.Second)`] 49 | c2 := make(chan string, 1) 50 | go func() { 51 | time.Sleep(2 * time.Second) 52 | c2 <- "result 2" 53 | }() 54 | select { 55 | case res := <-c2: 56 | fmt.Println(res) 57 | case <-time.After(3 * time.Second): 58 | fmt.Println("timeout 2") 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /examples/timeouts/timeouts.hash: -------------------------------------------------------------------------------- 1 | b4c28a0a1c3895f0ec88c9b7fe3a44a2172fd40b 2 | ZjO-5hGgHnP 3 | -------------------------------------------------------------------------------- /examples/timeouts/timeouts.sh: -------------------------------------------------------------------------------- 1 | # Running this program shows the first operation timing 2 | # out and the second succeeding. 3 | $ go run timeouts.go 4 | timeout 1 5 | result 2 6 | -------------------------------------------------------------------------------- /examples/timers/timers.go: -------------------------------------------------------------------------------- 1 | // Trường hợp chúng ta muốn thực thi code Go tại một thời 2 | // điểm trong tương lai, hoặc lặp lại sau một khoảng thời 3 | // gian cố định. Go cung cấp _timer_ and _ticker_ hỗ trợ 4 | // thực hiện các việc trên dễ dàng hơn. Chúng ta bắt đầu 5 | // với timers trong ví dụ này 6 | // (tickers sẽ được giới thiệu ở phần tiếp theo) 7 | 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | "time" 13 | ) 14 | 15 | func main() { 16 | 17 | // Timers thể hiện một sự kiện thực thi trong tương lai. 18 | // Bạn quyết định timer sẽ chờ trong bao lâu, và timer sẽ 19 | // tạo ra channel và thực thi sau đúng thời gian chờ. 20 | // Ví dụ timer1 sẽ chờ 2 giây. 21 | timer1 := time.NewTimer(2 * time.Second) 22 | 23 | // `<-timer1.C` sẽ buộc channel `C` phải chờ cho đến khi 24 | // hết thời gian chờ của timer1 (2 giây) thì mới thực thi 25 | <-timer1.C 26 | fmt.Println("Timer 1 fired") 27 | 28 | // Nếu bạn chỉ muốn chờ một khoảng thời gian, bạn có thể 29 | // sử dụng `time.Sleep`. Việc sử dụng timer sẽ tiện hơn 30 | // vì bạn có thể hủy timer trước khi nó bắt đầu chạy. 31 | // Xem ví dụ dưới đây để hiểu rõ hơn. 32 | timer2 := time.NewTimer(time.Second) 33 | go func() { 34 | <-timer2.C 35 | fmt.Println("Timer 2 fired") 36 | }() 37 | stop2 := timer2.Stop() 38 | if stop2 { 39 | fmt.Println("Timer 2 stopped") 40 | } 41 | 42 | // Cho dù chờ đủ thời gian 2 giây 43 | // `time.Sleep(2 * time.Second)` 44 | // Kết quả vẫn là "Timer 2 stopped" 45 | // vì timer đã được hủy bởi `timer2.Stop()` 46 | // trước khi đến giờ thực thi. 47 | time.Sleep(2 * time.Second) 48 | } 49 | -------------------------------------------------------------------------------- /examples/timers/timers.hash: -------------------------------------------------------------------------------- 1 | 088dff53efbe924b487245da796a1b3cb80c9b3e 2 | EvLGizHOxZS 3 | -------------------------------------------------------------------------------- /examples/timers/timers.sh: -------------------------------------------------------------------------------- 1 | // The first timer will fire ~2s after we start the 2 | // program, but the second should be stopped before it has 3 | // a chance to fire. 4 | $ go run timers.go 5 | Timer 1 fired 6 | Timer 2 stopped 7 | -------------------------------------------------------------------------------- /examples/url-parsing/url-parsing.go: -------------------------------------------------------------------------------- 1 | // URLs cung cấp một [cách đồng nhất để định vị tài nguyên](https://adam.herokuapp.com/past/2010/3/30/urls_are_the_uniform_way_to_locate_resources/). 2 | // Dưới đây là cách phân tích URLs trong Go. 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "net" 9 | "net/url" 10 | ) 11 | 12 | func main() { 13 | 14 | // Chúng ta sẽ phân tích cú pháp URL ví dụ này, bao gồm một 15 | // scheme, thông tin xác thực, host (máy chủ), port (cổng), 16 | // path (đường dẫn), tham số truy vấn và phần đánh dấu truy vấn. 17 | s := "postgres://user:pass@host.com:5432/path?k=v#f" 18 | 19 | // Phân tích URL và đảm bảo không có lỗi. 20 | u, err := url.Parse(s) 21 | if err != nil { 22 | panic(err) 23 | } 24 | 25 | // Truy cập scheme khá đơn giản. 26 | fmt.Println(u.Scheme) 27 | 28 | // `User` chứa tất cả thông tin xác thực; gọi 29 | // `Username` và `Password` trên đối tượng này để 30 | // lấy từng giá trị riêng lẻ. 31 | fmt.Println(u.User) 32 | fmt.Println(u.User.Username()) 33 | p, _ := u.User.Password() 34 | fmt.Println(p) 35 | 36 | // `Host` chứa cả tên máy chủ và port, nếu có. 37 | // Sử dụng `SplitHostPort` để trích xuất chúng. 38 | fmt.Println(u.Host) 39 | host, port, _ := net.SplitHostPort(u.Host) 40 | fmt.Println(host) 41 | fmt.Println(port) 42 | 43 | // Ở đây chúng ta trích xuất đường dẫn và đoạn sau dấu `#` 44 | fmt.Println(u.Path) 45 | fmt.Println(u.Fragment) 46 | 47 | // Để lấy các tham số truy vấn trong chuỗi với định dạng `k=v`, 48 | // sử dụng `RawQuery`. Bạn cũng có thể phân tích các tham số truy vấn 49 | // thành một map. Các map của tham số truy vấn được phân tích thành 50 | // chuỗi đến mảng chuỗi, vì vậy hãy truy cập chỉ mục `[0]` 51 | // nếu bạn chỉ muốn lấy giá trị. 52 | fmt.Println(u.RawQuery) 53 | m, _ := url.ParseQuery(u.RawQuery) 54 | fmt.Println(m) 55 | fmt.Println(m["k"][0]) 56 | } 57 | -------------------------------------------------------------------------------- /examples/url-parsing/url-parsing.hash: -------------------------------------------------------------------------------- 1 | 396f174b736841fdd5deeeb220b2e0bdd83b06f6 2 | WtbKlMnQZFN 3 | -------------------------------------------------------------------------------- /examples/url-parsing/url-parsing.sh: -------------------------------------------------------------------------------- 1 | # Khi chạy chương trình phân tích URL của chúng ta, nó sẽ 2 | # hiện thị tất cả các phần khác nhau mà chúng ta 3 | # trích xuất được. 4 | $ go run url-parsing.go 5 | postgres 6 | user:pass 7 | user 8 | pass 9 | host.com:5432 10 | host.com 11 | 5432 12 | /path 13 | f 14 | k=v 15 | map[k:[v]] 16 | v -------------------------------------------------------------------------------- /examples/values/values.go: -------------------------------------------------------------------------------- 1 | // Go có nhiều loại value như strings (chuỗi), 2 | // integers (số nguyên), floats (số thực), booleans (true/false), ... 3 | // Dưới đây là một vài ví dụ cơ bản. 4 | 5 | package main 6 | 7 | import "fmt" 8 | 9 | func main() { 10 | 11 | // Chuỗi có thể liên kết với nhau bằng `+` 12 | fmt.Println("go" + "lang") 13 | 14 | // Số nguyên và số thực. 15 | fmt.Println("1+1 =", 1+1) 16 | fmt.Println("7.0/3.0 =", 7.0/3.0) 17 | 18 | // Booleans với các toán tử booleans. 19 | fmt.Println(true && false) 20 | fmt.Println(true || false) 21 | fmt.Println(!true) 22 | } 23 | -------------------------------------------------------------------------------- /examples/values/values.hash: -------------------------------------------------------------------------------- 1 | 0da36235d46639086627bf69fec35dd4e2eb80d0 2 | 47oiMQCMq6C 3 | -------------------------------------------------------------------------------- /examples/values/values.sh: -------------------------------------------------------------------------------- 1 | $ go run values.go 2 | golang 3 | 1+1 = 2 4 | 7.0/3.0 = 2.3333333333333335 5 | false 6 | true 7 | false 8 | -------------------------------------------------------------------------------- /examples/variables/variables.go: -------------------------------------------------------------------------------- 1 | // Trong go, biến được khai báo rõ ràng và sử dụng bởi 2 | // trình biên dịch để, ví dụ như kiểm tra đúng loại (type) của hàm gọi. 3 | package main 4 | 5 | import "fmt" 6 | 7 | func main() { 8 | 9 | // `var` khai báo 1 hoặc nhiều biến. 10 | var a = "initial" 11 | fmt.Println(a) 12 | 13 | // Bạn có thể khai báo nhiều biến một lúc. 14 | var b, c int = 1, 2 15 | fmt.Println(b, c) 16 | 17 | // Go sẽ suy ra loại của biến được khởi tạo. 18 | var d = true 19 | fmt.Println(d) 20 | 21 | // Những biến được khai báo mà không có 22 | // giá trị khởi tạo ban đầu sẽ có giá trị _zero-valued_ (giá trị 0). Ví dụ 23 | // Giá trị không (zero value) cho `int` là `0` 24 | var e int 25 | fmt.Println(e) 26 | 27 | // Cú pháp `:=` là viết tắt cho việc khai báo và 28 | // khởi tạo một biến, ví dụ: 29 | // `var f string = "apple"` trong trường hợp này. 30 | // Cú pháp này chỉ có thể dùng trong các hàm. 31 | f := "apple" 32 | fmt.Println(f) 33 | } 34 | -------------------------------------------------------------------------------- /examples/variables/variables.hash: -------------------------------------------------------------------------------- 1 | b8f4861719ff544e049b76304d70151fe4bbe076 2 | VDBD4-JPrzz 3 | -------------------------------------------------------------------------------- /examples/variables/variables.sh: -------------------------------------------------------------------------------- 1 | $ go run variables.go 2 | initial 3 | 1 2 4 | true 5 | 0 6 | apple 7 | -------------------------------------------------------------------------------- /examples/variadic-functions/variadic-functions.go: -------------------------------------------------------------------------------- 1 | // [_Variadic functions_](https://en.wikipedia.org/wiki/Variadic_function) 2 | // có thể được gọi với bất kỳ số lượng đối số theo sau nào 3 | // Ví dụ, `fmt.Println` là một hàm biến đổi thường gặp. 4 | 5 | package main 6 | 7 | import "fmt" 8 | 9 | // Đây là một hàm sẽ nhận bất kì số tuỳ ý loại `int` như 10 | // tham số. 11 | func sum(nums ...int) { 12 | fmt.Print(nums, " ") 13 | total := 0 14 | // Bên trong hàm, loại của `nums` tương ứng 15 | // với `[]int`. Chúng ta có thể gọi `len(nums)`, 16 | // lặp lại nó với `range`, etc. 17 | for _, num := range nums { 18 | total += num 19 | } 20 | fmt.Println(total) 21 | } 22 | 23 | func main() { 24 | 25 | // Hàm biến đổi có thể được gọi một cách bình thường 26 | // với các từng tham số. 27 | sum(1, 2) 28 | sum(1, 2, 3) 29 | 30 | // Nếu bạn đã có nhiều tham số trong một slice, 31 | // ứng dụng nó vào hàm biến đổi bằng cách sử dụng 32 | // `func(slice...)` như dưới đây. 33 | nums := []int{1, 2, 3, 4} 34 | sum(nums...) 35 | } 36 | -------------------------------------------------------------------------------- /examples/variadic-functions/variadic-functions.hash: -------------------------------------------------------------------------------- 1 | c72294934e01d432879176342e3a99cfef55cb86 2 | oyTh_W6FTim 3 | -------------------------------------------------------------------------------- /examples/variadic-functions/variadic-functions.sh: -------------------------------------------------------------------------------- 1 | $ go run variadic-functions.go 2 | [1 2] 3 3 | [1 2 3] 6 4 | [1 2 3 4] 10 5 | 6 | # Một tính năng quan trọng khác của hàm trong Go là khả 7 | # năng tạo closures, cái mà chúng ta sẽ xem qua tiếp theo. 8 | -------------------------------------------------------------------------------- /examples/waitgroups/waitgroups.go: -------------------------------------------------------------------------------- 1 | // Để đợi nhiều goroutine kết thúc, ta có thể sử dụng 2 | // *wait group*. 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | "sync" 8 | "time" 9 | ) 10 | 11 | // Đây sẽ là hàm mà ta sẽ chạy trong mỗi goroutine. 12 | func worker(id int) { 13 | fmt.Printf("Worker %d starting\n", id) 14 | 15 | // Dừng một khoảng để giả lập một tác vụ ngốn rất nhiều thời gian. 16 | time.Sleep(time.Second) 17 | fmt.Printf("Worker %d done\n", id) 18 | } 19 | 20 | func main() { 21 | 22 | // WaitGroup này được dùng để đợi cho tất cả các goroutine 23 | // được khởi tạo ở đây chạy xong. Lưu ý: nếu một WaitGroup được 24 | // truyền vào các hàm, nó nên được truyền bằng cách *sử dụng con trỏ*. 25 | var wg sync.WaitGroup 26 | 27 | // Khởi chạy một vài goroutine và tăng giá trị số lượng của WaitGroup 28 | // cho mỗi goroutine. 29 | for i := 1; i <= 5; i++ { 30 | wg.Add(1) 31 | // Tránh việc sử dụng lại cùng một giá trị `i` trong mỗi closure của goroutine. 32 | // Đọc [FAQ](https://golang.org/doc/faq#closures_and_goroutines) 33 | i := i 34 | 35 | // Bọc hàm gọi worker trong một closure để đảm bảo rằng 36 | // WaitGroup sẽ được thông báo khi một worker kết thúc. Bằng cách này 37 | // worker không cần phải quan tâm đến các kiểu nguyên thuỷ của concurrency 38 | // (concurrency primitives) trong quá trình thực thi. 39 | go func() { 40 | defer wg.Done() 41 | worker(i) 42 | }() 43 | } 44 | 45 | // Chặn cho đến khi giá trị số lượng của WaitGroup trở về 0, tức là 46 | // tất cả các worker đã thông báo rằng chúng đã chạy xong. 47 | wg.Wait() 48 | 49 | // Chú ý rằng: phương pháp này không có cách nào 50 | // để truyền lỗi trực tiếp từ các worker. Để sử dụng 51 | // trong các trường hợp nâng cao hơn, hãy xem xét sử dụng 52 | // [errgroup package](https://pkg.go.dev/golang.org/x/sync/errgroup). 53 | } 54 | -------------------------------------------------------------------------------- /examples/waitgroups/waitgroups.hash: -------------------------------------------------------------------------------- 1 | 94aa3a47c107d370105c323c9da6ef301e37e549 2 | kCO_0FXzQtB 3 | -------------------------------------------------------------------------------- /examples/waitgroups/waitgroups.sh: -------------------------------------------------------------------------------- 1 | $ go run waitgroups.go 2 | Worker 5 starting 3 | Worker 3 starting 4 | Worker 4 starting 5 | Worker 1 starting 6 | Worker 2 starting 7 | Worker 4 done 8 | Worker 1 done 9 | Worker 2 done 10 | Worker 5 done 11 | Worker 3 done 12 | 13 | # Thứ tự các worker khởi chạy và hoàn thành 14 | # có thể khác nhau cho mỗi lần chúng được gọi. -------------------------------------------------------------------------------- /examples/worker-pools/worker-pools.go: -------------------------------------------------------------------------------- 1 | // Trong ví dụ này, chúng ta sẽ xem xét cách triển khai 2 | // một _worker pool_ sử dụng goroutines và channels. 3 | 4 | package main 5 | 6 | import ( 7 | "fmt" 8 | "time" 9 | ) 10 | 11 | // Dưới đây là một worker mà ta sẽ thực thi nhiều 12 | // instance cùng lúc. Các worker này sẽ nhận 13 | // tác vụ từ channel `jobs` và gửi kết quả tương ứng 14 | // đến channel `results`. Ta sẽ dừng một giây mỗi tác vụ 15 | // để giả lập một tác vụ ngốn rất nhiều thời gian. 16 | func worker(id int, jobs <-chan int, results chan<- int) { 17 | for j := range jobs { 18 | fmt.Println("worker", id, "started job", j) 19 | time.Sleep(time.Second) 20 | fmt.Println("worker", id, "finished job", j) 21 | results <- j * 2 22 | } 23 | } 24 | 25 | func main() { 26 | 27 | // Để sử dụng pool worker của chúng ta, ta cần gửi 28 | // tác vụ cho chúng và thu thập kết quả. Ta sẽ tạo 29 | // 2 channel để thực hiện việc này. 30 | const numJobs = 5 31 | jobs := make(chan int, numJobs) 32 | results := make(chan int, numJobs) 33 | 34 | // Dưới đây ta khởi tạo 3 worker, đầu tiên chúng sẽ 35 | // bị chặn vì chưa có tác vụ nào. 36 | for w := 1; w <= 3; w++ { 37 | go worker(w, jobs, results) 38 | } 39 | 40 | // Ở đây ta gửi 5 `tác vụ` và sau đó `đóng` channel 41 | // đó để chỉ ra rằng đây là tất cả các tác vụ mà ta có. 42 | for j := 1; j <= numJobs; j++ { 43 | jobs <- j 44 | } 45 | close(jobs) 46 | 47 | // Cuối cùng, ta thu thập tất cả các kết quả xử lý các tác vụ. 48 | // Điều này cũng đảm bảo rằng các goroutine worker đã hoàn thành. 49 | // Một cách khác để đợi kết quả từ nhiều 50 | // goroutine là sử dụng [WaitGroup](waitgroups). 51 | for a := 1; a <= numJobs; a++ { 52 | <-results 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /examples/worker-pools/worker-pools.hash: -------------------------------------------------------------------------------- 1 | 0ec37adf32d87476767ee5c972813dc405d2a7bc 2 | tPBrZXWZvjC 3 | -------------------------------------------------------------------------------- /examples/worker-pools/worker-pools.sh: -------------------------------------------------------------------------------- 1 | # Chương trình được thực thi sẽ 2 | # hiển thị 5 tác vụ được thực thi bởi 3 | # các worker khác nhau. Chương trình 4 | # chỉ mất khoảng 2 giây để thực thi, mặc dù 5 | # nó thực hiện các tác vụ trong khoảng 6 | # tổng cộng là 5 giây, vì có 3 worker chạy song song. 7 | $ time go run worker-pools.go 8 | worker 1 started job 1 9 | worker 2 started job 2 10 | worker 3 started job 3 11 | worker 1 finished job 1 12 | worker 1 started job 4 13 | worker 2 finished job 2 14 | worker 2 started job 5 15 | worker 3 finished job 3 16 | worker 1 finished job 4 17 | worker 2 finished job 5 18 | 19 | real 0m2.358s -------------------------------------------------------------------------------- /examples/writing-files/writing-files.go: -------------------------------------------------------------------------------- 1 | // Ghi tập tin trong Go làm theo các mô hình tương tự 2 | // như những gì chúng ta thấy trước đó khi đọc tập tin 3 | 4 | package main 5 | 6 | import ( 7 | "bufio" 8 | "fmt" 9 | "os" 10 | ) 11 | 12 | func check(e error) { 13 | if e != nil { 14 | panic(e) 15 | } 16 | } 17 | 18 | func main() { 19 | 20 | // Để bắt đầu, đây là cách dump một chuỗi (hoặc 21 | // chỉ các byte) vào tập tin. 22 | // bytes) into a file. 23 | d1 := []byte("hello\ngo\n") 24 | err := os.WriteFile("/tmp/dat1", d1, 0644) 25 | check(err) 26 | 27 | // Để ghi các phần nhỏ hơn, hãy mở một tệp tin để ghi. 28 | f, err := os.Create("/tmp/dat2") 29 | check(err) 30 | 31 | // Luôn tốt khi sử dụng defer để `đóng` tập tin ngay lập tức 32 | // sau khi mở tập tin. 33 | defer f.Close() 34 | 35 | // Bạn có thể `ghi` các slice byte như bạn mong đợi. 36 | d2 := []byte{115, 111, 109, 101, 10} 37 | n2, err := f.Write(d2) 38 | check(err) 39 | fmt.Printf("wrote %d bytes\n", n2) 40 | 41 | // Hàm `WriteString` có sẵn. 42 | n3, err := f.WriteString("writes\n") 43 | check(err) 44 | fmt.Printf("wrote %d bytes\n", n3) 45 | 46 | // Sử dụng `Sync` để flush (đẩy) các bản ghi vào bộ lưu trữ ổn định. 47 | f.Sync() 48 | 49 | // `bufio` cung cấp các writer được đệm bên cạnh 50 | // bên cạnh các reader được đệm mà chúng ta đã thấy 51 | // trước đó. 52 | w := bufio.NewWriter(f) 53 | n4, err := w.WriteString("buffered\n") 54 | check(err) 55 | fmt.Printf("wrote %d bytes\n", n4) 56 | 57 | // Sử dụng `Flush` đảm bảo tất cả các hoạt động được đệm 58 | // đã được áp dụng cho writer cơ sở. 59 | w.Flush() 60 | 61 | } 62 | -------------------------------------------------------------------------------- /examples/writing-files/writing-files.hash: -------------------------------------------------------------------------------- 1 | 3055ce9260e3be615073cd5d9d9b09009fc923df 2 | AqMtNflQ-XK 3 | -------------------------------------------------------------------------------- /examples/writing-files/writing-files.sh: -------------------------------------------------------------------------------- 1 | # Hãy thử chạy code (mã) ghi tệp tin. 2 | $ go run writing-files.go 3 | wrote 5 bytes 4 | wrote 7 bytes 5 | wrote 9 bytes 6 | 7 | # Sau đó kiểm tra nội dung của các tập tin đã viết. 8 | $ cat /tmp/dat1 9 | hello 10 | go 11 | $ cat /tmp/dat2 12 | some 13 | writes 14 | buffered 15 | 16 | # Tiếp theo, chúng ta sẽ xem xét áp dụng một số 17 | # ý tưởng I/O tệp mà chúng ta vừa thấy cho các 18 | # luồng `sdtin` và `stdout`. -------------------------------------------------------------------------------- /examples/xml/xml.go: -------------------------------------------------------------------------------- 1 | // Go hỗ trợ sẵn cho việc mã hóa và giải mã XML và 2 | // các định dạng tương tự XML với package `encoding.xml`. 3 | package main 4 | 5 | import ( 6 | "encoding/xml" 7 | "fmt" 8 | ) 9 | 10 | // Kiểu Plant sẽ được ánh xạ sang định dạng XML. Tương tự như 11 | // các ví dụ về JSON, các tag của các trường sẽ chứa các chỉ 12 | // dẫn cho encoder và decoder. Ở đây ta sẽ sử dụng một số tính 13 | // năng đặc biệt của package XML: trường `XMLName` sẽ quy định 14 | // tên của phần tử XML đại diện cho struct này; 15 | // `id,attr` có nghĩa là trường `Id` là một _thuộc tính_ XML 16 | // chứ không phải là một phần tử lồng nhau. 17 | type Plant struct { 18 | XMLName xml.Name `xml:"plant"` 19 | Id int `xml:"id,attr"` 20 | Name string `xml:"name"` 21 | Origin []string `xml:"origin"` 22 | } 23 | 24 | func (p Plant) String() string { 25 | return fmt.Sprintf("Plant id=%v, name=%v, origin=%v", 26 | p.Id, p.Name, p.Origin) 27 | } 28 | 29 | func main() { 30 | coffee := &Plant{Id: 27, Name: "Coffee"} 31 | coffee.Origin = []string{"Ethiopia", "Brazil"} 32 | 33 | // Tạo ra biểu diễn XML của kiểu Plant; sử dụng 34 | // `MarshalIndent` để tạo ra một đầu ra dễ đọc hơn. 35 | out, _ := xml.MarshalIndent(coffee, " ", " ") 36 | fmt.Println(string(out)) 37 | 38 | // Để thêm một tiêu đề XML chung vào đầu ra, ta có thể 39 | // thêm nó vào một cách trực tiếp. 40 | fmt.Println(xml.Header + string(out)) 41 | 42 | // Sử dụng `Unmarshal` để phân tích cú pháp một luồng bytes 43 | // với đinh dạng XML thành một cấu trúc dữ liệu. Nếu XML 44 | // bị lỗi cú pháp hoặc không thể ánh xạ vào kiểu Plant, 45 | // một mô tả về lỗi sẽ được trả về. 46 | var p Plant 47 | if err := xml.Unmarshal(out, &p); err != nil { 48 | panic(err) 49 | } 50 | fmt.Println(p) 51 | 52 | tomato := &Plant{Id: 81, Name: "Tomato"} 53 | tomato.Origin = []string{"Mexico", "California"} 54 | 55 | // Tag `parent>child>plant` cho biết encoder sẽ lồng 56 | // tất cả các `plant` vào trong `...` 57 | type Nesting struct { 58 | XMLName xml.Name `xml:"nesting"` 59 | Plants []*Plant `xml:"parent>child>plant"` 60 | } 61 | 62 | nesting := &Nesting{} 63 | nesting.Plants = []*Plant{coffee, tomato} 64 | 65 | out, _ = xml.MarshalIndent(nesting, " ", " ") 66 | fmt.Println(string(out)) 67 | } 68 | -------------------------------------------------------------------------------- /examples/xml/xml.hash: -------------------------------------------------------------------------------- 1 | 5aeabb470d42c4599e6e9daff4ead987945b446b 2 | LxIgPJ8ELK9 3 | -------------------------------------------------------------------------------- /examples/xml/xml.sh: -------------------------------------------------------------------------------- 1 | $ go run xml.go 2 | 3 | Coffee 4 | Ethiopia 5 | Brazil 6 | 7 | 8 | 9 | Coffee 10 | Ethiopia 11 | Brazil 12 | 13 | Plant id=27, name=Coffee, origin=[Ethiopia Brazil] 14 | 15 | 16 | 17 | 18 | Coffee 19 | Ethiopia 20 | Brazil 21 | 22 | 23 | Tomato 24 | Mexico 25 | California 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mmcgrana/gobyexample 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/alecthomas/chroma v0.8.2 7 | github.com/aws/aws-sdk-go-v2 v1.9.0 8 | github.com/aws/aws-sdk-go-v2/config v1.7.0 9 | github.com/aws/aws-sdk-go-v2/service/s3 v1.14.0 10 | github.com/russross/blackfriday/v2 v2.1.0 11 | ) 12 | 13 | require ( 14 | github.com/aws/aws-sdk-go-v2/credentials v1.4.0 // indirect 15 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.5.0 // indirect 16 | github.com/aws/aws-sdk-go-v2/internal/ini v1.2.2 // indirect 17 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.3.0 // indirect 18 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.3.0 // indirect 19 | github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.6.0 // indirect 20 | github.com/aws/aws-sdk-go-v2/service/sso v1.4.0 // indirect 21 | github.com/aws/aws-sdk-go-v2/service/sts v1.7.0 // indirect 22 | github.com/aws/smithy-go v1.8.0 // indirect 23 | github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect 24 | github.com/dlclark/regexp2 v1.2.0 // indirect 25 | ) 26 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Go by Example: Không tìm thấy 6 | 7 | 8 | 9 |
10 |

Go by Example

11 |

Xin lỗi, tôi không tìm thấy thứ bạn đang tìm kiếm! Hãy kiểm tra trang chủ?

12 | 13 | 16 | 17 |
18 | 19 | 20 | -------------------------------------------------------------------------------- /public/clipboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s6k-gopher/gobyexample-vn/f8bbf1e0da33a70de05dfbe3da294fdb3db6be29/public/clipboard.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s6k-gopher/gobyexample-vn/f8bbf1e0da33a70de05dfbe3da294fdb3db6be29/public/favicon.ico -------------------------------------------------------------------------------- /public/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s6k-gopher/gobyexample-vn/f8bbf1e0da33a70de05dfbe3da294fdb3db6be29/public/play.png -------------------------------------------------------------------------------- /templates/404.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Go by Example: Không tìm thấy 6 | 7 | 8 | 9 |
10 |

Go by Example

11 |

Xin lỗi, tôi không tìm thấy thứ bạn đang tìm kiếm! Hãy kiểm tra trang chủ?

12 | {{ template "footer" }} 13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /templates/clipboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s6k-gopher/gobyexample-vn/f8bbf1e0da33a70de05dfbe3da294fdb3db6be29/templates/clipboard.png -------------------------------------------------------------------------------- /templates/example.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Go by Example: {{.Name}} 6 | 7 | 8 | 22 | 23 |
24 |

Go by Example: {{.Name}}

25 | {{range .Segs}} 26 | 27 | {{range .}} 28 | 29 | 32 | 36 | 37 | {{end}} 38 |
30 | {{.DocsRendered}} 31 | 33 | {{if .CodeRun}}{{end}} 34 | {{.CodeRendered}} 35 |
39 | {{end}} 40 | {{if .NextExample}} 41 |

42 | Ví dụ tiếp theo: {{.NextExample.Name}}. 43 |

44 | {{end}} 45 | {{ template "footer" }} 46 |
47 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /templates/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s6k-gopher/gobyexample-vn/f8bbf1e0da33a70de05dfbe3da294fdb3db6be29/templates/favicon.ico -------------------------------------------------------------------------------- /templates/footer.tmpl: -------------------------------------------------------------------------------- 1 | {{define "footer"}} 2 | 5 | {{end}} 6 | -------------------------------------------------------------------------------- /templates/index.tmpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Go by Example 6 | 7 | 8 | 9 |
10 |

Go by Example

11 |

12 | Go là một 13 | ngôn ngữ lập trình mã nguồn mở được thiết kế để 14 | xây dựng phần mềm đơn giản, nhanh chóng và đáng tin cậy. 15 | Vui lòng đọc 16 | tài liệu chính thức 17 | để tìm hiểu một chút về mã nguồn Go, các gói công cụ và mô-đun. 18 |

19 | 20 |

21 | Go by Example là một hướng dẫn bằng cách thực hành 22 | với Go sử dụng các ví dụ được chú thích rõ ràng. Hãy xem 23 | ví dụ đầu tiên hoặc 24 | duyệt qua danh sách bên dưới. 25 |

26 | 27 |
    28 | {{range .}} 29 |
  • {{.Name}}
  • 30 | {{end}} 31 |
32 | {{ template "footer" }} 33 |
34 | 35 | 36 | -------------------------------------------------------------------------------- /templates/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s6k-gopher/gobyexample-vn/f8bbf1e0da33a70de05dfbe3da294fdb3db6be29/templates/play.png -------------------------------------------------------------------------------- /tools/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | verbose() { 6 | ! test -z "$VERBOSE" 7 | } 8 | 9 | verbose && echo "Running tests..." 10 | tools/test 11 | 12 | verbose && echo "Formatting code..." 13 | tools/format 14 | 15 | verbose && echo "Measuring line lengths..." 16 | tools/measure 17 | 18 | # SITE_DIR is the final location where we want generated content to be 19 | SITE_DIR="public" 20 | 21 | # GENERATE_DIR is where the content will be generated initially 22 | GENERATE_DIR="$(mktemp -d)" 23 | 24 | function cleanup() { 25 | rm -rf "$GENERATE_DIR" 26 | } 27 | trap cleanup EXIT 28 | 29 | verbose && echo "Generating HTML to $GENERATE_DIR..." 30 | tools/generate $GENERATE_DIR 31 | 32 | # In TESTING mode, make sure that the generated content is identical to 33 | # what's already in SITE_DIR. If a difference is found, this script exits 34 | # with an error. 35 | if [[ ! -z "$TESTING" ]]; then 36 | echo "Comparing $GENERATE_DIR with $SITE_DIR..." 37 | diff -r "$GENERATE_DIR" "$SITE_DIR" 38 | fi 39 | 40 | verbose && echo "Copying $GENERATE_DIR to $SITE_DIR" 41 | cp -rf "${GENERATE_DIR}/." "$SITE_DIR" 42 | -------------------------------------------------------------------------------- /tools/build-loop: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | TRAPPING=0 4 | trap "{ echo finishing; TRAPPING=1; }" SIGINT 5 | 6 | while : 7 | do 8 | tools/build 9 | RET=$? 10 | if [ $RET -eq 0 ]; then 11 | echo "success" 12 | else 13 | echo "error: $RET" 14 | fi 15 | if [ $TRAPPING -eq 0 ]; then 16 | sleep 1 17 | else 18 | echo "done" 19 | exit 0 20 | fi 21 | done 22 | -------------------------------------------------------------------------------- /tools/format: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -eo pipefail 4 | 5 | paths=$(ls examples/*/*.go) 6 | 7 | for path in $paths; do 8 | gofmt -w=true $path 9 | done 10 | -------------------------------------------------------------------------------- /tools/generate: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | exec go run tools/generate.go $@ 4 | -------------------------------------------------------------------------------- /tools/measure: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | exec go run tools/measure.go 4 | -------------------------------------------------------------------------------- /tools/measure.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "regexp" 8 | "strings" 9 | "unicode/utf8" 10 | ) 11 | 12 | func check(err error) { 13 | if err != nil { 14 | panic(err) 15 | } 16 | } 17 | 18 | func readLines(path string) []string { 19 | srcBytes, err := os.ReadFile(path) 20 | check(err) 21 | return strings.Split(string(srcBytes), "\n") 22 | } 23 | 24 | func isDir(path string) bool { 25 | fileStat, _ := os.Stat(path) 26 | return fileStat.IsDir() 27 | } 28 | 29 | var commentPat = regexp.MustCompile("\\s*\\/\\/") 30 | 31 | func main() { 32 | sourcePaths, err := filepath.Glob("./examples/*/*") 33 | check(err) 34 | foundLongFile := false 35 | for _, sourcePath := range sourcePaths { 36 | foundLongLine := false 37 | if !isDir(sourcePath) { 38 | lines := readLines(sourcePath) 39 | for i, line := range lines { 40 | // Convert tabs to spaces before measuring, so we get an accurate measure 41 | // of how long the output will end up being. 42 | line := strings.Replace(line, "\t", " ", -1) 43 | if !foundLongLine && !commentPat.MatchString(line) && (utf8.RuneCountInString(line) > 58) { 44 | fmt.Printf("measure: %s:%d\n", sourcePath, i+1) 45 | foundLongLine = true 46 | foundLongFile = true 47 | } 48 | } 49 | } 50 | } 51 | if foundLongFile { 52 | os.Exit(1) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tools/serve: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | exec go run tools/serve.go 4 | -------------------------------------------------------------------------------- /tools/serve.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | ) 7 | 8 | func main() { 9 | port := "8000" 10 | publicDir := "public" 11 | fmt.Printf("Serving Go by Example at http://127.0.0.1:%s\n", port) 12 | http.ListenAndServe(":"+port, http.FileServer(http.Dir(publicDir))) 13 | } 14 | -------------------------------------------------------------------------------- /tools/test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Sanity testing of the examples. 4 | 5 | set -eo pipefail 6 | 7 | # go vet will attempt to build each example, making sure it compiles. It will 8 | # also report known issues with the code. Disabling the -unreachable check 9 | # because it will fire false positives for some examples demonstrating panics. 10 | go vet -unreachable=false ./examples/... 11 | -------------------------------------------------------------------------------- /tools/upload: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | exec go run tools/upload.go -region us-east-1 -bucket gobyexample.com 4 | -------------------------------------------------------------------------------- /tools/upload.go: -------------------------------------------------------------------------------- 1 | // Uploads the generated site from the public/ directory to the S3 bucket from 2 | // which it's served. 3 | // To invoke this program, the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY 4 | // env vars have to be set appropriately, and the -region and -bucket flags 5 | // have to be passed in. 6 | package main 7 | 8 | import ( 9 | "context" 10 | "flag" 11 | "log" 12 | "os" 13 | "path/filepath" 14 | 15 | "github.com/aws/aws-sdk-go-v2/aws" 16 | "github.com/aws/aws-sdk-go-v2/config" 17 | "github.com/aws/aws-sdk-go-v2/service/s3" 18 | ) 19 | 20 | // guessContentType guesses the HTTP content type appropriate for the given 21 | // filename. 22 | func guessContentType(filename string) string { 23 | switch filepath.Ext(filename) { 24 | case ".ico": 25 | return "image/x-icon" 26 | case ".png": 27 | return "image/png" 28 | case ".css": 29 | return "text/css" 30 | default: 31 | return "text/html" 32 | } 33 | } 34 | 35 | func main() { 36 | region := flag.String("region", "", "S3 region") 37 | bucket := flag.String("bucket", "", "S3 bucket name") 38 | flag.Parse() 39 | 40 | if len(*region) == 0 || len(*bucket) == 0 { 41 | log.Fatalf("region and bucket must be specified [region=%s, bucket=%s]", *region, *bucket) 42 | } 43 | 44 | cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion(*region)) 45 | if err != nil { 46 | log.Fatal(err) 47 | } 48 | 49 | client := s3.NewFromConfig(cfg) 50 | 51 | // The whole contents of the public/ directory are uploaded. This code assumes 52 | // the directory structure is flat - there are no subdirectories. 53 | publicDir := "./public/" 54 | c, err := os.ReadDir(publicDir) 55 | if err != nil { 56 | log.Fatal(err) 57 | } 58 | 59 | for _, entry := range c { 60 | if !entry.IsDir() { 61 | file, err := os.Open(filepath.Join(publicDir, entry.Name())) 62 | if err != nil { 63 | log.Fatal(err) 64 | } 65 | defer file.Close() 66 | 67 | contentType := guessContentType(entry.Name()) 68 | log.Printf("Uploading %s (%s)", entry.Name(), contentType) 69 | 70 | cfg := &s3.PutObjectInput{ 71 | Bucket: bucket, 72 | Key: aws.String(entry.Name()), 73 | Body: file, 74 | ContentType: aws.String(contentType), 75 | } 76 | 77 | _, err = client.PutObject(context.TODO(), cfg) 78 | if err != nil { 79 | log.Fatal(err) 80 | } 81 | } 82 | } 83 | } 84 | --------------------------------------------------------------------------------