From 63383ac8bc4f9383fece1329cd986544e6ef04b4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 13 Mar 2024 16:39:26 +0000 Subject: [PATCH] Deploy to GitHub pages --- 404.html | 20 + CHANGELOG.html | 50 + assets/css/0.styles.93ee3edc.css | 1 + assets/img/a.732405d8.png | Bin 0 -> 66181 bytes assets/img/gofunc.32e2f0aa.svg | 4 + assets/img/hash.b54950a0.svg | 149 +++ assets/img/ringwaiter.c4e44723.png | Bin 0 -> 38556 bytes assets/img/search.83621669.svg | 1 + assets/img/wechat.4a3030a4.png | Bin 0 -> 41224 bytes assets/js/10.3785ea8d.js | 1 + assets/js/100.6f49f3c0.js | 1 + assets/js/101.4512c8a2.js | 1 + assets/js/102.0befb22b.js | 1 + assets/js/103.d78ec0dd.js | 1 + assets/js/104.49e17432.js | 1 + assets/js/105.27135b71.js | 1 + assets/js/106.450795c9.js | 1 + assets/js/107.68b61ef6.js | 1 + assets/js/108.1b1c79b1.js | 1 + assets/js/109.a7608d9f.js | 1 + assets/js/11.25dd6abf.js | 1 + assets/js/110.638aa560.js | 1 + assets/js/111.800f7506.js | 1 + assets/js/112.d3ef22bf.js | 1 + assets/js/113.befe1449.js | 1 + assets/js/114.c126b120.js | 1 + assets/js/115.773ca7ce.js | 1 + assets/js/12.521ef871.js | 1 + assets/js/13.6a579e8b.js | 1 + assets/js/14.46f64113.js | 1 + assets/js/15.ccbbdb7a.js | 1 + assets/js/16.2a7384f8.js | 1 + assets/js/17.50442aef.js | 1 + assets/js/18.4b8a1599.js | 1 + assets/js/19.d153f9b9.js | 1 + assets/js/2.5c331e53.js | 1 + assets/js/20.fea9a837.js | 1 + assets/js/21.6c80f263.js | 1 + assets/js/22.821a2e8d.js | 1 + assets/js/23.6633ca8a.js | 1 + assets/js/24.5a95c868.js | 1 + assets/js/25.c1fe94a5.js | 1 + assets/js/26.529d156a.js | 1 + assets/js/27.1c78dbce.js | 1 + assets/js/28.8c065b05.js | 1 + assets/js/29.b8584add.js | 1 + assets/js/3.159fe1e3.js | 1 + assets/js/30.40e92043.js | 1 + assets/js/31.89f31e41.js | 1 + assets/js/32.a03da74e.js | 1 + assets/js/33.39bfe4d4.js | 1 + assets/js/34.7610889f.js | 1 + assets/js/35.32a9ec30.js | 1 + assets/js/36.8d562ec5.js | 1 + assets/js/37.1fac0e0a.js | 1 + assets/js/38.53670642.js | 1 + assets/js/39.9c8315fb.js | 1 + assets/js/4.a73c4d70.js | 1 + assets/js/40.c47687be.js | 1 + assets/js/41.0dc8c7ec.js | 1 + assets/js/42.d4dc564f.js | 1 + assets/js/43.ceafd4d8.js | 1 + assets/js/44.5dc33e34.js | 1 + assets/js/45.59f733ac.js | 1 + assets/js/46.ed939de4.js | 1 + assets/js/47.6dd9b6a9.js | 1 + assets/js/48.e525f69a.js | 1 + assets/js/49.75d23cdb.js | 1 + assets/js/5.864fc44c.js | 1 + assets/js/50.ac1acdef.js | 1 + assets/js/51.df597de1.js | 1 + assets/js/52.9dfffd13.js | 1 + assets/js/53.c98436b4.js | 1 + assets/js/54.3fb40b8b.js | 1 + assets/js/55.7d9bf41e.js | 1 + assets/js/56.4127148d.js | 1 + assets/js/57.ba1b4c58.js | 1 + assets/js/58.d6c32d73.js | 1 + assets/js/59.a733d97b.js | 1 + assets/js/6.39778f4f.js | 1 + assets/js/60.9d927a66.js | 1 + assets/js/61.155a3987.js | 1 + assets/js/62.3ecc77bf.js | 1 + assets/js/63.87a8106f.js | 1 + assets/js/64.3240a531.js | 1 + assets/js/65.c6f6ac65.js | 1 + assets/js/66.949483d3.js | 1 + assets/js/67.613f77e3.js | 1 + assets/js/68.42cf41b2.js | 1 + assets/js/69.a1051de8.js | 1 + assets/js/7.34b0a1c7.js | 1 + assets/js/70.ae50c48a.js | 1 + assets/js/71.7e2f9033.js | 1 + assets/js/72.4cc3186e.js | 1 + assets/js/73.7fe20629.js | 1 + assets/js/74.68c618b1.js | 1 + assets/js/75.d44fc2e7.js | 1 + assets/js/76.f53bf284.js | 1 + assets/js/77.bad2588a.js | 1 + assets/js/78.2ee31447.js | 1 + assets/js/79.f5c3f20d.js | 1 + assets/js/8.706832d3.js | 1 + assets/js/80.5aa44af4.js | 1 + assets/js/81.1584ddc2.js | 1 + assets/js/82.4e698f70.js | 1 + assets/js/83.5b3104c6.js | 1 + assets/js/84.fd4850fb.js | 1 + assets/js/85.17171820.js | 1 + assets/js/86.411754fa.js | 1 + assets/js/87.c1433965.js | 1 + assets/js/88.0ee83428.js | 1 + assets/js/89.32dbf192.js | 1 + assets/js/9.0852bf61.js | 1 + assets/js/90.86c77607.js | 1 + assets/js/91.bc281b0a.js | 1 + assets/js/92.52bff9af.js | 1 + assets/js/93.a157b4e1.js | 1 + assets/js/94.bd1a1b8b.js | 1 + assets/js/95.471eafef.js | 1 + assets/js/96.216ae8b8.js | 1 + assets/js/97.46e63812.js | 1 + assets/js/98.e08b5049.js | 1 + assets/js/99.ad21ff40.js | 1 + assets/js/app.c50a697f.js | 17 + css/index.css | 3 + favicon.ico | Bin 0 -> 277379 bytes index.html | 52 + runtime/gmp/index.html | 50 + runtime/index.html | 50 + runtime/netpool/index.html | 50 + .../index.html" | 50 + .../index.html" | 50 + .../index.html" | 50 + .../index.html" | 50 + .../index.html" | 50 + .../helloWorld/index.html" | 64 + "\345\237\272\347\241\200/index.html" | 50 + .../interface/index.html" | 714 ++++++++++ "\345\237\272\347\241\200/map/index.html" | 149 +++ "\345\237\272\347\241\200/slice/index.html" | 298 +++++ "\345\237\272\347\241\200/string/index.html" | 256 ++++ .../index.html" | 145 +++ .../index.html" | 155 +++ .../0.html" | 112 ++ .../1.html" | 92 ++ .../2.html" | 50 + .../3.html" | 98 ++ .../4.html" | 120 ++ .../5.html" | 84 ++ .../6.html" | 50 + .../7.html" | 176 +++ .../index.html" | 1050 +++++++++++++++ .../index.html" | 145 +++ .../index.html" | 94 ++ .../index.html" | 146 +++ .../index.html" | 119 ++ .../index.html" | 74 ++ .../\346\263\233\345\236\213/index.html" | 453 +++++++ .../index.html" | 319 +++++ .../index.html" | 199 +++ .../index.html" | 562 ++++++++ .../\351\233\266\345\200\274/index.html" | 117 ++ "\345\267\245\347\250\213/cgo/index.html" | 50 + "\345\267\245\347\250\213/copilot/index.html" | 50 + .../goweb\347\274\226\347\250\213/index.html" | 50 + .../index.html" | 121 ++ .../encoding-hex.html" | 50 + .../fmt.html" | 50 + .../index.html" | 158 +++ .../sync.html" | 64 + .../index.html" | 50 + .../k8s_visitor/index.html" | 50 + .../pipeline/index.html" | 50 + .../\344\273\243\347\220\206/index.html" | 50 + .../index.html" | 50 + .../index.html" | 50 + .../index.html" | 50 + .../\345\215\225\344\276\213/index.html" | 50 + .../\345\216\237\345\236\213/index.html" | 50 + .../index.html" | 50 + .../index.html" | 50 + .../index.html" | 50 + .../index.html" | 50 + .../\345\271\266\350\241\214/index.html" | 50 + .../\346\211\207\345\205\245/index.html" | 50 + .../\346\211\207\345\207\272/index.html" | 50 + .../index.html" | 50 + .../index.html" | 50 + .../index.html" | 50 + .../\347\255\226\347\225\245/index.html" | 50 + .../index.html" | 50 + .../index.html" | 50 + .../index.html" | 50 + .../architect.html" | 50 + .../index.html" | 50 + .../intermediate.html" | 50 + .../primary.html" | 50 + .../senior.html" | 50 + ...\350\257\225\351\242\230\344\270\200.html" | 50 + "\345\267\245\347\250\213/index.html" | 50 + "\345\267\245\347\250\213/ioc/index.html" | 50 + "\345\267\245\347\250\213/log/index.html" | 50 + "\345\267\245\347\250\213/wasm/index.html" | 50 + .../index.html" | 133 ++ .../gin/index.html" | 50 + .../index.html" | 50 + .../sonic/index.html" | 50 + .../index.html" | 139 ++ .../index.html" | 50 + .../index.html" | 275 ++++ .../\345\217\215\345\260\204/index.html" | 50 + .../\345\221\275\344\273\244/index.html" | 61 + .../\345\235\221/index.html" | 50 + .../index.html" | 50 + .../\346\265\213\350\257\225/index.html" | 50 + ...\345\207\206\346\265\213\350\257\225.html" | 50 + ...\347\241\200\346\265\213\350\257\225.html" | 50 + ...\347\263\212\346\265\213\350\257\225.html" | 50 + .../index.html" | 50 + .../index.html" | 50 + .../index.html" | 50 + "\345\271\266\345\217\221/atomic/index.html" | 61 + "\345\271\266\345\217\221/channel/index.html" | 1158 +++++++++++++++++ "\345\271\266\345\217\221/context/index.html" | 208 +++ "\345\271\266\345\217\221/index.html" | 50 + .../index.html" | 201 +++ .../index.html" | 791 +++++++++++ .../index.html" | 263 ++++ .../index.html" | 165 +++ .../gc/index.html" | 201 +++ .../gccgo/index.html" | 53 + .../index.html" | 65 + .../llvm/index.html" | 54 + .../index.html" | 50 + 234 files changed, 13573 insertions(+) create mode 100644 404.html create mode 100644 CHANGELOG.html create mode 100644 assets/css/0.styles.93ee3edc.css create mode 100644 assets/img/a.732405d8.png create mode 100644 assets/img/gofunc.32e2f0aa.svg create mode 100644 assets/img/hash.b54950a0.svg create mode 100644 assets/img/ringwaiter.c4e44723.png create mode 100644 assets/img/search.83621669.svg create mode 100644 assets/img/wechat.4a3030a4.png create mode 100644 assets/js/10.3785ea8d.js create mode 100644 assets/js/100.6f49f3c0.js create mode 100644 assets/js/101.4512c8a2.js create mode 100644 assets/js/102.0befb22b.js create mode 100644 assets/js/103.d78ec0dd.js create mode 100644 assets/js/104.49e17432.js create mode 100644 assets/js/105.27135b71.js create mode 100644 assets/js/106.450795c9.js create mode 100644 assets/js/107.68b61ef6.js create mode 100644 assets/js/108.1b1c79b1.js create mode 100644 assets/js/109.a7608d9f.js create mode 100644 assets/js/11.25dd6abf.js create mode 100644 assets/js/110.638aa560.js create mode 100644 assets/js/111.800f7506.js create mode 100644 assets/js/112.d3ef22bf.js create mode 100644 assets/js/113.befe1449.js create mode 100644 assets/js/114.c126b120.js create mode 100644 assets/js/115.773ca7ce.js create mode 100644 assets/js/12.521ef871.js create mode 100644 assets/js/13.6a579e8b.js create mode 100644 assets/js/14.46f64113.js create mode 100644 assets/js/15.ccbbdb7a.js create mode 100644 assets/js/16.2a7384f8.js create mode 100644 assets/js/17.50442aef.js create mode 100644 assets/js/18.4b8a1599.js create mode 100644 assets/js/19.d153f9b9.js create mode 100644 assets/js/2.5c331e53.js create mode 100644 assets/js/20.fea9a837.js create mode 100644 assets/js/21.6c80f263.js create mode 100644 assets/js/22.821a2e8d.js create mode 100644 assets/js/23.6633ca8a.js create mode 100644 assets/js/24.5a95c868.js create mode 100644 assets/js/25.c1fe94a5.js create mode 100644 assets/js/26.529d156a.js create mode 100644 assets/js/27.1c78dbce.js create mode 100644 assets/js/28.8c065b05.js create mode 100644 assets/js/29.b8584add.js create mode 100644 assets/js/3.159fe1e3.js create mode 100644 assets/js/30.40e92043.js create mode 100644 assets/js/31.89f31e41.js create mode 100644 assets/js/32.a03da74e.js create mode 100644 assets/js/33.39bfe4d4.js create mode 100644 assets/js/34.7610889f.js create mode 100644 assets/js/35.32a9ec30.js create mode 100644 assets/js/36.8d562ec5.js create mode 100644 assets/js/37.1fac0e0a.js create mode 100644 assets/js/38.53670642.js create mode 100644 assets/js/39.9c8315fb.js create mode 100644 assets/js/4.a73c4d70.js create mode 100644 assets/js/40.c47687be.js create mode 100644 assets/js/41.0dc8c7ec.js create mode 100644 assets/js/42.d4dc564f.js create mode 100644 assets/js/43.ceafd4d8.js create mode 100644 assets/js/44.5dc33e34.js create mode 100644 assets/js/45.59f733ac.js create mode 100644 assets/js/46.ed939de4.js create mode 100644 assets/js/47.6dd9b6a9.js create mode 100644 assets/js/48.e525f69a.js create mode 100644 assets/js/49.75d23cdb.js create mode 100644 assets/js/5.864fc44c.js create mode 100644 assets/js/50.ac1acdef.js create mode 100644 assets/js/51.df597de1.js create mode 100644 assets/js/52.9dfffd13.js create mode 100644 assets/js/53.c98436b4.js create mode 100644 assets/js/54.3fb40b8b.js create mode 100644 assets/js/55.7d9bf41e.js create mode 100644 assets/js/56.4127148d.js create mode 100644 assets/js/57.ba1b4c58.js create mode 100644 assets/js/58.d6c32d73.js create mode 100644 assets/js/59.a733d97b.js create mode 100644 assets/js/6.39778f4f.js create mode 100644 assets/js/60.9d927a66.js create mode 100644 assets/js/61.155a3987.js create mode 100644 assets/js/62.3ecc77bf.js create mode 100644 assets/js/63.87a8106f.js create mode 100644 assets/js/64.3240a531.js create mode 100644 assets/js/65.c6f6ac65.js create mode 100644 assets/js/66.949483d3.js create mode 100644 assets/js/67.613f77e3.js create mode 100644 assets/js/68.42cf41b2.js create mode 100644 assets/js/69.a1051de8.js create mode 100644 assets/js/7.34b0a1c7.js create mode 100644 assets/js/70.ae50c48a.js create mode 100644 assets/js/71.7e2f9033.js create mode 100644 assets/js/72.4cc3186e.js create mode 100644 assets/js/73.7fe20629.js create mode 100644 assets/js/74.68c618b1.js create mode 100644 assets/js/75.d44fc2e7.js create mode 100644 assets/js/76.f53bf284.js create mode 100644 assets/js/77.bad2588a.js create mode 100644 assets/js/78.2ee31447.js create mode 100644 assets/js/79.f5c3f20d.js create mode 100644 assets/js/8.706832d3.js create mode 100644 assets/js/80.5aa44af4.js create mode 100644 assets/js/81.1584ddc2.js create mode 100644 assets/js/82.4e698f70.js create mode 100644 assets/js/83.5b3104c6.js create mode 100644 assets/js/84.fd4850fb.js create mode 100644 assets/js/85.17171820.js create mode 100644 assets/js/86.411754fa.js create mode 100644 assets/js/87.c1433965.js create mode 100644 assets/js/88.0ee83428.js create mode 100644 assets/js/89.32dbf192.js create mode 100644 assets/js/9.0852bf61.js create mode 100644 assets/js/90.86c77607.js create mode 100644 assets/js/91.bc281b0a.js create mode 100644 assets/js/92.52bff9af.js create mode 100644 assets/js/93.a157b4e1.js create mode 100644 assets/js/94.bd1a1b8b.js create mode 100644 assets/js/95.471eafef.js create mode 100644 assets/js/96.216ae8b8.js create mode 100644 assets/js/97.46e63812.js create mode 100644 assets/js/98.e08b5049.js create mode 100644 assets/js/99.ad21ff40.js create mode 100644 assets/js/app.c50a697f.js create mode 100644 css/index.css create mode 100644 favicon.ico create mode 100644 index.html create mode 100644 runtime/gmp/index.html create mode 100644 runtime/index.html create mode 100644 runtime/netpool/index.html create mode 100644 "runtime/\344\270\211\350\211\262gc\347\256\227\346\263\225/index.html" create mode 100644 "runtime/\345\240\206\345\206\205\345\255\230\345\210\206\351\205\215/index.html" create mode 100644 "runtime/\345\256\232\346\227\266\345\231\250/index.html" create mode 100644 "runtime/\346\240\210\345\206\205\345\255\230\347\256\241\347\220\206/index.html" create mode 100644 "runtime/\347\263\273\347\273\237\347\233\221\346\216\247/index.html" create mode 100644 "\345\237\272\347\241\200/helloWorld/index.html" create mode 100644 "\345\237\272\347\241\200/index.html" create mode 100644 "\345\237\272\347\241\200/interface/index.html" create mode 100644 "\345\237\272\347\241\200/map/index.html" create mode 100644 "\345\237\272\347\241\200/slice/index.html" create mode 100644 "\345\237\272\347\241\200/string/index.html" create mode 100644 "\345\237\272\347\241\200/\344\275\234\347\224\250\345\237\237/index.html" create mode 100644 "\345\237\272\347\241\200/\345\205\266\344\273\226\345\206\205\345\256\271/index.html" create mode 100644 "\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/0.html" create mode 100644 "\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/1.html" create mode 100644 "\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/2.html" create mode 100644 "\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/3.html" create mode 100644 "\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/4.html" create mode 100644 "\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/5.html" create mode 100644 "\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/6.html" create mode 100644 "\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/7.html" create mode 100644 "\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/index.html" create mode 100644 "\345\237\272\347\241\200/\345\217\230\351\207\217\345\243\260\346\230\216/index.html" create mode 100644 "\345\237\272\347\241\200/\345\244\215\345\220\210\345\255\227\351\235\242\351\207\217/index.html" create mode 100644 "\345\237\272\347\241\200/\345\270\270\351\207\217\345\243\260\346\230\216/index.html" create mode 100644 "\345\237\272\347\241\200/\346\225\260\345\255\227\347\261\273\345\236\213/index.html" create mode 100644 "\345\237\272\347\241\200/\346\261\202\345\200\274\351\241\272\345\272\217/index.html" create mode 100644 "\345\237\272\347\241\200/\346\263\233\345\236\213/index.html" create mode 100644 "\345\237\272\347\241\200/\347\273\223\346\236\204\344\275\223/index.html" create mode 100644 "\345\237\272\347\241\200/\351\200\273\350\276\221\345\222\214\345\210\244\346\226\255\350\257\255\345\217\245/index.html" create mode 100644 "\345\237\272\347\241\200/\351\224\231\350\257\257\345\244\204\347\220\206/index.html" create mode 100644 "\345\237\272\347\241\200/\351\233\266\345\200\274/index.html" create mode 100644 "\345\267\245\347\250\213/cgo/index.html" create mode 100644 "\345\267\245\347\250\213/copilot/index.html" create mode 100644 "\345\267\245\347\250\213/goweb\347\274\226\347\250\213/index.html" create mode 100644 "\345\267\245\347\250\213/go\345\221\275\345\220\215\346\203\257\344\276\213/index.html" create mode 100644 "\345\267\245\347\250\213/go\346\240\207\345\207\206\345\272\223/encoding-hex.html" create mode 100644 "\345\267\245\347\250\213/go\346\240\207\345\207\206\345\272\223/fmt.html" create mode 100644 "\345\267\245\347\250\213/go\346\240\207\345\207\206\345\272\223/index.html" create mode 100644 "\345\267\245\347\250\213/go\346\240\207\345\207\206\345\272\223/sync.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/index.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/k8s_visitor/index.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/pipeline/index.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\344\273\243\347\220\206/index.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\344\277\241\345\217\267\351\207\217/index.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\210\233\345\273\272\350\200\205/index.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\212\237\350\203\275\351\200\211\351\241\271/index.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\215\225\344\276\213/index.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\216\237\345\236\213/index.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\217\221\345\270\203\350\200\205\350\256\242\351\230\205\350\200\205/index.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\256\232\346\227\266\345\207\275\346\225\260/index.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\257\271\350\261\241\346\261\240/index.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\267\245\345\216\202\346\226\271\346\263\225/index.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\271\266\350\241\214/index.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\346\211\207\345\205\245/index.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\346\211\207\345\207\272/index.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\346\226\255\350\267\257\345\231\250/index.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\346\234\211\347\225\214\345\271\266\350\241\214/index.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\347\224\237\346\210\220\345\231\250/index.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\347\255\226\347\225\245/index.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\350\243\205\351\245\260\345\231\250/index.html" create mode 100644 "\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\350\247\202\345\257\237\350\200\205/index.html" create mode 100644 "\345\267\245\347\250\213/go\350\257\255\350\250\200\350\247\204\350\214\203/index.html" create mode 100644 "\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/architect.html" create mode 100644 "\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/index.html" create mode 100644 "\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/intermediate.html" create mode 100644 "\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/primary.html" create mode 100644 "\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/senior.html" create mode 100644 "\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/\344\270\255\351\253\230\347\272\247go\351\235\242\350\257\225\351\242\230\344\270\200.html" create mode 100644 "\345\267\245\347\250\213/index.html" create mode 100644 "\345\267\245\347\250\213/ioc/index.html" create mode 100644 "\345\267\245\347\250\213/log/index.html" create mode 100644 "\345\267\245\347\250\213/wasm/index.html" create mode 100644 "\345\267\245\347\250\213/\344\273\243\347\240\201\346\243\200\346\237\245/index.html" create mode 100644 "\345\267\245\347\250\213/\344\274\230\347\247\200\347\254\254\344\270\211\346\226\271\345\214\205/gin/index.html" create mode 100644 "\345\267\245\347\250\213/\344\274\230\347\247\200\347\254\254\344\270\211\346\226\271\345\214\205/index.html" create mode 100644 "\345\267\245\347\250\213/\344\274\230\347\247\200\347\254\254\344\270\211\346\226\271\345\214\205/sonic/index.html" create mode 100644 "\345\267\245\347\250\213/\345\206\205\345\255\230\345\257\271\351\275\220\345\256\236\350\267\265/index.html" create mode 100644 "\345\267\245\347\250\213/\345\212\250\346\200\201\350\260\203\350\257\225/index.html" create mode 100644 "\345\267\245\347\250\213/\345\214\205\345\217\212\345\205\266\346\236\204\345\273\272\345\267\245\345\205\267/index.html" create mode 100644 "\345\267\245\347\250\213/\345\217\215\345\260\204/index.html" create mode 100644 "\345\267\245\347\250\213/\345\221\275\344\273\244/index.html" create mode 100644 "\345\267\245\347\250\213/\345\235\221/index.html" create mode 100644 "\345\267\245\347\250\213/\345\255\227\347\254\246\347\274\226\347\240\201/index.html" create mode 100644 "\345\267\245\347\250\213/\346\265\213\350\257\225/index.html" create mode 100644 "\345\267\245\347\250\213/\346\265\213\350\257\225/\345\237\272\345\207\206\346\265\213\350\257\225.html" create mode 100644 "\345\267\245\347\250\213/\346\265\213\350\257\225/\345\237\272\347\241\200\346\265\213\350\257\225.html" create mode 100644 "\345\267\245\347\250\213/\346\265\213\350\257\225/\346\250\241\347\263\212\346\265\213\350\257\225.html" create mode 100644 "\345\267\245\347\250\213/\347\263\273\347\273\237\344\277\241\345\217\267\347\232\204\345\244\204\347\220\206/index.html" create mode 100644 "\345\267\245\347\250\213/\350\257\273\345\206\231\346\250\241\345\236\213/index.html" create mode 100644 "\345\267\245\347\250\213/\351\241\271\347\233\256\347\273\204\347\273\207\345\275\242\345\274\217/index.html" create mode 100644 "\345\271\266\345\217\221/atomic/index.html" create mode 100644 "\345\271\266\345\217\221/channel/index.html" create mode 100644 "\345\271\266\345\217\221/context/index.html" create mode 100644 "\345\271\266\345\217\221/index.html" create mode 100644 "\345\271\266\345\217\221/\345\206\205\345\255\230\346\250\241\345\236\213/index.html" create mode 100644 "\345\271\266\345\217\221/\345\220\214\346\255\245\345\216\237\350\257\255/index.html" create mode 100644 "\345\271\266\345\217\221/\345\271\266\345\217\221\344\274\230\345\214\226/index.html" create mode 100644 "\345\271\266\345\217\221/\345\271\266\345\217\221\346\250\241\345\236\213/index.html" create mode 100644 "\347\274\226\350\257\221\345\231\250/gc/index.html" create mode 100644 "\347\274\226\350\257\221\345\231\250/gccgo/index.html" create mode 100644 "\347\274\226\350\257\221\345\231\250/index.html" create mode 100644 "\347\274\226\350\257\221\345\231\250/llvm/index.html" create mode 100644 "\347\274\226\350\257\221\345\231\250/\345\256\236\347\216\260\344\270\200\344\270\252\347\274\226\347\250\213\350\257\255\350\250\200/index.html" diff --git a/404.html b/404.html new file mode 100644 index 000000000..d360c16fe --- /dev/null +++ b/404.html @@ -0,0 +1,20 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

404

Looks like we've got some broken links.
+ Take me home. +
+ + + diff --git a/CHANGELOG.html b/CHANGELOG.html new file mode 100644 index 000000000..eaf6459f4 --- /dev/null +++ b/CHANGELOG.html @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

v0.3.0 (2022-03-02)

Features

引入 ci

+ + + diff --git a/assets/css/0.styles.93ee3edc.css b/assets/css/0.styles.93ee3edc.css new file mode 100644 index 000000000..be8cbe390 --- /dev/null +++ b/assets/css/0.styles.93ee3edc.css @@ -0,0 +1 @@ +code[class*=language-],pre[class*=language-]{color:#ccc;background:none;font-family:Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.block-comment,.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#999}.token.punctuation{color:#ccc}.token.attr-name,.token.deleted,.token.namespace,.token.tag{color:#e2777a}.token.function-name{color:#6196cc}.token.boolean,.token.function,.token.number{color:#f08d49}.token.class-name,.token.constant,.token.property,.token.symbol{color:#f8c555}.token.atrule,.token.builtin,.token.important,.token.keyword,.token.selector{color:#cc99cd}.token.attr-value,.token.char,.token.regex,.token.string,.token.variable{color:#7ec699}.token.entity,.token.operator,.token.url{color:#67cdcc}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:green}.theme-default-content code{color:#476582;padding:.25rem .5rem;margin:0;font-size:.85em;background-color:rgba(27,31,35,.05);border-radius:3px}.theme-default-content code .token.deleted{color:#ec5975}.theme-default-content code .token.inserted{color:#3eaf7c}.theme-default-content pre,.theme-default-content pre[class*=language-]{line-height:1.4;padding:1.25rem 1.5rem;margin:.85rem 0;background-color:#282c34;border-radius:6px;overflow:auto}.theme-default-content pre[class*=language-] code,.theme-default-content pre code{color:#fff;padding:0;background-color:transparent;border-radius:0}div[class*=language-]{position:relative;background-color:#282c34;border-radius:6px}div[class*=language-] .highlight-lines{-webkit-user-select:none;user-select:none;padding-top:1.3rem;position:absolute;top:0;left:0;width:100%;line-height:1.4}div[class*=language-] .highlight-lines .highlighted{background-color:rgba(0,0,0,.66)}div[class*=language-] pre,div[class*=language-] pre[class*=language-]{background:transparent;position:relative;z-index:1}div[class*=language-]:before{position:absolute;z-index:3;top:.8em;right:1em;font-size:.75rem;color:hsla(0,0%,100%,.4)}div[class*=language-]:not(.line-numbers-mode) .line-numbers-wrapper{display:none}div[class*=language-].line-numbers-mode .highlight-lines .highlighted{position:relative}div[class*=language-].line-numbers-mode .highlight-lines .highlighted:before{content:" ";position:absolute;z-index:3;left:0;top:0;display:block;width:3.5rem;height:100%;background-color:rgba(0,0,0,.66)}div[class*=language-].line-numbers-mode pre{padding-left:4.5rem;vertical-align:middle}div[class*=language-].line-numbers-mode .line-numbers-wrapper{position:absolute;top:0;width:3.5rem;text-align:center;color:hsla(0,0%,100%,.3);padding:1.25rem 0;line-height:1.4}div[class*=language-].line-numbers-mode .line-numbers-wrapper br{-webkit-user-select:none;user-select:none}div[class*=language-].line-numbers-mode .line-numbers-wrapper .line-number{position:relative;z-index:4;-webkit-user-select:none;user-select:none;font-size:.85em}div[class*=language-].line-numbers-mode:after{content:"";position:absolute;z-index:2;top:0;left:0;width:3.5rem;height:100%;border-radius:6px 0 0 6px;border-right:1px solid rgba(0,0,0,.66);background-color:#282c34}div[class~=language-js]:before{content:"js"}div[class~=language-ts]:before{content:"ts"}div[class~=language-html]:before{content:"html"}div[class~=language-md]:before{content:"md"}div[class~=language-vue]:before{content:"vue"}div[class~=language-css]:before{content:"css"}div[class~=language-sass]:before{content:"sass"}div[class~=language-scss]:before{content:"scss"}div[class~=language-less]:before{content:"less"}div[class~=language-stylus]:before{content:"stylus"}div[class~=language-go]:before{content:"go"}div[class~=language-java]:before{content:"java"}div[class~=language-c]:before{content:"c"}div[class~=language-sh]:before{content:"sh"}div[class~=language-yaml]:before{content:"yaml"}div[class~=language-py]:before{content:"py"}div[class~=language-docker]:before{content:"docker"}div[class~=language-dockerfile]:before{content:"dockerfile"}div[class~=language-makefile]:before{content:"makefile"}div[class~=language-javascript]:before{content:"js"}div[class~=language-typescript]:before{content:"ts"}div[class~=language-markup]:before{content:"html"}div[class~=language-markdown]:before{content:"md"}div[class~=language-json]:before{content:"json"}div[class~=language-ruby]:before{content:"rb"}div[class~=language-python]:before{content:"py"}div[class~=language-bash]:before{content:"sh"}div[class~=language-php]:before{content:"php"}.custom-block .custom-block-title{font-weight:600;margin-bottom:-.4rem}.custom-block.danger,.custom-block.tip,.custom-block.warning{padding:.1rem 1.5rem;border-left-width:.5rem;border-left-style:solid;margin:1rem 0}.custom-block.tip{background-color:#f3f5f7;border-color:#42b983}.custom-block.warning{background-color:rgba(255,229,100,.3);border-color:#e7c000;color:#6b5900}.custom-block.warning .custom-block-title{color:#b29400}.custom-block.warning a{color:#2c3e50}.custom-block.danger{background-color:#ffe6e6;border-color:#c00;color:#4d0000}.custom-block.danger .custom-block-title{color:#900}.custom-block.danger a{color:#2c3e50}.custom-block.details{display:block;position:relative;border-radius:2px;margin:1.6em 0;padding:1.6em;background-color:#eee}.custom-block.details h4{margin-top:0}.custom-block.details figure:last-child,.custom-block.details p:last-child{margin-bottom:0;padding-bottom:0}.custom-block.details summary{outline:none;cursor:pointer}.arrow{display:inline-block;width:0;height:0}.arrow.up{border-bottom:6px solid #ccc}.arrow.down,.arrow.up{border-left:4px solid transparent;border-right:4px solid transparent}.arrow.down{border-top:6px solid #ccc}.arrow.right{border-left:6px solid #ccc}.arrow.left,.arrow.right{border-top:4px solid transparent;border-bottom:4px solid transparent}.arrow.left{border-right:6px solid #ccc}.theme-default-content:not(.custom){max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.theme-default-content:not(.custom){padding:2rem}}@media (max-width:419px){.theme-default-content:not(.custom){padding:1.5rem}}.table-of-contents .badge{vertical-align:middle}body,html{padding:0;margin:0;background-color:#fff}body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:16px;color:#2c3e50}.page{padding-left:20rem}.navbar{z-index:20;right:0;height:3.6rem;background-color:#fff;box-sizing:border-box;border-bottom:1px solid #eaecef}.navbar,.sidebar-mask{position:fixed;top:0;left:0}.sidebar-mask{z-index:9;width:100vw;height:100vh;display:none}.sidebar{font-size:16px;background-color:#fff;width:20rem;position:fixed;z-index:10;margin:0;top:3.6rem;left:0;bottom:0;box-sizing:border-box;border-right:1px solid #eaecef;overflow-y:auto}.theme-default-content:not(.custom)>:first-child{margin-top:3.6rem}.theme-default-content:not(.custom) a:hover{text-decoration:underline}.theme-default-content:not(.custom) p.demo{padding:1rem 1.5rem;border:1px solid #ddd;border-radius:4px}.theme-default-content:not(.custom) img{max-width:100%}.theme-default-content.custom{padding:0;margin:0}.theme-default-content.custom img{max-width:100%}a{font-weight:500;text-decoration:none}a,p a code{color:#3eaf7c}p a code{font-weight:400}kbd{background:#eee;border:.15rem solid #ddd;border-bottom:.25rem solid #ddd;border-radius:.15rem;padding:0 .15em}blockquote{font-size:1rem;color:#999;border-left:.2rem solid #dfe2e5;margin:1rem 0;padding:.25rem 0 .25rem 1rem}blockquote>p{margin:0}ol,ul{padding-left:1.2em}strong{font-weight:600}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.25}.theme-default-content:not(.custom)>h1,.theme-default-content:not(.custom)>h2,.theme-default-content:not(.custom)>h3,.theme-default-content:not(.custom)>h4,.theme-default-content:not(.custom)>h5,.theme-default-content:not(.custom)>h6{margin-top:-3.1rem;padding-top:4.6rem;margin-bottom:0}.theme-default-content:not(.custom)>h1:first-child,.theme-default-content:not(.custom)>h2:first-child,.theme-default-content:not(.custom)>h3:first-child,.theme-default-content:not(.custom)>h4:first-child,.theme-default-content:not(.custom)>h5:first-child,.theme-default-content:not(.custom)>h6:first-child{margin-top:-1.5rem;margin-bottom:1rem}.theme-default-content:not(.custom)>h1:first-child+.custom-block,.theme-default-content:not(.custom)>h1:first-child+p,.theme-default-content:not(.custom)>h1:first-child+pre,.theme-default-content:not(.custom)>h2:first-child+.custom-block,.theme-default-content:not(.custom)>h2:first-child+p,.theme-default-content:not(.custom)>h2:first-child+pre,.theme-default-content:not(.custom)>h3:first-child+.custom-block,.theme-default-content:not(.custom)>h3:first-child+p,.theme-default-content:not(.custom)>h3:first-child+pre,.theme-default-content:not(.custom)>h4:first-child+.custom-block,.theme-default-content:not(.custom)>h4:first-child+p,.theme-default-content:not(.custom)>h4:first-child+pre,.theme-default-content:not(.custom)>h5:first-child+.custom-block,.theme-default-content:not(.custom)>h5:first-child+p,.theme-default-content:not(.custom)>h5:first-child+pre,.theme-default-content:not(.custom)>h6:first-child+.custom-block,.theme-default-content:not(.custom)>h6:first-child+p,.theme-default-content:not(.custom)>h6:first-child+pre{margin-top:2rem}h1:focus .header-anchor,h1:hover .header-anchor,h2:focus .header-anchor,h2:hover .header-anchor,h3:focus .header-anchor,h3:hover .header-anchor,h4:focus .header-anchor,h4:hover .header-anchor,h5:focus .header-anchor,h5:hover .header-anchor,h6:focus .header-anchor,h6:hover .header-anchor{opacity:1}h1{font-size:2.2rem}h2{font-size:1.65rem;padding-bottom:.3rem;border-bottom:1px solid #eaecef}h3{font-size:1.35rem}a.header-anchor{font-size:.85em;float:left;margin-left:-.87em;padding-right:.23em;margin-top:.125em;opacity:0}a.header-anchor:focus,a.header-anchor:hover{text-decoration:none}.line-number,code,kbd{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}ol,p,ul{line-height:1.7}hr{border:0;border-top:1px solid #eaecef}table{border-collapse:collapse;margin:1rem 0;display:block;overflow-x:auto}tr{border-top:1px solid #dfe2e5}tr:nth-child(2n){background-color:#f6f8fa}td,th{border:1px solid #dfe2e5;padding:.6em 1em}.theme-container.sidebar-open .sidebar-mask{display:block}.theme-container.no-navbar .theme-default-content:not(.custom)>h1,.theme-container.no-navbar h2,.theme-container.no-navbar h3,.theme-container.no-navbar h4,.theme-container.no-navbar h5,.theme-container.no-navbar h6{margin-top:1.5rem;padding-top:0}.theme-container.no-navbar .sidebar{top:0}@media (min-width:720px){.theme-container.no-sidebar .sidebar{display:none}.theme-container.no-sidebar .page{padding-left:0}}@media (max-width:959px){.sidebar{font-size:15px;width:16.4rem}.page{padding-left:16.4rem}}@media (max-width:719px){.sidebar{top:0;padding-top:3.6rem;transform:translateX(-100%);transition:transform .2s ease}.page{padding-left:0}.theme-container.sidebar-open .sidebar{transform:translateX(0)}.theme-container.no-navbar .sidebar{padding-top:0}}@media (max-width:419px){h1{font-size:1.9rem}.theme-default-content div[class*=language-]{margin:.85rem -1.5rem;border-radius:0}}.gt-container{width:80%;margin-left:auto;margin-right:auto}html{scroll-behavior:smooth}.go-to-top[data-v-5fd4ef0c]{cursor:pointer;position:fixed;bottom:2rem;right:2.5rem;width:2rem;color:#3eaf7c;z-index:1}.go-to-top[data-v-5fd4ef0c]:hover{color:#72cda4}@media (max-width:959px){.go-to-top[data-v-5fd4ef0c]{display:none}}.fade-enter-active[data-v-5fd4ef0c],.fade-leave-active[data-v-5fd4ef0c]{transition:opacity .3s}.fade-enter[data-v-5fd4ef0c],.fade-leave-to[data-v-5fd4ef0c]{opacity:0}#nprogress{pointer-events:none}#nprogress .bar{background:#3eaf7c;position:fixed;z-index:1031;top:0;left:0;width:100%;height:2px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #3eaf7c,0 0 5px #3eaf7c;opacity:1;transform:rotate(3deg) translateY(-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1031;top:15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border-color:#3eaf7c transparent transparent #3eaf7c;border-style:solid;border-width:2px;border-radius:50%;-webkit-animation:nprogress-spinner .4s linear infinite;animation:nprogress-spinner .4s linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@-webkit-keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.icon.outbound{color:#aaa;display:inline-block;vertical-align:middle;position:relative;top:-1px}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.home{padding:3.6rem 2rem 0;max-width:960px;margin:0 auto;display:block}.home .hero{text-align:center}.home .hero img{max-width:100%;max-height:280px;display:block;margin:3rem auto 1.5rem}.home .hero h1{font-size:3rem}.home .hero .action,.home .hero .description,.home .hero h1{margin:1.8rem auto}.home .hero .description{max-width:35rem;font-size:1.6rem;line-height:1.3;color:#6a8bad}.home .hero .action-button{display:inline-block;font-size:1.2rem;color:#fff;background-color:#3eaf7c;padding:.8rem 1.6rem;border-radius:4px;transition:background-color .1s ease;box-sizing:border-box;border-bottom:1px solid #389d70}.home .hero .action-button:hover{background-color:#4abf8a}.home .features{border-top:1px solid #eaecef;padding:1.2rem 0;margin-top:2.5rem;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:stretch;justify-content:space-between}.home .feature{flex-grow:1;flex-basis:30%;max-width:30%}.home .feature h2{font-size:1.4rem;font-weight:500;border-bottom:none;padding-bottom:0;color:#3a5169}.home .feature p{color:#4e6e8e}.home .footer{padding:2.5rem;border-top:1px solid #eaecef;text-align:center;color:#4e6e8e}@media (max-width:719px){.home .features{flex-direction:column}.home .feature{max-width:100%;padding:0 2.5rem}}@media (max-width:419px){.home{padding-left:1.5rem;padding-right:1.5rem}.home .hero img{max-height:210px;margin:2rem auto 1.2rem}.home .hero h1{font-size:2rem}.home .hero .action,.home .hero .description,.home .hero h1{margin:1.2rem auto}.home .hero .description{font-size:1.2rem}.home .hero .action-button{font-size:1rem;padding:.6rem 1.2rem}.home .feature h2{font-size:1.25rem}}.search-box{display:inline-block;position:relative;margin-right:1rem}.search-box input{cursor:text;width:10rem;height:2rem;color:#4e6e8e;display:inline-block;border:1px solid #cfd4db;border-radius:2rem;font-size:.9rem;line-height:2rem;padding:0 .5rem 0 2rem;outline:none;transition:all .2s ease;background:#fff url(/GOFamily/assets/img/search.83621669.svg) .6rem .5rem no-repeat;background-size:1rem}.search-box input:focus{cursor:auto;border-color:#3eaf7c}.search-box .suggestions{background:#fff;width:20rem;position:absolute;top:2rem;border:1px solid #cfd4db;border-radius:6px;padding:.4rem;list-style-type:none}.search-box .suggestions.align-right{right:0}.search-box .suggestion{line-height:1.4;padding:.4rem .6rem;border-radius:4px;cursor:pointer}.search-box .suggestion a{white-space:normal;color:#5d82a6}.search-box .suggestion a .page-title{font-weight:600}.search-box .suggestion a .header{font-size:.9em;margin-left:.25em}.search-box .suggestion.focused{background-color:#f3f4f5}.search-box .suggestion.focused a{color:#3eaf7c}@media (max-width:959px){.search-box input{cursor:pointer;width:0;border-color:transparent;position:relative}.search-box input:focus{cursor:text;left:0;width:10rem}}@media (-ms-high-contrast:none){.search-box input{height:2rem}}@media (max-width:959px) and (min-width:719px){.search-box .suggestions{left:0}}@media (max-width:719px){.search-box{margin-right:0}.search-box input{left:1rem}.search-box .suggestions{right:0}}@media (max-width:419px){.search-box .suggestions{width:calc(100vw - 4rem)}.search-box input:focus{width:8rem}}.sidebar-button{cursor:pointer;display:none;width:1.25rem;height:1.25rem;position:absolute;padding:.6rem;top:.6rem;left:1rem}.sidebar-button .icon{display:block;width:1.25rem;height:1.25rem}@media (max-width:719px){.sidebar-button{display:block}}.dropdown-enter,.dropdown-leave-to{height:0!important}.dropdown-wrapper{cursor:pointer}.dropdown-wrapper .dropdown-title,.dropdown-wrapper .mobile-dropdown-title{display:block;font-size:.9rem;font-family:inherit;cursor:inherit;padding:inherit;line-height:1.4rem;background:transparent;border:none;font-weight:500;color:#2c3e50}.dropdown-wrapper .dropdown-title:hover,.dropdown-wrapper .mobile-dropdown-title:hover{border-color:transparent}.dropdown-wrapper .dropdown-title .arrow,.dropdown-wrapper .mobile-dropdown-title .arrow{vertical-align:middle;margin-top:-1px;margin-left:.4rem}.dropdown-wrapper .mobile-dropdown-title{display:none;font-weight:600}.dropdown-wrapper .mobile-dropdown-title font-size inherit:hover{color:#3eaf7c}.dropdown-wrapper .nav-dropdown .dropdown-item{color:inherit;line-height:1.7rem}.dropdown-wrapper .nav-dropdown .dropdown-item h4{margin:.45rem 0 0;border-top:1px solid #eee;padding:1rem 1.5rem .45rem 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper{padding:0;list-style:none}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem-wrapper .dropdown-subitem{font-size:.9em}.dropdown-wrapper .nav-dropdown .dropdown-item a{display:block;line-height:1.7rem;position:relative;border-bottom:none;font-weight:400;margin-bottom:0;padding:0 1.5rem 0 1.25rem}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active,.dropdown-wrapper .nav-dropdown .dropdown-item a:hover{color:#3eaf7c}.dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active:after{content:"";width:0;height:0;border-left:5px solid #3eaf7c;border-top:3px solid transparent;border-bottom:3px solid transparent;position:absolute;top:calc(50% - 2px);left:9px}.dropdown-wrapper .nav-dropdown .dropdown-item:first-child h4{margin-top:0;padding-top:0;border-top:0}@media (max-width:719px){.dropdown-wrapper.open .dropdown-title{margin-bottom:.5rem}.dropdown-wrapper .dropdown-title{display:none}.dropdown-wrapper .mobile-dropdown-title{display:block}.dropdown-wrapper .nav-dropdown{transition:height .1s ease-out;overflow:hidden}.dropdown-wrapper .nav-dropdown .dropdown-item h4{border-top:0;margin-top:0;padding-top:0}.dropdown-wrapper .nav-dropdown .dropdown-item>a,.dropdown-wrapper .nav-dropdown .dropdown-item h4{font-size:15px;line-height:2rem}.dropdown-wrapper .nav-dropdown .dropdown-item .dropdown-subitem{font-size:14px;padding-left:1rem}}@media (min-width:719px){.dropdown-wrapper{height:1.8rem}.dropdown-wrapper.open .nav-dropdown,.dropdown-wrapper:hover .nav-dropdown{display:block!important}.dropdown-wrapper.open:blur{display:none}.dropdown-wrapper .nav-dropdown{display:none;height:auto!important;box-sizing:border-box;max-height:calc(100vh - 2.7rem);overflow-y:auto;position:absolute;top:100%;right:0;background-color:#fff;padding:.6rem 0;border:1px solid;border-color:#ddd #ddd #ccc;text-align:left;border-radius:.25rem;white-space:nowrap;margin:0}}.nav-links{display:inline-block}.nav-links a{line-height:1.4rem;color:inherit}.nav-links a.router-link-active,.nav-links a:hover{color:#3eaf7c}.nav-links .nav-item{position:relative;display:inline-block;margin-left:1.5rem;line-height:2rem}.nav-links .nav-item:first-child{margin-left:0}.nav-links .repo-link{margin-left:1.5rem}@media (max-width:719px){.nav-links .nav-item,.nav-links .repo-link{margin-left:0}}@media (min-width:719px){.nav-links a.router-link-active,.nav-links a:hover{color:#2c3e50}.nav-item>a:not(.external).router-link-active,.nav-item>a:not(.external):hover{margin-bottom:-2px;border-bottom:2px solid #46bd87}}.navbar{padding:.7rem 1.5rem;line-height:2.2rem}.navbar a,.navbar img,.navbar span{display:inline-block}.navbar .logo{height:2.2rem;min-width:2.2rem;margin-right:.8rem;vertical-align:top}.navbar .site-name{font-size:1.3rem;font-weight:600;color:#2c3e50;position:relative}.navbar .links{padding-left:1.5rem;box-sizing:border-box;background-color:#fff;white-space:nowrap;font-size:.9rem;position:absolute;right:1.5rem;top:.7rem;display:flex}.navbar .links .search-box{flex:0 0 auto;vertical-align:top}@media (max-width:719px){.navbar{padding-left:4rem}.navbar .can-hide{display:none}.navbar .links{padding-left:1.5rem}.navbar .site-name{width:calc(100vw - 9.4rem);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}}.page-edit{max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.page-edit{padding:2rem}}@media (max-width:419px){.page-edit{padding:1.5rem}}.page-edit{padding-top:1rem;padding-bottom:1rem;overflow:auto}.page-edit .edit-link{display:inline-block}.page-edit .edit-link a{color:#4e6e8e;margin-right:.25rem}.page-edit .last-updated{float:right;font-size:.9em}.page-edit .last-updated .prefix{font-weight:500;color:#4e6e8e}.page-edit .last-updated .time{font-weight:400;color:#767676}@media (max-width:719px){.page-edit .edit-link{margin-bottom:.5rem}.page-edit .last-updated{font-size:.8em;float:none;text-align:left}}.page-nav{max-width:740px;margin:0 auto;padding:2rem 2.5rem}@media (max-width:959px){.page-nav{padding:2rem}}@media (max-width:419px){.page-nav{padding:1.5rem}}.page-nav{padding-top:1rem;padding-bottom:0}.page-nav .inner{min-height:2rem;margin-top:0;border-top:1px solid #eaecef;padding-top:1rem;overflow:auto}.page-nav .next{float:right}.page{padding-bottom:2rem;display:block}.sidebar-group .sidebar-group{padding-left:.5em}.sidebar-group:not(.collapsable) .sidebar-heading:not(.clickable){cursor:auto;color:inherit}.sidebar-group.is-sub-group{padding-left:0}.sidebar-group.is-sub-group>.sidebar-heading{font-size:.95em;line-height:1.4;font-weight:400;padding-left:2rem}.sidebar-group.is-sub-group>.sidebar-heading:not(.clickable){opacity:.5}.sidebar-group.is-sub-group>.sidebar-group-items{padding-left:1rem}.sidebar-group.is-sub-group>.sidebar-group-items>li>.sidebar-link{font-size:.95em;border-left:none}.sidebar-group.depth-2>.sidebar-heading{border-left:none}.sidebar-heading{color:#2c3e50;transition:color .15s ease;cursor:pointer;font-size:1.1em;font-weight:700;padding:.35rem 1.5rem .35rem 1.25rem;width:100%;box-sizing:border-box;margin:0;border-left:.25rem solid transparent}.sidebar-heading.open,.sidebar-heading:hover{color:inherit}.sidebar-heading .arrow{position:relative;top:-.12em;left:.5em}.sidebar-heading.clickable.active{font-weight:600;color:#3eaf7c;border-left-color:#3eaf7c}.sidebar-heading.clickable:hover{color:#3eaf7c}.sidebar-group-items{transition:height .1s ease-out;font-size:.95em;overflow:hidden}.sidebar .sidebar-sub-headers{padding-left:1rem;font-size:.95em}a.sidebar-link{font-size:1em;font-weight:400;display:inline-block;color:#2c3e50;border-left:.25rem solid transparent;padding:.35rem 1rem .35rem 1.25rem;line-height:1.4;width:100%;box-sizing:border-box}a.sidebar-link:hover{color:#3eaf7c}a.sidebar-link.active{font-weight:600;color:#3eaf7c;border-left-color:#3eaf7c}.sidebar-group a.sidebar-link{padding-left:2rem}.sidebar-sub-headers a.sidebar-link{padding-top:.25rem;padding-bottom:.25rem;border-left:none}.sidebar-sub-headers a.sidebar-link.active{font-weight:500}.sidebar ul{padding:0;margin:0;list-style-type:none}.sidebar a{display:inline-block}.sidebar .nav-links{display:none;border-bottom:1px solid #eaecef;padding:.5rem 0 .75rem}.sidebar .nav-links a{font-weight:600}.sidebar .nav-links .nav-item,.sidebar .nav-links .repo-link{display:block;line-height:1.25rem;font-size:1.1em;padding:.5rem 0 .5rem 1.5rem}.sidebar>.sidebar-links{padding:1.5rem 0}.sidebar>.sidebar-links>li>a.sidebar-link{font-size:1.1em;line-height:1.7;font-weight:700}.sidebar>.sidebar-links>li:not(:first-child){margin-top:.75rem}@media (max-width:719px){.sidebar .nav-links{display:block}.sidebar .nav-links .dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active:after{top:calc(1rem - 2px)}.sidebar>.sidebar-links{padding:1rem 0}}.badge[data-v-15b7b770]{display:inline-block;font-size:14px;height:18px;line-height:18px;border-radius:3px;padding:0 6px;color:#fff}.badge.green[data-v-15b7b770],.badge.tip[data-v-15b7b770],.badge[data-v-15b7b770]{background-color:#42b983}.badge.error[data-v-15b7b770]{background-color:#da5961}.badge.warn[data-v-15b7b770],.badge.warning[data-v-15b7b770],.badge.yellow[data-v-15b7b770]{background-color:#e7c000}.badge+.badge[data-v-15b7b770]{margin-left:5px}.theme-code-block[data-v-759a7d02]{display:none}.theme-code-block__active[data-v-759a7d02]{display:block}.theme-code-block>pre[data-v-759a7d02]{background-color:orange}.theme-code-group__nav[data-v-deefee04]{margin-bottom:-35px;background-color:#282c34;padding-bottom:22px;border-top-left-radius:6px;border-top-right-radius:6px;padding-left:10px;padding-top:10px}.theme-code-group__ul[data-v-deefee04]{margin:auto 0;padding-left:0;display:inline-flex;list-style:none}.theme-code-group__nav-tab[data-v-deefee04]{border:0;padding:5px;cursor:pointer;background-color:transparent;font-size:.85em;line-height:1.4;color:hsla(0,0%,100%,.9);font-weight:600}.theme-code-group__nav-tab-active[data-v-deefee04]{border-bottom:1px solid #42b983}.pre-blank[data-v-deefee04]{color:#42b983} \ No newline at end of file diff --git a/assets/img/a.732405d8.png b/assets/img/a.732405d8.png new file mode 100644 index 0000000000000000000000000000000000000000..dd56ade1607c72201543127b7321b375d70cceb4 GIT binary patch literal 66181 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 + + +
main 包
main 包
pkg 1
pkg 1
pkg 2
pkg 2
pkg 3
pkg 3
const...
const...
var...
var...
init()
init()
main()
main()
import pkg1
import pkg1
import pkg 3
import pkg 3

程序开始执行

程序开始执行
const...
const...
var...
var...
init()
init()
const...
const...
var...
var...
init()
init()
import pkg2
import pkg2
const...
const...
var...
var...
init()
init()

程序执行完毕

程序执行完毕
Text is not SVG - cannot display
\ No newline at end of file diff --git a/assets/img/hash.b54950a0.svg b/assets/img/hash.b54950a0.svg new file mode 100644 index 000000000..857539bec --- /dev/null +++ b/assets/img/hash.b54950a0.svg @@ -0,0 +1,149 @@ + + + 未命名 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + buckets[0] + + + tophash[0] + + + key[0] + + + key[1] + + + key[7] + + + value[0] + + + value[1] + + + value[7] + + + overflow + + + tophashs + + + overflow buckets + + + keys + + + values + + + overflow + + + tophashs + + + 链表 + + + keys + + + values + + + overflow + + + tophash[7] + + + tophash[1] + + + . + . + . + + + . + . + . + + + . + . + . + + + buckets[1] + + + v,ok := m[key] + + + hashcode = hash(key) + + + + + + + + + + + + + hashcode高位区 + + + hashcode的高位区域组成tophash, + 用来确定在tophash中的位置 + + hashcode的低位区域经过mask最终来 + 确定是哪一个桶 + + + hashcode低位区 + + + + \ No newline at end of file diff --git a/assets/img/ringwaiter.c4e44723.png b/assets/img/ringwaiter.c4e44723.png new file mode 100644 index 0000000000000000000000000000000000000000..23d275975afb40248677c6b2c8ee5f4713d8a8b5 GIT binary patch literal 38556 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 literal 0 HcmV?d00001 diff --git a/assets/img/search.83621669.svg b/assets/img/search.83621669.svg new file mode 100644 index 000000000..03d83913e --- /dev/null +++ b/assets/img/search.83621669.svg @@ -0,0 +1 @@ + diff --git a/assets/img/wechat.4a3030a4.png b/assets/img/wechat.4a3030a4.png new file mode 100644 index 0000000000000000000000000000000000000000..81b494bee6a243f17e187f5ed353243575c4951a GIT binary patch literal 41224 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%0AruYyeMbQY39cN>XtA&jpJ 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")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\ti"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ok "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" reflect"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Select")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("cases"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 从 cases 取数据,一旦取完,cases 会取消已经取完的 case")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("ok "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\tcases "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("cases"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" cases"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("continue")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// out 发送数据")]),t._v("\n\t\t\tout "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Interface")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" out\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("反射版本这里有个小问题,从 cases 中取出数据,往 out 中添加,这个操作只能一个 case 一个 case 的取,但是你如果观察上文的递归实现模式,你会发现它开启了很多 goroutine 所以是并发的去取数据往 out 中送。")]),t._v(" "),n("h3",{attrs:{id:"fan-out"}},[t._v("fan-out")]),t._v(" "),n("p",[t._v("fan out 跟 fan in 模式是相反的,fan out 指的是拥有一个输入源,多个输出源,实际上这是一种广播机制,读取一个数据,广播给众多接受者。")]),t._v(" "),n("p",[t._v("下面看一下代码:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("fanout")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" out "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" async "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" out "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 对一个nil的通道进行 for range 遍历会导致阻塞(block)。")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" value "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" vi "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" out "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\tvi "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" vi "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// if go version is lower then 1.22")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" async "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t\t\tvi "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" v\n\t\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t\tvi "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" v\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("实际上这段代码有 goroutine 泄露的风险,如果 out 一直不被消费,value 的数据越来越多,那么这么多等待发送信息的 goroutine 就会发生泄露问题")]),t._v(" "),n("p",[t._v("解决方案有下面几种:")]),t._v(" "),n("ul",[n("li",[t._v("使用信号量去控制最多的 goroutine 数量")]),t._v(" "),n("li",[t._v("给每一个 goroutine 设置超时时间")]),t._v(" "),n("li",[t._v("控制发送的 value channel 的发送频率")]),t._v(" "),n("li",[t._v("使用 worker pool 去限制并发数量")])]),t._v(" "),n("p",[t._v("那么让我们用代码去实现这些改进的方案:")]),t._v(" "),n("p",[t._v("方案一:使用信号量控制")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n maxWorkers "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" runtime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("GOMAXPROCS")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tsema "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" semaphore"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewWeighted")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("int64")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("maxWorkers"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 在 value传递结束之后,记得close value")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("fanout")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" out "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" async "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" out "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 对一个nil的通道进行 for range 遍历会导致阻塞(block)。")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" value "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" vi "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" out "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\tvi "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" vi "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// if go version is lower then 1.22")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" async "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n ctx "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" context"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Background")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" sema"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Acquire")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),t._v("\n\t\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" sema"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Release")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t\t\t\tvi "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" v\n\t\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t\tvi "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" v\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("方案二:使用超时时间")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("fanout")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" out "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" async "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" out "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 对一个nil的通道进行 for range 遍历会导致阻塞(block)。")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" value "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" vi "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" out "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\tvi "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" vi "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// if go version is lower then 1.22")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" async "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" vi "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("After")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t\t\t\t\t\n\t\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t\tvi "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" v\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("方案三:控制发送 value 的发送频率")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("After")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"关闭了吗"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" value "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"%"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t\t\ttime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">>")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"发送了吗"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),n("p",[t._v("方案四:使用 worker 池复用 goroutine 去控制并发的 goroutine 数量")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// github.com/shgopher/grpool")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("fanout")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" out "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" async "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tpool "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" grpool"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewPool")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("100")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("50")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" pool"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Release")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" out "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 对一个nil的通道进行 for range 遍历会导致阻塞(block)。")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" value "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" vi "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" out "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" async "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t\tpool"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("JobQueue "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t\t\tvi "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" v\n\t\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t\tvi "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" v\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("p",[t._v("按照我的工作经验,使用工作池和信号量控制 goroutine 数量的方法最为常用,他们都是保证最多同时存在 n 个 goroutine,这样就避免了 goroutine 泄漏问题")]),t._v(" "),n("h3",{attrs:{id:"map-reduce"}},[t._v("map-reduce")]),t._v(" "),n("p",[t._v("我们在函数那个"),n("a",{attrs:{href:"https://github.com/shgopher/GOFamily/blob/aca91397692f99cd744fabcaca2055da3ec92c6f/%E5%9F%BA%E7%A1%80/%E5%87%BD%E6%95%B0%E6%96%B9%E6%B3%95/1.md?plain=1#L11",target:"_blank",rel:"noopener noreferrer"}},[t._v("章节"),n("OutboundLink")],1),t._v("介绍过 map-reduce 模式,不过并没有牵涉到并发,这里我们介绍一下并发版的 map-reduce 模式\nmap")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("mapChan")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("in "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("fn "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n out "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" in "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("out"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" out\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("out"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" in "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n out "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("fn")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" out\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("reduce")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("reduceChan")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("in "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("fn "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("r"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("v any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("any "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" in "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n out "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" in\n\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" in "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n out "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("fn")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("out"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" \n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" out\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h3",{attrs:{id:"pipeline-流水线模式"}},[t._v("pipeline-流水线模式")]),t._v(" "),n("p",[t._v("pipeline 模式的核心思想就是顺序,单线模式,数据从头到尾,顺序执行,一个阶段的输出是下一阶段的输入。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a A\na"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("then")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Download")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),n("p",[t._v("这就是 pipeline 的一个简单掩饰,它的底层可能是这样实现的")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("A"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("A"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("A"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("then")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("A"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("A"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Download")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("A"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("那么在并发的场景里,如何使 goroutine 实现 pipeline 模式呢?")]),t._v(" "),n("p",[t._v("这显然跟一般的流水线实现方法不同。")]),t._v(" "),n("p",[t._v("我们将 channel 比作一个 token,所谓令牌,只要我们控制获取令牌的顺序,那么就可以控制持有这些令牌的 goroutine 顺序。")]),t._v(" "),n("p",[t._v("下面我们使用 4 个 goroutine 依次打印 “a b c d”,300 次,我们使用流水线的方式实现:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Token "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\tGNumber "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\tNumber "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\tsign "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("world")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("switch")]),t._v(" id "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"a"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"b"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"c"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"d"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("worker")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" in "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" Token"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" out "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" Token"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" number "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("fn "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" number "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\ttoken "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("in\n\t\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("fn")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n \n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这段代码是为了保证最后一个 goroutine 执行完毕退出 ")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" id "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("GNumber"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" number "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("sign"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n\t\tout "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" token\n\t\tnumber"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("--")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("RunPipeline")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("GNumber "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("Number "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("chs"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" Token"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("fn "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n \n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" GNumber "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 核心代码: ")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 传入的in chan 和 out chan 刚好前后顺序")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这样 token 才能前后传递")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("worker")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" chs"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" chs"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v("GNumber"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Number"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("fn"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tchs"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("sign\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n GNumber "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),t._v("\n\tNumber "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("300")]),t._v("\n chs "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" Token"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" Token"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" Token"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" Token"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" Token"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("RunPipeline")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("GNumber"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("Number"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("chs"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("world"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h3",{attrs:{id:"stream-流模式"}},[t._v("stream-流模式")]),t._v(" "),n("p",[t._v("将 channel 当做流式管道,提供多种功能,比如筛选元素,跳过元素等")]),t._v(" "),n("p",[t._v("将数据转化为流,其中 channel 就是流,众多数据这里是 "),n("code",[t._v("...any")])]),t._v(" "),n("p",[t._v("创建流")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("stream")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("done "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" values "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),t._v("any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tc "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("c"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" values "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("done"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" s "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" c\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" \n")])])]),n("p",[t._v("根据流,我们可以有以下的处理")]),t._v(" "),n("ul",[n("li",[t._v("takeN:只取流中的前 n 个数据")]),t._v(" "),n("li",[t._v("takeFn:筛选流中的数据,只保留满足条件的数据")]),t._v(" "),n("li",[t._v("takeWhile:只取前面满足条件的数据,一旦不满足,就不再取了")]),t._v(" "),n("li",[t._v("skipN:跳过流中的前 n 个数据")]),t._v(" "),n("li",[t._v("skipFn:跳过满足条件的所有数据")]),t._v(" "),n("li",[t._v("skipWhile:跳过前面满足条件的数据,一旦不满足条件了,当前这个元素和以后的元素都会输出")])]),t._v(" "),n("h4",{attrs:{id:"taken"}},[t._v("takeN")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("TakeN")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("done "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("in "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("num "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tc "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("c"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" num"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("done"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" \n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("ok "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" in"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" ok "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t\t\tc "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("v\n\t\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\t\t\t\t\t\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" c\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h4",{attrs:{id:"takefn"}},[t._v("takeFn")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("TakeFn")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("done "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("in "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("fn "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tc "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("c"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" in "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("fn")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("done"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" c "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\t\t\t\t\t\n\t\t\t "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" c\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h4",{attrs:{id:"takewhile"}},[t._v("takeWhile")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("takeWhile")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("done "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" in "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fn "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n c "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("c"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" in "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("fn")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("done"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" c "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" c \n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h4",{attrs:{id:"skipn"}},[t._v("skipN")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("skipN")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("done "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" in "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" n "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n c "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("c"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" in "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("continue")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("done"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" c "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" c\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h4",{attrs:{id:"skipfn"}},[t._v("skipFn")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("skipFn")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("done "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" in "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fn "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n c "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("c"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" in "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("fn")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("done"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" c "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" c\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h4",{attrs:{id:"skipwhile"}},[t._v("skipWhile")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("skipWhile")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("done "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" in "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fn "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n c "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("c"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n skip "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" in "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" skip "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("fn")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("continue")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n skip "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("done"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" c "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" c\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h4",{attrs:{id:"测试-stream"}},[t._v("测试 stream")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("TestStream")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("testing"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("T"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n done "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("done"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n in "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("stream")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("done"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// takeN")]),t._v("\n out "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("takeN")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("done"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" in"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n want "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// reflect.DeepEqual 更适用于判断复杂类型的变量,")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 像数组、切片、map、结构体等是否相等,")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 简单类型可以用==运算符判断。")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" got "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("drain")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("out"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("reflect"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("DeepEqual")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("got"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" want"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"takeN = %v, want %v"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" got"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" want"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// takeFn")]),t._v("\n fn "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n n "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" n"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n out "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("takeFn")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("done"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" in"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fn"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n want "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" got "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("drain")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("out"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("reflect"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("DeepEqual")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("got"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" want"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"takeFn = %v, want %v"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" got"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" want"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// takeWhile")]),t._v("\n out "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("takeWhile")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("done"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" in"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n want "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" got "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("drain")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("out"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("reflect"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("DeepEqual")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("got"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" want"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"takeWhile = %v, want %v"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" got"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" want"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// skipN ")]),t._v("\n out "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("skipN")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("done"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" in"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n want "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" got "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("drain")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("out"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("reflect"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("DeepEqual")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("got"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" want"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"skipN = %v, want %v"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" got"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" want"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// skipFn")]),t._v("\n out "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("skipFn")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("done"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" in"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n want "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" got "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("drain")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("out"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("reflect"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("DeepEqual")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("got"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" want"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"skipFn = %v, want %v"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" got"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" want"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// skipWhile")]),t._v("\n out "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("skipWhile")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("done"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" in"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n want "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" got "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("drain")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("out"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("reflect"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("DeepEqual")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("got"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" want"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"skipWhile = %v, want %v"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" got"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" want"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("drain")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("in "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("any "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n out "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" in "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n out "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("out"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" out\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h3",{attrs:{id:"pipeline-流水线模式和-stream-流模式的对比"}},[t._v("pipeline 流水线模式和 stream 流模式的对比")]),t._v(" "),n("p",[t._v("流水线模式 (Pipeline Pattern) 和流模式 (Stream Pattern) 都是将任务分解成多个阶段来处理,但两者还是有一些区别:")]),t._v(" "),n("ul",[n("li",[t._v("数据流动方向不同\n"),n("ul",[n("li",[t._v("流水线模式是一条主线,数据从头流到尾,每个阶段处理完输出给下一个阶段。")]),t._v(" "),n("li",[t._v("流模式是多条分支,数据可以从多个来源流入,经过处理流向多个去处。")])])]),t._v(" "),n("li",[t._v("执行方式不同\n"),n("ul",[n("li",[t._v("流水线模式强调阶段间"),n("strong",[t._v("串行执行")]),t._v("。"),n("strong",[t._v("一个阶段的输出是下一阶段的输入")]),t._v("。")]),t._v(" "),n("li",[t._v("流模式可以"),n("strong",[t._v("并行执行")]),t._v(","),n("strong",[t._v("不同阶段可以同时操作不同数据")]),t._v("。")])])]),t._v(" "),n("li",[t._v("连接方式不同\n"),n("ul",[n("li",[t._v("流水线模式中,每个阶段靠固定管道连接,顺序不能改变。")]),t._v(" "),n("li",[t._v("流模式中,流可以更灵活自由地连接。")])])]),t._v(" "),n("li",[t._v("扩展性不同\n"),n("ul",[n("li",[t._v("流水线模式扩展一个阶段可能影响整个流水线。")]),t._v(" "),n("li",[t._v("流模式扩展一个阶段对其他阶段影响很小。")])])])]),t._v(" "),n("p",[t._v("总结:")]),t._v(" "),n("ul",[n("li",[t._v("流水线模式注重将任务划分多个线性执行的固定阶段。")]),t._v(" "),n("li",[t._v("流模式注重构建灵活的流处理流程,流可以并行运动。")])]),t._v(" "),n("p",[t._v("所以两者侧重点不同,在使用上也有区别。")]),t._v(" "),n("h2",{attrs:{id:"channel-注意事项"}},[t._v("channel 注意事项")]),t._v(" "),n("p",[t._v("发生下面事项一定会触发 panic:")]),t._v(" "),n("ul",[n("li",[t._v("向已经关闭的 channel 中发送数据")]),t._v(" "),n("li",[t._v("关闭一个 nil channel")]),t._v(" "),n("li",[t._v("关闭一个已经被关闭的 channel")])]),t._v(" "),n("h3",{attrs:{id:"goroutine-泄露"}},[t._v("goroutine 泄露")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n ch "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n ch "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("ch"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"ch 被关闭了"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("After")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("当 time.After(time.Second) 执行完毕以后,上面那个 goroutine 因为无法接收数据,所以就会一直阻塞在发送数据那个地方,所以这个代码中,goroutine 就会泄露了。")]),t._v(" "),n("p",[t._v("解决之道就是将容量设置为 1 即可:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n ch "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n ch "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("ch"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"ch 被关闭了"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("After")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("当设置为 1 的时候,即使没有接受者了,发送这个地方的代码也能执行完毕,所以这个 goroutine 是不会泄露了。")]),t._v(" "),n("p",[t._v("这里插一句,main goroutine 只要退出,其它 goroutine 不管有没有执行完毕也会退出,所以如果这种代码在 main 函数中出现,那么是不会发生 goroutine 泄露问题的,因为:")]),t._v(" "),n("p",[n("em",[n("strong",[t._v("main 函数结束以后,其它 goroutine 自动结束")])])]),t._v(" "),n("h2",{attrs:{id:"channle-和运行时调度如何交互"}},[t._v("channle 和运行时调度如何交互")]),t._v(" "),n("p",[t._v("channel 和 Go 运行时调度器的交互方式如下:")]),t._v(" "),n("ol",[n("li",[n("p",[t._v("当 goroutine 向 channel 发送数据时,会调用 runtime.chansend 函数。该函数会判断 channel 是否已满,如果满了则让 goroutine 进入阻塞等待。")])]),t._v(" "),n("li",[n("p",[t._v("当 goroutine 从 channel 接收数据时,会调用 runtime.chanrecv 函数。该函数会判断 channel 是否为空,如果为空则让 goroutine 进入阻塞等待。")])]),t._v(" "),n("li",[n("p",[t._v("运行时调度器通过维护等待队列来管理被阻塞的 goroutine。当 channel 可读写时,会从等待队列中弹出 goroutine 继续运行。")])]),t._v(" "),n("li",[n("p",[t._v("运行时调度器以非常高频率运行,会在 goroutine 之间快速切换。这给用户的感觉就是 goroutine 并发运行。")])]),t._v(" "),n("li",[n("p",[t._v("channel 的发送接收动作必须匹配,否则程序会死锁。调度器试图重排发送接收顺序来避免死锁。")])])]),t._v(" "),n("p",[t._v("总结来说,channel 的阻塞是建立在运行时调度器的基础上。调度器管理被阻塞的 goroutine 队列,在条件允许时恢复 goroutine 继续运行。channel 的使用保证了数据的同步传递。")]),t._v(" "),n("h3",{attrs:{id:"如果去掉-channel-goroutine-就可以真实的并行了吗"}},[t._v("如果去掉 channel,goroutine 就可以真实的并行了吗")]),t._v(" "),n("p",[t._v("如果没有 channel 的话,goroutine 可以真正并行执行,不会被阻塞。")]),t._v(" "),n("p",[t._v("channel 的发送接收机制,会在对端未准备好时造成 goroutine 阻塞。这就会引起 goroutine 之间的同步关系。")]),t._v(" "),n("p",[t._v("而如果没有 channel:")]),t._v(" "),n("ol",[n("li",[n("p",[t._v("goroutine 之间不存在同步依赖,可以任意交叉执行。")])]),t._v(" "),n("li",[n("p",[t._v("程序需要自己维护竞争和同步逻辑,较难处理好并发问题。")])]),t._v(" "),n("li",[n("p",[t._v("goroutine 无法通过 channel 通信,必须使用共享变量,增加竞争风险。")])]),t._v(" "),n("li",[n("p",[t._v("程序整体并发效果可能更好,但复杂度和错误风险都更高。")])]),t._v(" "),n("li",[n("p",[t._v("丧失了 channel 带来的可组合性、表达能力等优势。")])])]),t._v(" "),n("p",[t._v("所以 channel 的引入让编程变复杂,但收获是可靠性和可维护性。需要根据实际情况权衡 channel 的引入带来的收益。")]),t._v(" "),n("p",[t._v("如果非要最大程度发挥并行,确实可以考虑去掉 channel,但成功的并发程序往往不是依靠纯并行来实现的。")]),t._v(" "),n("p",[t._v("这需要根据具体情形来权衡 channel 的引入带来的收益和复杂度。")]),t._v(" "),n("h3",{attrs:{id:"channel-底层实际上是互斥锁-阻塞机制也符合互斥锁对于-goroutine-的阻塞规则吗"}},[t._v("channel 底层实际上是互斥锁,阻塞机制也符合互斥锁对于 goroutine 的阻塞规则吗")]),t._v(" "),n("p",[t._v("channel 的发送接收在底层会使用互斥锁来实现同步。所以它继承了互斥锁一些内在的特性:")]),t._v(" "),n("ol",[n("li",[n("p",[t._v("阻塞的 goroutine 会形成 FIFO (先进先出) 的等待队列,这确保了公平性。")])]),t._v(" "),n("li",[n("p",[t._v("runtime 会优先唤醒等待时间最长的 goroutine,防止饥饿。")])]),t._v(" "),n("li",[n("p",[t._v("发送和接收有时会采用短暂的自旋等待锁,避免过早休眠线程。")])]),t._v(" "),n("li",[n("p",[t._v("发送方和接收方争用同一个锁,这会引起优先级反转等问题。")])])]),t._v(" "),n("p",[t._v("但是")]),t._v(" "),n("p",[t._v("channel 的实现要比直接使用锁更复杂,它内部做了很多优化:")]),t._v(" "),n("ol",[n("li",[n("p",[t._v("非阻塞发送和接收会被优先执行,避免不必要的阻塞。")])]),t._v(" "),n("li",[n("p",[t._v("尽量避免线程休眠,减少锁的争用。")])]),t._v(" "),n("li",[n("p",[t._v("多核心会并行匹配发送和接收等。")])]),t._v(" "),n("li",[n("p",[t._v("可调节的缓冲区大小可根据场景优化吞吐量。")])])]),t._v(" "),n("p",[t._v("综上,channel 继承了互斥锁的一些内在特性,但它的实现要复杂很多,做了大量优化来减少锁争用的影响。")]),t._v(" "),n("p",[t._v("所以使用 channel 的信号量模式去实现的互斥锁并不适合,因为它比 go 本身的互斥锁更加复杂")]),t._v(" "),n("h2",{attrs:{id:"issues"}},[t._v("issues")]),t._v(" "),n("h3",{attrs:{id:"channel-是并发银弹吗"}},[t._v("channel 是并发银弹吗?")]),t._v(" "),n("p",[t._v("在 go 语言中,绝大多数情况下都是是使用 channel 更有优势,比如上文提到的那几种场景,例如数据的传递以及任务编排,需要跟 select 或者 context 结合,那么这都是 channel 的适用场景")]),t._v(" "),n("p",[t._v("不过如果是对于共享资源的并发访问,使用传统的互斥锁更有优势一些")]),t._v(" "),n("p",[t._v("如果只是线程安全的对于某个变量的数据变更,使用原子包显然是更加合适的选择")]),t._v(" "),n("h3",{attrs:{id:"有无-buffer-的-channel-区别"}},[t._v("有无 buffer 的 channel 区别")]),t._v(" "),n("p",[t._v("go 语言中经常会出现一个 bug,就是死锁,很多都很没有设置 channel 缓存有关,有的时候给 channel 设置一个缓存往往可以规避很多的 panic 风险")]),t._v(" "),n("h3",{attrs:{id:"channel-close-后-read-write-close-的区别"}},[t._v("channel close 后,read write close 的区别")]),t._v(" "),n("ul",[n("li",[t._v("read:正常值+零值")]),t._v(" "),n("li",[t._v("close:panic")]),t._v(" "),n("li",[t._v("write:panic")])]),t._v(" "),n("h3",{attrs:{id:"channle-底层是什么"}},[t._v("channle 底层是什么")]),t._v(" "),n("p",[t._v("一个内部有锁的循环队列")]),t._v(" "),n("p",[t._v("channel 在 Go 语言中的底层实现主要涉及以下几个方面:")]),t._v(" "),n("ol",[n("li",[n("p",[t._v("数据结构:channel 在内部维护一个消息队列,用于存储缓冲区的数据 (有缓存的话),同时有发送方、接收方的指针。")])]),t._v(" "),n("li",[n("p",[t._v("同步机制:使用互斥锁 (Mutex) 和条件变量 (Cond) 实现同步,保证并发安全。")])]),t._v(" "),n("li",[n("p",[t._v("调度机制:通过运行时调度器管理 goroutine 的阻塞和唤醒,维护等待队列。")])]),t._v(" "),n("li",[n("p",[t._v("内存管理:与运行时内存管理紧密结合,缓冲区数据的内存分配与释放。")])]),t._v(" "),n("li",[n("p",[t._v("性能优化:负载均衡、本地队列等方式提高性能。")])])]),t._v(" "),n("p",[t._v("具体而言,channel 的数据结构 hchan 中包含以下字段:")]),t._v(" "),n("ul",[n("li",[t._v("buf:循环队列,维护缓冲区")]),t._v(" "),n("li",[t._v("sendx/recvx:发送和接收指针")]),t._v(" "),n("li",[t._v("lock:互斥锁")]),t._v(" "),n("li",[t._v("sendsema/recvsema:发送和接收的条件变量")])]),t._v(" "),n("p",[t._v("发送接收时需要获取互斥锁,阻塞时等待条件变量。调度器 basis 上维护等待队列。")]),t._v(" "),n("h3",{attrs:{id:"编程题-使用三个-goroutine-打印-abc-100-次"}},[t._v("编程题,使用三个 goroutine 打印 abc 100 次")]),t._v(" "),n("p",[t._v("上文提到的 channel 任务编排中的 pipeline 流水线模式完美解决这个问题。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Token "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\tGNumber "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\tNumber "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\tsign "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("world")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("switch")]),t._v(" id "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"a"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"b"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"c"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("worker")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" in "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" Token"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" out "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" Token"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" number "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("fn "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" number "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\ttoken "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("in\n\t\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("fn")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("id"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" id "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("GNumber"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" number "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("sign"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" \n\t\tout "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" token\n\t\tnumber"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("--")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("RunPipeline")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("GNumber "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("Number "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("chs"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" Token"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("fn "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n \n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" GNumber "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("worker")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" chs"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" chs"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v("GNumber"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Number"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("fn"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tchs"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("sign\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n GNumber "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v("\n\tNumber "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("100")]),t._v("\n chs "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" Token"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" Token"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" Token"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" Token"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" Token"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("RunPipeline")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("GNumber"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("Number"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("chs"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("world"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("包括之前的水生成工厂那道题,也可以使用 pieline 模式来解决,替换掉之前使用的 waitgroup 和信号量,以及循环栅栏")]),t._v(" "),n("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),n("ul",[n("li",[t._v("https://betterprogramming.pub/common-goroutine-leaks-that-you-should-avoid-fe12d12d6ee")]),t._v(" "),n("li",[t._v("https://github.com/fortytw2/leaktest")]),t._v(" "),n("li",[t._v("https://cloud.tencent.com/developer/article/1921580")])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/107.68b61ef6.js b/assets/js/107.68b61ef6.js new file mode 100644 index 000000000..a7c7e0a74 --- /dev/null +++ b/assets/js/107.68b61ef6.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[107],{540:function(t,n,s){"use strict";s.r(n);var a=s(36),e=Object(a.a)({},(function(){var t=this,n=t.$createElement,s=t._self._c||n;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h1",{attrs:{id:"context"}},[t._v("context")]),t._v(" "),s("p",[t._v("context 应该被翻译为上下文,但是 go 语言中的 context 更多的作用是"),s("strong",[t._v("取消子 goroutine")]),t._v(",传递信息这个上下文本来的含义,在 go 的 context 反而不是重点")]),t._v(" "),s("p",[t._v("context 本身是一个接口类型,它拥有四个方法:")]),t._v(" "),s("ul",[s("li",[t._v("Deadline()(deadline time.Time,ok bool):返回一个代表此上下文完成工作的时间,如果没有截止时间,返回 false")]),t._v(" "),s("li",[t._v("Done() <-chan struct {}:代表了完结")]),t._v(" "),s("li",[t._v("Err() error:如果 context 已经 done 了返回一个 error 错误,错误值分别为:Canceled,DeadlineExceeded,如果没有 done,error 返回 nil")]),t._v(" "),s("li",[t._v("Value(key any) any:context 中存储的键值对")])]),t._v(" "),s("p",[t._v("context 作为上下文,需要一个最顶层的 context 接口类型,你可以使用 "),s("code",[t._v("context.Background()")]),t._v(" 或者 "),s("code",[t._v("context.TODO()")]),t._v(" 去充当这个顶端,这两者是一个意思,用哪个都可以,这是 go 提供的已经实现了 context 接口类型的对象,它的底层是一个结构体")]),t._v(" "),s("p",[t._v("context 有一些编程范式:")]),t._v(" "),s("ul",[s("li",[t._v("将 cotext 设置为参数的第一个,例如 "),s("code",[t._v("func age(ctx context.Context,a string)")])]),t._v(" "),s("li",[t._v("不要使用 nil 作为上下文参数,如果想要空的顶端上下文,使用 "),s("code",[t._v("context.Background")]),t._v(",虽然 background 底层实现接口的时候,也是内容为空,但是它的确是实现了接口")]),t._v(" "),s("li",[t._v("context 只能作为函数的临时传递对象,不能持久化它,使用数据库保存,等持久化方式都是不可取的")]),t._v(" "),s("li",[t._v("使用 withValue 方法的时候,key 值不要使用 string,如果起冲突,使用自建的类型,例如 "),s("code",[t._v("type A struct{}")])]),t._v(" "),s("li",[t._v("尽量不要定义输出的 key 值")])]),t._v(" "),s("h2",{attrs:{id:"使用-context"}},[t._v("使用 context")]),t._v(" "),s("p",[t._v("标准库中提供了多个 context 接口类型实例:")]),t._v(" "),s("ul",[s("li",[t._v("WithCancel")]),t._v(" "),s("li",[t._v("WithCancelCause")]),t._v(" "),s("li",[t._v("WithDeadline")]),t._v(" "),s("li",[t._v("WithDeadlineCause")]),t._v(" "),s("li",[t._v("WithTimeout")]),t._v(" "),s("li",[t._v("WithTimeoutCause")])]),t._v(" "),s("p",[t._v("其中,带有 Cause 的函数跟不带的函数基本意思相同,但是多了一个 cause 的内容,它是指的是取消的原因")]),t._v(" "),s("h2",{attrs:{id:"withvalue"}},[t._v("withValue")]),t._v(" "),s("p",[t._v("WithValue 基于 parent Context 生成一个新的 Context,保存了一个 key-value 键值\n对。它常常用来传递上下文。")]),t._v(" "),s("p",[t._v("context 在查询 key 值的时候还支持链式查找,如果没有发现数据就往 parent context 中查询")]),t._v(" "),s("div",{staticClass:"language-go extra-class"},[s("pre",{pre:!0,attrs:{class:"language-go"}},[s("code",[t._v("ctx "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("TODO")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nctx "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithValue")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"key1"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"0001"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nctx "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithValue")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"key2"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"0001"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nctx "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithValue")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"key3"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"0001"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nctx "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithValue")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"key4"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"0004"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nfmt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Value")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"key1"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("h3",{attrs:{id:"withcancel"}},[t._v("WithCancel")]),t._v(" "),s("p",[t._v("withCancel 返回父 context 中的 ctx 实例副本,它相当于父 context 的子 context,并且在父 context 被取消时,子 context 也会被取消。")]),t._v(" "),s("div",{staticClass:"language-go extra-class"},[s("pre",{pre:!0,attrs:{class:"language-go"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("withCancel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("parent Context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("cancelCtx "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" parent "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("panic")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"cannot create context from nil parent"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tc "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("cancelCtx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 向上寻找")]),t._v("\n\tc"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("propagateCancel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("parent"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" c"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" c\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("propagateCancel 部分代码:")]),t._v(" "),s("div",{staticClass:"language-go extra-class"},[s("pre",{pre:!0,attrs:{class:"language-go"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("c "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("cancelCtx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("propagateCancel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("parent Context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" child canceler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tc"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Context "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" parent\n\n\tdone "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" parent"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Done")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" done "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// parent is never canceled")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("done"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 如果父done了,那么子ctx一定也会出发cancel")]),t._v("\n\t\tchild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("cancel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" parent"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Err")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Cause")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("parent"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("default")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" p"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ok "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("parentCancelCtx")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("parent"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" ok "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// parent is a *cancelCtx, or derives from one.")]),t._v("\n\t\tp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("mu"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" p"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("err "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 如果父发生了cancel,那么子ctx也要触发cancel")]),t._v("\n\t\t\tchild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("cancel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" p"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("err"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" p"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("cause"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" p"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("children "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\tp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("children "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("canceler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 将子ctx添加到父ctx中")]),t._v("\n\t\t\tp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("children"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("child"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\tp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("mu"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 父 done被触发,那么子ctx就会被触发cancel操作")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" parent"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Done")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t\tchild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("cancel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" parent"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Err")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Cause")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("parent"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("child"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Done")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" canceler "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("cancel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("removeFromParent "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" cause "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Done")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("propagateCancel 将 c 向上传播,顺着 parent 的路径一直向上查找,直到找到 parentCancelCtx,如果不为空,就把自己加入到这个 parentCancelCtx 的 children 切片中,然后就可以在父 ctx 取消的时候,通知自己也被取消")]),t._v(" "),s("p",[t._v("当这个 cancelCtx 的 cancel 函数被调用的时候,parent 的 Done 被 close 的时候,或者父 ctx 触发了 cancel 的时候,这个子 ctx 会被触发 cancel 动作")]),t._v(" "),s("p",[t._v("cancel 是向下传递的,如果一个 WithCancel 生成的 Context 被 cancel 时,如果它的子 Context (也有可能是孙,或者更低),就会被 cancel,但是不会向上传递。parent Context 不会因为子 Context 被 cancel 而 cancel。")]),t._v(" "),s("div",{staticClass:"language-go extra-class"},[s("pre",{pre:!0,attrs:{class:"language-go"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"context"')]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"time"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" cancel "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithCancel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Background")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("cancel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("ctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Done")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t\t\tfmt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"done"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("WithCancel 返回 parent context 的一个副本,它自然就是子 context,当父 context 被 cancel 的时候,子 context 也会被 cancel")]),t._v(" "),s("h3",{attrs:{id:"withtimeout-withdeadline"}},[t._v("withTimeout withDeadline")]),t._v(" "),s("p",[t._v("这两个只是添加了到期时间,一个是超时时间,一个是截止时间,一旦超过时间后,自动 close 这个 done 这个 channel")]),t._v(" "),s("p",[t._v("综上所述,done 这个 channel 被 close 有三个原因:")]),t._v(" "),s("ul",[s("li",[t._v("截止时间到了")]),t._v(" "),s("li",[t._v("cancel 函数被调用了")]),t._v(" "),s("li",[t._v("parent context 的 done close 了,然后子 ctx 也要触发 cancel 方法")]),t._v(" "),s("li",[t._v("parent context cancel 了触发子 ctx cancel 方法")])]),t._v(" "),s("p",[t._v("关于第三条,解释一下:(第四条类似)")]),t._v(" "),s("div",{staticClass:"language-go extra-class"},[s("pre",{pre:!0,attrs:{class:"language-go"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"context"')]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"time"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 创建一个父context,设置deadline为3秒")]),t._v("\n\tparentCtx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" cancel "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithTimeout")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Background")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("cancel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 创建一个子context,deadline继承父context")]),t._v("\n\tchildCtx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" cancel "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithCancel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("parentCtx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("cancel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 注意这里需要调用cancel")]),t._v("\n\n\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("doWork")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("childCtx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\ttime"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("14")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("doWork")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("ctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Done")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 工作代码")]),t._v("\n\t\t\tfmt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"over"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//default:")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v('//\tfmt.Println("default")')]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),s("p",[t._v("在使用 select 监听 Context 的 Done 通道时,最好不要使用 default 分支。")]),t._v(" "),s("p",[t._v("原因有以下几点:")]),t._v(" "),s("ul",[s("li",[t._v("default 分支会导致无法准确检测到 Context cancellation 的信号,如我们之前分析的那样")]),t._v(" "),s("li",[t._v("使用 default 时需要仔细设计 case 分支的阻塞时间,比较 tricky")]),t._v(" "),s("li",[t._v("不使用 default 可以确保每次 select 都会阻塞,从而能捕捉到外部的取消通知")]),t._v(" "),s("li",[t._v("默认情况下,不使用 default 也可以使代码更简洁")])]),t._v(" "),s("h2",{attrs:{id:"带有-cause-的函数"}},[t._v("带有 cause 的函数")]),t._v(" "),s("p",[t._v("我们看一个例子")]),t._v(" "),s("div",{staticClass:"language-go extra-class"},[s("pre",{pre:!0,attrs:{class:"language-go"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"context"')]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"errors"')]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" myError "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" errors"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("New")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"myError"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" cancel "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithCancelCause")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("TODO")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("cancel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("myError"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Err")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n\tfmt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Cause")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// returns myError")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("但我们调用 cancel 函数的时候,内部参数是一个 error 类型,调用 context.Cause(ctx) 返回的就是它的取消原因,那么这里的话就是 MyError")]),t._v(" "),s("h2",{attrs:{id:"在-withtimeout-中-cancel-函数的存在意义是什么"}},[t._v("在 WithTimeout 中 cancel() 函数的存在意义是什么?")]),t._v(" "),s("p",[t._v("cancel 函数的作用是可以手动提前取消 Context,使其 Done channel 关闭,不用等待 timeout 的时间或者 deadline 的时间")]),t._v(" "),s("p",[t._v("所以 cancel 函数相当于手动取消的意思,并不是说有了 timeout deadline 之后,cancel 函数就没有存在的意义了。")]),t._v(" "),s("p",[t._v("按照 go 的语法,即便是 timeout 触发了 <- done 操作,你仍然需要手动的去在最后调用 cancel 函数,否则就会报错")]),t._v(" "),s("p",[t._v("WithTimeout 在超时时会自动 cancel context,但是 cancel 函数还是需要调用,以释放/重置 Context 内部的 timer,如果不调用 cancel,timer 不会被释放,持续运行并重复 cancel 导致 context leak,占用更多资源。")]),t._v(" "),s("div",{staticClass:"language-go extra-class"},[s("pre",{pre:!0,attrs:{class:"language-go"}},[s("code",[t._v("ctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" cancel "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" context"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithTimeout")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("parentCtx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("doWork")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 1秒后决定取消任务")]),t._v("\ntime"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("time"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n"),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("cancel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("h2",{attrs:{id:"issues"}},[t._v("issues")]),t._v(" "),s("h3",{attrs:{id:"contex-contex-如何实现并发安全的"}},[t._v("contex.Contex 如何实现并发安全的?")]),t._v(" "),s("p",[t._v("Go 语言中 context 实现并发安全的主要手段是通过原子操作和 Mutex 来保证状态的原子性")]),t._v(" "),s("p",[t._v("根据底层代码可知,当不同的 goroutine 获取 ctx 的时候,每次操作都会加上互斥锁,来保证数据的非竞争性,线程的安全,例如")]),t._v(" "),s("div",{staticClass:"language-go extra-class"},[s("pre",{pre:!0,attrs:{class:"language-go"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" p"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ok "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("parentCancelCtx")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("parent"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" ok "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// parent is a *cancelCtx, or derives from one.")]),t._v("\n\t\tp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("mu"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" p"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("err "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 如果父发生了cancel,那么子ctx也要触发cancel")]),t._v("\n\t\t\tchild"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("cancel")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" p"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("err"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" p"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("cause"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" p"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("children "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\tp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("children "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("canceler"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 将子ctx添加到父ctx中")]),t._v("\n\t\t\tp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("children"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("child"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\tp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("mu"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])])}),[],!1,null,null,null);n.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/108.1b1c79b1.js b/assets/js/108.1b1c79b1.js new file mode 100644 index 000000000..86b835cb6 --- /dev/null +++ b/assets/js/108.1b1c79b1.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[108],{537:function(t,s,n){"use strict";n.r(s);var a=n(36),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"内存模型"}},[t._v("内存模型")]),t._v(" "),n("p",[t._v("go 官方介绍 go 内存模型的时候说:探究在什么条件下,A goroutine 在读取一个变量的值的时,能够看到其它 goroutine 对这个变量进行的写的结果。")]),t._v(" "),n("p",[t._v("我们为什么需要内存模型?")]),t._v(" "),n("p",[t._v("CPU 指令可能会被重排序,并且存在多级缓存,例如 Go 语言中的多级内存模型。不同的 CPU 架构 (比如 x86 和 ARM) 也会对指令顺序产生影响,编译器优化也可能改变指令顺序。")]),t._v(" "),n("p",[t._v("因此,编程语言需要一个内存规范,也就是内存模型,来规定程序中的内存访问和同步的语义,保证在不同的硬件和编译器下,程序的执行结果保持一致")]),t._v(" "),n("h2",{attrs:{id:"介绍一些-go-语言中的基本操作"}},[t._v("介绍一些 go 语言中的基本操作")]),t._v(" "),n("blockquote",[n("p",[t._v("除了基本的读和写之外都是同步操作,包括以下列举的,并且更多以这些基础操作衍生出的操作。")])]),t._v(" "),n("ul",[n("li",[t._v("读\n"),n("ul",[n("li",[t._v("基础的读\n"),n("ul",[n("li",[t._v("对超过机器 word 大小的值读,可以看作是拆成 word 大小的几个读的无序进行")])])]),t._v(" "),n("li",[t._v("原子包的读")]),t._v(" "),n("li",[t._v("使用 sync.Mux 的读")]),t._v(" "),n("li",[t._v("使用 channel 形式的读")])])]),t._v(" "),n("li",[t._v("写\n"),n("ul",[n("li",[t._v("基础的写\n"),n("ul",[n("li",[t._v("变量的初始化就是一个写操作")]),t._v(" "),n("li",[t._v("对超过机器 word 大小的值写,可以看作是拆成 word 大小的几个写的无序进行")])])]),t._v(" "),n("li",[t._v("原子包的写")]),t._v(" "),n("li",[t._v("sync.mux 的写")]),t._v(" "),n("li",[t._v("向 channel 发送数据")])])]),t._v(" "),n("li",[t._v("atomic 的 compare and swap 操作,兼顾了写和读")])]),t._v(" "),n("p",[t._v("我们讨论 go 内存模型的时候,主要聚焦下面四种细节的"),n("strong",[t._v("差异性")]),t._v(":")]),t._v(" "),n("ul",[n("li",[t._v("操作的种类:\n"),n("ul",[n("li",[t._v("普通的读或者写")]),t._v(" "),n("li",[n("strong",[t._v("同步操作")]),t._v(",比如利用 sync 包进行同步,利用 channel 在不同的 goroutine 中间进行同步,利用 atomic 包进行同步等。")])])]),t._v(" "),n("li",[t._v("这个操作者在程序中的位置")]),t._v(" "),n("li",[t._v("被操作者在内存位置,或者是被访问的变量的位置")]),t._v(" "),n("li",[t._v("被操作者值的类型")])]),t._v(" "),n("h2",{attrs:{id:"内存模型存存在的意义"}},[t._v("内存模型存存在的意义")]),t._v(" "),n("ul",[n("li",[n("p",[t._v("给程序员一个最根本的法则 --- 你只要遵循这个法则,那么在进行多 goroutine 数据访问的时候,做串行控制 (使用同步操作) 一定会成功。")])]),t._v(" "),n("li",[n("p",[t._v("给编译器优化开发者一个法则,只要遵循这个法则,就能不出错的做出编译器级别的优化。")])])]),t._v(" "),n("h3",{attrs:{id:"具体表现"}},[t._v("具体表现")]),t._v(" "),n("ul",[n("li",[n("strong",[t._v("探究可见性")])]),t._v(" "),n("li",[n("strong",[t._v("happens- before")]),t._v(":x 操作一定在 y 操作之前执行完毕,这里说的并不是时间,而是 “执行完毕” 这个结论,我们可以放心的在 y 中使用 x 的执行结果。主要突出的是这个完成性。")])]),t._v(" "),n("h3",{attrs:{id:"三条定律"}},[t._v("三条定律")]),t._v(" "),n("ul",[n("li",[n("p",[t._v("在一个 goroutine 中,程序执行的顺序一定是符合代码写的顺序的。但是不同的 goroutine 中这个定理失效。")])]),t._v(" "),n("li",[n("p",[t._v("在"),n("strong",[t._v("同步操作")]),t._v("的时候,从 a goroutine 看到 b goroutine 的执行顺序,必须符合同步操作的顺序,这里举个例子,举例:b 的同步操作是什么顺序,那么 a 读取的就是什么顺序。比如 b 中有三个 Channel,他们执行的顺序是先 a 发 b 收,b 发,c 收,那么在 a 中读取的顺序跟这个同步的操作顺序是一致的,"),n("strong",[t._v("如果不是同步操作就可能不一致。")])])]),t._v(" "),n("li",[n("p",[t._v("对于非同步操作的普通读和写,如果 b 操作了 a,那么必须成立")]),t._v(" "),n("ul",[n("li",[t._v("b 发生在 a 之前")])])])]),t._v(" "),n("p",[t._v("我们可以使用 "),n("code",[t._v("go build -race")]),t._v(" 来发现数据竞争。")]),t._v(" "),n("p",[t._v("接下来我们对于内存模型的具体表现进行更具体的介绍。")]),t._v(" "),n("h2",{attrs:{id:"重排和可见性"}},[t._v("重排和可见性")]),t._v(" "),n("p",[t._v("由于指令的重排,代码"),n("strong",[t._v("不一定")]),t._v("会按照你写的顺序执行。")]),t._v(" "),n("p",[t._v("这里给出一个案例:有两个 goroutine 分别是 g1,和 g2,g1 读某个变量的数据,g2 写这个变量的数据,当 g1 读取到 g2 写的新数据之后,你也不一定能读取到在 goroutine g2 中排列在这个写数据操作之前的操作:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\ta "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n\tb "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v("\n\tdone "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\ta "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v("\n\t\tb "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),t._v("\n\t\tdone "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("done "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 可能观察到的就是输出的 4 3,但是无法保证必须是4 3 ,能观察到 != 保证")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("p",[t._v("下面这种情况,a goroutine 无法观察到 b goroutine 是否完成了写操作,有可能造成 a goroutine 一直被卡的现象:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Result "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tdata "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\tr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Result\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("set")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\td "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("new")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Result"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\td"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("data "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hi there"')]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里无法确认一定会运行")]),t._v("\n\tr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" d "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里无法确认观察到")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("set")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" r "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("r"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("data"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("blockquote",[n("p",[t._v("there is no guarantee that the write to done will ever be observed by main,"),n("strong",[t._v("since there are no synchronization events between the two threads")]),t._v("。The loop in main is not guaranteed to finish。“")])]),t._v(" "),n("p",[t._v("这一句是 go blog 中对于这代代码的描述,重点是说,这俩线程中,没有存在同步原语,所以无法做到从 main goroutine 去观测到 r 的状态,也就是说,在 goroutine 中只要存在了同步原语,那么就可以把两个 goroutine 当作正常的语句来看。")]),t._v(" "),n("p",[t._v("这里有两点无法确认,第一:主 goroutine 无法"),n("strong",[t._v("确认")]),t._v(" r 的状态,这里指的就是"),n("strong",[t._v("可观测性")]),t._v(",因为这里是非同步操作,所以主 goroutine 的读无法观测到 "),n("code",[t._v("go set()")]),t._v(" 这里 "),n("code",[t._v("r = d")]),t._v(" 这里的写,注意是无法完全确认,通常来说这段代码是可以运行的,我说的是通常,第二无法保证 "),n("code",[t._v("d.data")]),t._v(" 一定会运行,所以有可能输出的是空字符串,这里说的就是因为编译优化导致的指令重排。")]),t._v(" "),n("h2",{attrs:{id:"同步操作的-happens-before"}},[t._v("同步操作的 happens-before")]),t._v(" "),n("p",[t._v("go 不直接提供 cpu 屏障,来保证编译器或者 cpu 保证顺序性,而是使用不同架构的内存屏障指令来实现统一的并发原语。")]),t._v(" "),n("h3",{attrs:{id:"单个-goroutine"}},[t._v("单个 goroutine")]),t._v(" "),n("p",[t._v("上文说到,"),n("strong",[t._v("在一个 goroutine 中,happens- before 其实就等于代码书写的顺序,这一点是严格成立的")]),t._v("。")]),t._v(" "),n("h3",{attrs:{id:"init-函数"}},[t._v("init 函数")]),t._v(" "),n("p",[t._v("go 语言的初始化是在单一的 goroutine 中执行的,也就是 main goroutine。")]),t._v(" "),n("p",[n("strong",[t._v("p 包导入了 q 包,那么 q 的 init 函数一定 happens before p 的任何初始化代码")]),t._v("。")]),t._v(" "),n("p",[t._v("这里有点像树,导入的过程,以及初始化的过程,会形成一个多叉树,从最底层的树开始进行初始化,逐步的忘上层走,先进后出,栈一样的感觉,并且相同的包只会导入一次,进而也只会初始化一次,比如 q 包被第三层导入了,在第五层也导入了,那么这个包在从底层往上走的过程中,只会在第五层先全局变量后 init 函数的初始化一次。这导入的包全部初始化一遍之后,才开始进行 main 包的 mian 函数,然后进而去运行接下来的逻辑。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n a "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" c "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" b\n\n b "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n \n c "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n d "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v(" \n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n d "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" \n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" d \n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("初始化的顺序是这样的,首先初始化 a,因为 a = c + b,那么系统开始初始化 c 和 b,c 和 b 等于一个函数 f,那么就开始初始化这个函数,函数里面是 d 那么开始初始化 d,所以 b = 3+1 c = 3+1+1 a = 9,这个时候 d 已经++ 两次了,所以最终的初始化以后 a = 9 b = 4 c = 5 d = 5")]),t._v(" "),n("p",[t._v("包级别的变量按照代码顺序初始化,同一个包的众多文件会按照文件名的排列顺序进行初始化。")]),t._v(" "),n("h3",{attrs:{id:"goroutine"}},[t._v("goroutine")]),t._v(" "),n("p",[t._v("启动 goroutine 的 go 语言执行,一定 happens - before 此 goroutine 内的代码执行。退出可就没有任何 happens- before 的理论了,所以退出我们通常要部署同步语句。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n a "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hi"')]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("从单个 goroutine 的 happens- before 的关系来看,a = “hi” 一定 happens- before go f (),从新 goroutine 的开辟来说,go f () 一定 happens- before fmt.Println 所以这个代码中,hi 一定能被输出。")]),t._v(" "),n("h3",{attrs:{id:"channel"}},[t._v("channel")]),t._v(" "),n("p",[t._v("go 语言有一句经典名言,不要使用共享内存来通信 (sync.Mutex) 而应该采用通信的方式来共享内存,这个后者说的就是 channel,channel 是同步操作的首选。")]),t._v(" "),n("ul",[n("li",[t._v("往 channel 中发数据,happens - before 从这个 channel 接收数据"),n("strong",[t._v("完成")]),t._v("。注意这里说的是接收完成,"),n("strong",[t._v("没说")]),t._v("发数据 happens- before 这个 channel 接受数据开始的时候。")])]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" c "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" \n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//这里之所以a = hi 发生在 c <-0 之前,就是因为这个goroutine和main goroutine 存在同步原语,")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//只要存在,那么代码就会按照程序员写的顺序执行,并不会进行重排。")]),t._v("\n a "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hi"')]),t._v(" \n c "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里因为有了同步操作,所以 a = “hi” 在main goroutine看 也是 a= “hi” happens- before c <- 0")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("c "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里只要收到数据了,那么 c <- 0 肯定已经运行了,并且发送完毕了。")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("print")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("ul",[n("li",[n("p",[t._v("close 一个 channel 一定 happens- before 从关闭的 channel 中读取一个零值。我们都知道可以从一个 closed 的一个 channel 中读取零值是可以的,这里说明的是读零值这个过程一定在关闭这个操作之后。")])]),t._v(" "),n("li",[n("p",[t._v("一个 unbuffered 的 channle,读的准备好一定 happens-before 发准备好,意思是说,只有收数据准备好了,才能发,否则就会一直卡在发那个地方。")])]),t._v(" "),n("li",[n("p",[t._v("一个容量大于 0 为 m 的 channel,第 n 个接收,一定 happens- before 第 n+m 的发送,意思是说,如果容量满了,必须先拿出来一个才能往里面再塞进去一个。比如:n=1 m=2,第一个接收一定 happens- before 第三个发送,因为容量一共是 2,要想往里面塞进去第三个,"),n("strong",[t._v("必须拿出来一个并且拿出来这个操作完毕了")]),t._v(",才能发送第三个。")])])]),t._v(" "),n("h3",{attrs:{id:"mutex-rwmutex"}},[t._v("Mutex/RWMutex")]),t._v(" "),n("ul",[n("li",[t._v("第 n 次的 unlock 一定 happends- before 第 n+1 次 lock 方法的返回\n意思是说,只有先解锁才能再次加锁。")]),t._v(" "),n("li",[t._v("读写锁虽然有两把锁,但是不能同时使用,必须等待一个锁解锁后,另一个锁才能上锁,比如读锁解锁后才能再次上读锁,或者才能上写锁。")])]),t._v(" "),n("p",[t._v("互斥锁可以由 a goroutine 上锁,b goroutine 解锁")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" mu sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Mutex\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n s "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hi"')]),t._v("\n mu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里拥有了一个同步原语,所以 s = hi 一定发生在 mu.unlock() 之前。(相当于在一段锁的内部)")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n mu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n mu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里的lock 一定发生在 另一个goroutine 中f 的 mu.Unlock() 之后。")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("print")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h3",{attrs:{id:"waitgroup"}},[t._v("WaitGroup")]),t._v(" "),n("ul",[n("li",[t._v("wait 方法等到计数值归零才能返回 (运行完毕)")])]),t._v(" "),n("h3",{attrs:{id:"once"}},[t._v("Once")]),t._v(" "),n("ul",[n("li",[t._v("对于 once.Do(f) f 一定会在 do 方法返回之前执行。")])]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" once sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Once\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n s "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hi"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n once"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Do")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("print")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里 f 的执行完毕 一定在 Do()返回之前,所以一定能输出 s = hi")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h3",{attrs:{id:"atomic"}},[t._v("atomic")]),t._v(" "),n("p",[t._v("按照 atomic load / store 顺序来保证 happens - before 不过 go 官方并没有严格定义,直到目前为止并没有严格定义。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" b "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int32")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n atomic"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("StoreInt32")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n atomic"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("StoreInt32")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" atomic"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("LoadInt32")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n runtime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Gosched")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("atomic"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("LoadInt32")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h2",{attrs:{id:"issues"}},[t._v("issues")]),t._v(" "),n("p",[n("code",[t._v("使用channel实现一个互斥锁")])]),t._v(" "),n("p",[t._v("我们利用 channel 发送数据 happens- before 收到数据完毕这个特性来实现互斥锁。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Locker "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tch "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 初始状态是已经有一个")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewLocker")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Locker"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tch "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tch "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Locker"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tch"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" ch"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// lock 从缓存是1编程0,从chan中取出来数据")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("l "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Locker"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" l"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ch\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// unlock 将缓存从0变成1,向ch中放入数据。")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("l "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Locker"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tl"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ch "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("一个小小的经验:channel 拥有 buffer 比没有 buffer 常用很多。")]),t._v(" "),n("p",[t._v("完结。")]),t._v(" "),n("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),n("ul",[n("li",[t._v("https://go.dev/ref/mem")]),t._v(" "),n("li",[t._v("https://time.geekbang.org/column/article/307469")])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/109.a7608d9f.js b/assets/js/109.a7608d9f.js new file mode 100644 index 000000000..ee6e7438f --- /dev/null +++ b/assets/js/109.a7608d9f.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[109],{541:function(t,s,n){"use strict";n.r(s);var a=n(36),p=Object(a.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"go-语言实战项目并发优化"}},[t._v("go 语言实战项目并发优化")]),t._v(" "),n("p",[t._v("上文我们已经讲解了 goroutine,channel,并发原语,atomic,context,以及 go 的内存模型,内容还是比较多的,我们需要通过实战的项目的优化演进过程来更好的理解并发的最佳实践")]),t._v(" "),n("h2",{attrs:{id:"能不并发就不并发"}},[t._v("能不并发就不并发")]),t._v(" "),n("p",[t._v("并发是一个双刃剑,一方面它可以加速程序,另一方面它也增加了程序的复杂度,所以需要在并发和不并发之间做取舍,如果你发现你的程序在不使用并发的时候也能满足你的需求,答应我,不要使用并发,"),n("em",[n("strong",[t._v("累活脏活自己干,不要委派给另一个 goroutine 去做所谓的并发工作。")])])]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// bad")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("HandleFunc")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("w http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ResponseWriter"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("r "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Request"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Fprintln")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("w"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello wrold!"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("ListenAndServe")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('":8080"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n log"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Fatal")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("在这段代码中,委派一个 goroutine 去启动一个监听服务,又使用 select {} 去阻塞主 goroutine 的运行")]),t._v(" "),n("p",[t._v("确实,这可以满足需求")]),t._v(" "),n("p",[t._v("但是,委派一个后台 goroutine 去执行监听服务没有带来任何有利的收益,反而增加了代码的复杂度,所以我们应该取消委派,主 goroutine 去执行监听即可")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// better")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("HandleFunc")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("w http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ResponseWriter"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("r "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Request"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Fprintln")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("w"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello wrold!"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n \n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("ListenAndServe")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('":8080"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n log"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Fatal")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h2",{attrs:{id:"优先使用-channel-context-的方法去优雅关闭"}},[t._v("优先使用 channel + context 的方法去优雅关闭")]),t._v(" "),n("p",[t._v("核心就是把众多启动的 goroutine 改成 worker pool + context 的模型")]),t._v(" "),n("p",[t._v("在 channel 中我们讲过 worker pool 的实现,为什么一定要优先使用 worker pool?因为很多时候你启动了很多的 goroutine,不知不觉就会造成混乱,以及 goroutine 的泄露问题,我们使用 worker pool 的方式,可以很好的控制 goroutine 的并发数量,以及优雅的关闭 goroutine")]),t._v(" "),n("p",[t._v("看一个优秀的例子:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Tracker "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tch "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 作为工作池子让worker消费")]),t._v("\n\tstop "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewTracker")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Tracker "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Tracker"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tch"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\tstop"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Tracker"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Event")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx context"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Context"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" data "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ch "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" data"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("ctx"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Done")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" ctx"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Err")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Tracker"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Run")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" data "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ch "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 模拟消费")]),t._v("\n\t\ttime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("data"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// run 数据结束之后就可以发送信号了")]),t._v("\n\tt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("stop "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// shutDown 通过关闭ch 让 run中的range结束,进而再发送stop信号,让shutdown函数退出")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Tracker"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Shutdown")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx context"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Context"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ch"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("stop"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("ctx"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Done")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" \n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ttr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewTracker")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 开启两个消费者")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" tr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Run")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" tr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Run")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\n tr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Event")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Background")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"test"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ttr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Event")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Background")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"test2"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n tr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Event")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Background")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"test3"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ttr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Event")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Background")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"test4"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n tr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Event")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Background")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"tes5"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ttr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Event")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Background")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"test6"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\ttime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tctx"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" cancel "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" context"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithDeadline")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Background")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Now")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("cancel")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ttr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Shutdown")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h2",{attrs:{id:"使用方去决定是否并发"}},[t._v("使用方去决定是否并发")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("ListDirectory")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("dir "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n")])])]),n("p",[t._v("这是一个返回目录下所有文件路径的函数,它返回的是一个 channel,很明显,这跟题目所说的让使用方去决定是否并发是相违背的")]),t._v(" "),n("p",[t._v("因为在这个函数体内部,已经完成了一个 goroutine 的创建,但是作为使用方,我们并不能说一定要使用并发,而且你也无法控制发送的停止,假设你在这个函数内部使用一个 close 作为关闭的信号,也是有问题的,比如我想恢复读取,那么你已经 close 了,该如何继续开启呢?")]),t._v(" "),n("p",[t._v("还有一个问题,这个函数返回的是一个 channel,但是并没有返回 error,我们如果去判断 channel 的状态,并不能分辨是读取完毕 channel 被 close 还是出现 error,然后 channel 被关闭了,这就是二象性的问题,你不能设置二象性这种状态的函数,就跟刚才说的那样,你无法获悉真实的最精准的状态。")]),t._v(" "),n("p",[t._v("也就是说你的发送过程不够透明,调用方无法决定暂停,恢复读取这些行为")]),t._v(" "),n("p",[t._v("再提一个需求,我只需要读取的数据中的某些符合规定的路径,那么你如果作为一个黑箱的函数,完全无法做到定制化对不对")]),t._v(" "),n("p",[t._v("那么为什么不能设计成一个普通函数,然后在调用方再决定是否并发这个行为模式呢?")]),t._v(" "),n("p",[t._v("我们看一下 go 语言标准库中的用法")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[t._v("filepath"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Walk")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("root "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("fn filepath"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("WalkFunc"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n")])])]),n("p",[t._v("很明显这是一个实现了功能的普通函数,那么让我们分别看看它的普通模式和并发模式")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 普通模式")]),t._v("\n\nerr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" filepath"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Walk")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"."')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" info fs"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("FileInfo"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Printf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"prevent panic by handling failure accessing a path %q: %v\\n"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" path"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("IsDir")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" subDirToSkip "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Printf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"skipping a dir without errors: %+v \\n"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Name")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 可以看到我们返回的err是不同的err")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" filepath"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("SkipDir\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 定制需求")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" path "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"xxx"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Printf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"visited file or dir: %q\\n"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" path"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Printf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"error walking the path %q: %v\\n"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" tmpDir"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("那么再让我们看一下并发模式")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 并发模式")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\terr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" filepath"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Walk")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("root"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" info os"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("FileInfo"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// if the file is noe regular, it mean the file is done,you should return")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Mode")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("IsRegular")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t\tvalue "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" path\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),n("p",[t._v("解释一下这个 Walk 的函数本质,它传入一个路径和一个函数,这个函数拥有的参数是一个要输出的路径,一个文件信息和一个错误类型,返回一个错误类型,参数函数被执行的时候 walk 底层会将真实 path 值作为实际参数传入这个函数里,所以我们可以在 path 中,指定一个 channel 去作为流的方式导出数据。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// walk 的底层源码")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" name "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" names "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tfilename "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Join")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\tfileInfo"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("lstat")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("filename"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里将读取到的path值传入了 walkFn 函数里,来完成实际参数的赋值行为")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("walkFn")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("filename"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fileInfo"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" SkipDir "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\terr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("walk")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("filename"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fileInfo"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" walkFn"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("fileInfo"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("IsDir")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("||")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" SkipDir "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("可以看到这个函数的实现完成了基本的功能,既可以并发,又可以不并发")]),t._v(" "),n("h2",{attrs:{id:"必须让发送方决定-channel-的关闭"}},[t._v("必须让发送方决定 channel 的关闭")]),t._v(" "),n("p",[t._v("如果不能让发送方决定 channel 的关闭,而是不控制或者是接收方去控制,那么数据的丢失就不可避免了")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"sync"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n ch "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n wg "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("WaitGroup"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 发送者")]),t._v("\n wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Done")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n ch "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" i\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ch"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 发送方关闭channel")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 接收者")]),t._v("\n wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Done")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" data"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ok "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("ch"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" ok "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("data"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// channel已关闭")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n \n wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Wait")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("那么让我看看一下几个反例")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 反例1:接收方关闭channel")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n ch "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n ch "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" i \n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("ch"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 当你接收方去关闭channel的时候")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 发送方如果不知道的话,向一个close的channel发送数据会panic的")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" xx "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ch"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 接收方关闭channel")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 反例2:没有关闭channel")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n ch "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n \n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n ch "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" i\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里 ok 永远都不会false,所以 这里会死循环")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" data"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ok "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("ch"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" ok "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("data"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" \n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])])}),[],!1,null,null,null);s.default=p.exports}}]); \ No newline at end of file diff --git a/assets/js/11.25dd6abf.js b/assets/js/11.25dd6abf.js new file mode 100644 index 000000000..e71616b20 --- /dev/null +++ b/assets/js/11.25dd6abf.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[11],{431:function(t,s,a){t.exports=a.p+"assets/img/ringwaiter.c4e44723.png"},539:function(t,s,a){"use strict";a.r(s);var n=a(36),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"同步原语"}},[t._v("同步原语")]),t._v(" "),n("p",[t._v("传统同步原语是 go 提供的相对底层的同步机制,它更加灵活,但是同时也更加复杂,如果可能的话,我们应该尽量使用 csp 的并发模型,使用 channel 去代替传统同步原语。")]),t._v(" "),n("p",[t._v("本单元讲的传统同步原语,channel 和 contex 也属于同步原语,他们属于 csp 的并发模型,所以他们会单独为列。")]),t._v(" "),n("p",[t._v("下面讲一下这么多同步原语 (也叫并发原语) 的基本功能:")]),t._v(" "),n("ul",[n("li",[n("code",[t._v("sync.Mutex, sync.RWMutex")]),t._v(" 共享资源,避免数据竞争 (data race)")]),t._v(" "),n("li",[n("code",[t._v("sync.Waitgroup, channel")]),t._v(" 任务编排,各个 goroutine 所代表的任务的前后顺序关系")]),t._v(" "),n("li",[n("code",[t._v("channel")]),t._v(" 传递消息")])]),t._v(" "),n("p",[t._v("这是基本的划分,当然这个划分还不严谨,但是你只需要知道,这些属于最常见的同步原语,以及他们最常见的功能。")]),t._v(" "),n("h2",{attrs:{id:"sync-mutex"}},[t._v("sync.Mutex")]),t._v(" "),n("p",[t._v("下面介绍的众多并发原语,甚至下一章的 channel,都使用了这个核心内容,它就是 "),n("code",[t._v("sync.Mutex")]),t._v(",它是所有同步原语包含 channel 的底层核心。")]),t._v(" "),n("p",[t._v("Mutex 也就是互斥锁,它主要是为了解决多线程下数据的竞争问题,所以互斥锁是同步原语的最底层最核心的组件")]),t._v(" "),n("p",[t._v("让我们看几个场景")]),t._v(" "),n("ol",[n("li",[n("p",[t._v("计数器,多个线程去更新一个计数器的时候,如果不加锁,就会出现数据的错误,本来你只加上了 1,正当你读的时候你发现别的线程也加上了 1,所以导致读取的错误")])]),t._v(" "),n("li",[n("p",[t._v("秒杀服务,这也是一个常见的场景,如果不加锁,就会导致超卖的情况出现")])]),t._v(" "),n("li",[n("p",[t._v("往一个 buffer 中传入数据,如果不加锁,数据的顺序就会发生乱序")])])]),t._v(" "),n("h3",{attrs:{id:"临界区"}},[t._v("临界区")]),t._v(" "),n("p",[t._v("临界区的概念是指在多线程的时候,临界区域的内容会被不同的线程去获取和释放,这个区域会发生数据的争夺问题。")]),t._v(" "),n("p",[t._v("这个区域因为会发生争夺,所以会被保护起来,可以这么说,临界区是"),n("strong",[t._v("多线程中整体数据中的共享部分")])]),t._v(" "),n("p",[t._v("所以临界区是要保护的区域,"),n("strong",[t._v("一次只能让一个线程去使用")])]),t._v(" "),n("p",[t._v("所以 sync.Mutex 就是用来保护临界区的,它可以保证临界区的互斥")]),t._v(" "),n("p",[t._v("共享资源通常是某个变量,例如临界区是变量 count,临界区操作是 count++,只要在临界区前面获取\n锁,在离开临界区的时候释放锁,就能解决 data race 的问题。")]),t._v(" "),n("h3",{attrs:{id:"sync-mutex-基础操作"}},[t._v("sync.Mutex 基础操作")]),t._v(" "),n("p",[t._v("我们现看一下它的基础使用功能:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" mu sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Mutex\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n mu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" mu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 跨goroutine 加解锁")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("age1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" mu sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Mutex\n\tmu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" mu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" mu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\ttime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hi there"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tmu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("sync.Mutex 即为互斥锁,规则是:")]),t._v(" "),n("ul",[n("li",[t._v("锁的加锁和解锁可以跨 goroutine 使用,比如 a goroutine 加锁,在 b goroutine 将锁解开。")]),t._v(" "),n("li",[t._v("只有现解锁才能继续上锁,happens-before 就是:"),n("em",[n("strong",[t._v("n 次解锁一定 happens-before n+1 次加锁")])])])]),t._v(" "),n("h3",{attrs:{id:"sync-locker-接口"}},[t._v("sync.Locker 接口")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Locker "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("Mutex 就实现了这个 Locker 接口")]),t._v(" "),n("p",[t._v("Locker 定义了锁的基本方法,加锁 + 解锁")]),t._v(" "),n("h3",{attrs:{id:"什么是-data-race-的本质"}},[t._v("什么是 data race 的本质")]),t._v(" "),n("p",[t._v("我们常说 data race 的情况,是多线程同时对于某块内存进行数据的变更,那么问题来了,这个地方谈到的同时,是真的物理层面的同时还是近似同时?")]),t._v(" "),n("p",[t._v("关于 data race 中的 “同时” 通常是"),n("strong",[t._v("指逻辑上的同时或近似同时,而不是物理层面严格的同一时刻。")])]),t._v(" "),n("p",[t._v("主要原因有以下几点:")]),t._v(" "),n("ul",[n("li",[t._v("现代 CPU 中,同一时刻真正执行指令的只有一个核心。不同核心之间以及同一核心的不同执行周期之间,不存在物理层面严格的同步。")]),t._v(" "),n("li",[t._v("即使在单核 CPU 上,由于指令流水线、内存缓存、分支预测等机制,实际执行顺序也可能与代码顺序不一致,很难定义物理层面严格的同步。")]),t._v(" "),n("li",[t._v("不同线程之间进行切换的时间间隔非常小 (几十到几百纳秒),对程序逻辑而言可以视为同时进行。")]),t._v(" "),n("li",[t._v("要构成 data race,不同线程对同一地址的访问之间间隔不能太长,必须在一个指令/操作的启始和完成之间,所以也符合逻辑上的近似同时。")])]),t._v(" "),n("p",[t._v("所以,data race 中的 “同时” 就是指逻辑上近似同时,或者无法确定准确执行顺序的情况,而不是物理层面严格同一时刻。这种近似同时从程序角度就是可能造成冲突,需要进行同步处理。")]),t._v(" "),n("h3",{attrs:{id:"检测-data-race-的工具"}},[t._v("检测 data race 的工具")]),t._v(" "),n("p",[t._v("并发问题不是一定能肉眼看出来的,如果只是基础的,容易看出来的也就罢了,但是很多隐藏的 data race 问题必须使用专业的工具去鉴别,go 语言提供了 "),n("code",[t._v("-race")]),t._v(" 功能,在编译,测试,run 的时候,会自动检测到 data race 问题,并且给出详细的错误信息。")]),t._v(" "),n("div",{staticClass:"language-bash extra-class"},[n("pre",{pre:!0,attrs:{class:"language-bash"}},[n("code",[t._v(" go run -race main.go\n")])])]),n("p",[t._v("我们看一个例子")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tvalue "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\tvalue "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n\t\t "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\ttime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("本能的你会以为能输出 10000,但是结果确实 9000 多,而且还不一定,这是为啥呢?")]),t._v(" "),n("p",[t._v("因为你以为 ++ 操作是原子操作,其实并不是。")]),t._v(" "),n("p",[t._v("++ 操作分为三个步骤")]),t._v(" "),n("ul",[n("li",[t._v("获取 value 值")]),t._v(" "),n("li",[t._v("值+1")]),t._v(" "),n("li",[t._v("将新值写回")])]),t._v(" "),n("p",[t._v("这其实是三个步骤,10000 个线程,假如同时有 10 个去读了这个 value,在他们看来都是初始值是 0,然后他们+1,然后写回去了结果 value 是 1,相当于 10 个 goroutine 都去写,本来应该是 10,但是赋值回去都变成了 1")]),t._v(" "),n("p",[t._v("这个时候,如果你使用 run -race 就能检测出来")]),t._v(" "),n("div",{staticClass:"language-bash extra-class"},[n("pre",{pre:!0,attrs:{class:"language-bash"}},[n("code",[t._v("go1 go run -race main.go \n"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v("\nWARNING: DATA RACE\nRead at 0x00c00010a018 by goroutine "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("8")]),t._v(":\n main.main.func1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n /Users/shgopher/Desktop/1/go1/main.go:23 +0x2c\n\nPrevious "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("write")]),t._v(" at 0x00c00010a018 by goroutine "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("6")]),t._v(":\n main.main.func1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n /Users/shgopher/Desktop/1/go1/main.go:23 +0x3c\n\nGoroutine "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("8")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("running"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" created at:\n main.main"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n /Users/shgopher/Desktop/1/go1/main.go:22 +0x48\n\nGoroutine "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("6")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("finished"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" created at:\n main.main"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n /Users/shgopher/Desktop/1/go1/main.go:22 +0x48\n"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("9957")]),t._v("\nFound "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" data race"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("exit")]),t._v(" status "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("66")]),t._v("\n")])])]),n("p",[t._v("多线程多某个区域的内存进行同时 (或者近似同时) 操作,这就是数据竞争")]),t._v(" "),n("p",[t._v("使用这个内置工具有个很大的缺陷,就是只有在数据真的执行中发生了数据竞争才能被发现,并且,使用-race 会影响编译的程序执行速度")]),t._v(" "),n("p",[t._v("如果我们使用 "),n("code",[t._v("go tool compile -race -S x.go")]),t._v(" 的时候就发现使用-race 之后,编译的时候 go 编译器往代码中加入了很多运行时监控代码,"),n("strong",[t._v("这些运行时的监控代码")]),t._v("会影响性能")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[t._v("\t"),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x001c")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("00028")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\tPCDATA\t$"),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" $"),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x001c")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("00028")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\tCALL\truntime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("racefuncenter")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("SB"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x0020")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("00032")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\tMOVD\t$"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("SB"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" R0\n\t"),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x0028")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("00040")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\tCALL\truntime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("newobject")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("SB"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),n("p",[n("code",[t._v("runtime.xx")]),t._v(" 的代码就是添加的运行时监控")]),t._v(" "),n("blockquote",[n("p",[t._v("小知识,使用 go tool compile 的时候不要加入第三方库,标准库也不行,编译工具只能编译本文件,跟 go build 那种能查找依赖树的方式不同")])]),t._v(" "),n("h3",{attrs:{id:"将互斥锁嵌入到结构体中"}},[t._v("将互斥锁嵌入到结构体中")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" MyAge "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n mu sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Mutex\n value "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("或者直接嵌入")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" MyAge "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Mutex\n value "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" age MyAge\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\tage"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" age"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\tage"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("value "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\ttime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("age"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("value"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h3",{attrs:{id:"sync-mutex-互斥锁的实现原理"}},[t._v("sync.Mutex 互斥锁的实现原理")]),t._v(" "),n("p",[t._v("go 语言互斥锁的实现非常简单,只有这一个结构体就是核心:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Mutex "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n state "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int32")]),t._v("\n sema "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint32")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("blockquote",[n("p",[t._v("在阅读下面互斥锁的几个阶段之前,建议先读一下 G:M:P 模型")])]),t._v(" "),n("h4",{attrs:{id:"互斥锁演变的四个阶段一-简单实现"}},[t._v("互斥锁演变的四个阶段一:简单实现")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 最初的代码")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Mutex "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n key "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int32")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 锁是否被持有的标志,1 被持有,0 没有被持有")]),t._v("\n sema "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint32")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 锁的具体状态,此信号量具有高级语意,用来控制goroutine的状态")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// compare and swap 操作:val 和 old 进行对比,如果相同,使用new去替代 val的值")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("cas")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("val "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int32")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" old"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("new")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int32")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// val 数据添加一个 delta 值,返回新的值")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("xadd")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("val "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int32")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" delta "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int32")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("new")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int32")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("val\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("cas")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("val"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v("delta"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v("delta\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("panic")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"unreached"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("m "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("mutex"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("xadd")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("key"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 标识+1 ,如果等于1 获取到锁")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" \n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里就是说,当key >1 的时候,我们通知goroutine休眠等待锁")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 只有key 等于 1 才能算获取锁")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("semacquire")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sema"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("m "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("mutex"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("xadd")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("key"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 标识-1 ,如果等于0 表示没有其它等待者")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这个函数是汇编语言写的,上面那个 semacquire 也是")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("semrelease")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("sema"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 唤醒等待锁的其它的goroutine中的一个")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("可以看到,这种简单的实现,完全没有考虑任何的情况,仅仅是简单的加锁和解锁,不考虑 goroutne 的任何情况,就是随机的,随机的获取锁然后解锁。")]),t._v(" "),n("p",[t._v("注意 go 语言的锁可以 a 加 b 解,所以一定要谁加锁就谁解锁,不然就无法构成互斥锁这个概念了。")]),t._v(" "),n("h4",{attrs:{id:"互斥锁演变的四个阶段二-优先新-goroutine"}},[t._v("互斥锁演变的四个阶段二:优先新 goroutine")]),t._v(" "),n("p",[t._v("在这个演变中,go 的互斥锁调度会优先将锁权分给新创建的 goroutine")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 1")]),t._v("\n mutexLock "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<<")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("iota")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 2")]),t._v("\n mutexWoken\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 2")]),t._v("\n mutexWaiterShift "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("iota")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Mutex "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n state "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int32")]),t._v("\n sema "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint32")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("p",[t._v("state 的内容变成了三个:")]),t._v(" "),n("ul",[n("li",[t._v("第一个字段:mutexWaiters 阻塞等待的数量")]),t._v(" "),n("li",[t._v("第二个字段:mutexWoken 唤醒标记")]),t._v(" "),n("li",[t._v("第三个字段:mutexLocked 持有锁的标记")])]),t._v(" "),n("p",[t._v("我们先前知道,如果想要获取锁的 goroutine 没有机会获取到锁,就会进行休眠,但是在锁释放唤醒之后,它并不能像先前一样直接获取到锁,还是要和正在请求锁 goroutine 进行竞争。这会给后来请求锁的 goroutine 一个机会,也让 CPU 中正在执行的 goroutine 有更多的机会获取到锁,在一定程度上提高了程序的性能。")]),t._v(" "),n("p",[t._v("在这次演变中,go 的调度器会将新的 goroutine 给赋予锁,因为新的 goroutine 就是正在 cpu 执行片段中执行的 goroutine,这个时候将锁给他们无疑是效率最高的。")]),t._v(" "),n("h4",{attrs:{id:"互斥锁演变的四个阶段三-多给机会-优先新-goroutine-以及被唤醒的-goroutine"}},[t._v("互斥锁演变的四个阶段三:多给机会,优先新 goroutine 以及被唤醒的 goroutine")]),t._v(" "),n("p",[t._v("如果新来的 goroutine 或者是被唤醒的 goroutine 首次获取不\n到锁,它们就会通过"),n("strong",[t._v("自旋")]),t._v(" (spin,通过循环不断尝试) 的方式,尝试检查锁是否被释放。在尝试"),n("strong",[t._v("一定")]),t._v("的自旋次数后,再执行原来的逻辑。")]),t._v(" "),n("h4",{attrs:{id:"互斥锁演变的四个阶段四-在第三个阶段的基础上引入饥饿模式"}},[t._v("互斥锁演变的四个阶段四:在第三个阶段的基础上引入饥饿模式")]),t._v(" "),n("p",[t._v("为什么会加入饥饿模式呢?就是因为如果都优先给新的 goroutine,那么等待队列中的 goroutine 就永远不会被执行,所以引入了饥饿模式,优先执行等待中的 goroutine\n然后新的 gorontine 就被添加到了等待队列中的队尾,这个时期称之为饥饿模式,因为新的 goroutine 基本上都要在 cpu 的时间片段中执行,所以在这个模式下,执行效率反而是底下的,因为正在执行的 goroutine 被强行放到队尾了。")]),t._v(" "),n("p",[t._v("当等待的队首的 goroutine 等待时间超过 1ms 就会进入这个模式")]),t._v(" "),n("p",[t._v("当队首的 goroutine 等待时间小于 1ms 或者已经执行到队尾了,那么这个模式就会从饥饿模式改为正常的模式")]),t._v(" "),n("h3",{attrs:{id:"sync-mutex-易错的几种场景"}},[t._v("sync.Mutex 易错的几种场景")]),t._v(" "),n("h4",{attrs:{id:"lock-unlock-不是成对出现"}},[t._v("Lock/Unlock 不是成对出现")]),t._v(" "),n("p",[t._v("因为 go 语言中,互斥锁是无法获取 goroutine 的信息的,所以存在 a 锁 b 解的情况,即:a goroutine 上了锁,b goroutine 给解开了。")]),t._v(" "),n("p",[t._v("如果你不是为了实现锁,是为了任务编排,那么可以这么做。")]),t._v(" "),n("p",[t._v("如果是为了锁,请不要这么做,因为这么做的后果就是这将不能形成锁这个概念")]),t._v(" "),n("p",[t._v("或者说当你使用了 lock 的时候忘记 unlock 了,那么最终都会导致系统走向失败")]),t._v(" "),n("h4",{attrs:{id:"copy-已经使用的-mutex"}},[t._v("Copy 已经使用的 Mutex")]),t._v(" "),n("p",[t._v("go 语言的 mutex 使用 state 字段去表示锁的含义,所以当你 copy 一个锁的时候,实际上已经 copy 了这个锁的状态,这将导致错误的结果")]),t._v(" "),n("p",[t._v("go 语言的同步原语众多,使用的底层都是 mutex (包括 channel),所以说,不仅仅是 mutex 不能使用 copy,其它的同步原语都不能。")]),t._v(" "),n("h4",{attrs:{id:"重入"}},[t._v("重入")]),t._v(" "),n("p",[t._v("所谓重入,就是多次上锁,注意这里是拥有锁的这个线程去请求这把锁")]),t._v(" "),n("p",[t._v("go 语言不支持重入,系统会 panic,这种重入锁无法实现也跟 go 语言的互斥锁没有记录使用它的 goroutine 有关系")]),t._v(" "),n("p",[t._v("那么如果 go 语言也实现一个重入锁,核心就是将持有锁的 goroutine 的 id 记录下来")]),t._v(" "),n("h4",{attrs:{id:"死锁"}},[t._v("死锁")]),t._v(" "),n("p",[t._v("当多线程的情况下,多个线程陷入了争抢资源的情况,当他们都陷入了停滞状态,或者阻塞状态的时候,就会发生死锁,deaadlock")]),t._v(" "),n("p",[t._v("一般来讲,当你发现系统多个线程都堵死的时候,就会发生死锁情况了,但是通常发生死锁是发生在满足这四个情况下")]),t._v(" "),n("ul",[n("li",[t._v("互斥:资源具有排他性,只能有一个 goroutine 访问")]),t._v(" "),n("li",[n("strong",[t._v("持有和等待")]),t._v(":goroutine 持有资源,并还在请求其它资源")]),t._v(" "),n("li",[t._v("不可剥夺:资源只有被它持有的 goroutine 释放")]),t._v(" "),n("li",[n("strong",[t._v("环路等待")]),t._v(":发生了环路等待事件,下面这个案例可以解释这个理论")])]),t._v(" "),n("p",[t._v("举一个案例")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"sync"')]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"time"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" wg sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("WaitGroup\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" mu1 sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Mutex\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" mu2 sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Mutex\n wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Done")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n mu1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" mu1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n mu2"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" mu2"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Done")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n mu2"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" mu2"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n mu1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" mu1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Wait")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("在这个案例中,mu1 和 mu2 代表两个资源,两个 goroutine 在争夺这两个资源,下面我们盘点一下上文说的四个理论知识:")]),t._v(" "),n("ul",[n("li",[t._v("互斥性:资源只能被一个 goroutine 持有")]),t._v(" "),n("li",[t._v("持有和等待,一个 goroutine 获取了一把锁,还想获取第二把")]),t._v(" "),n("li",[t._v("不可剥夺,持有锁的 goroutine 释放锁后,其他 goroutine 不能再获取该锁")]),t._v(" "),n("li",[t._v("环路等待,两个 goroutine 陷入了环路这个概念总,第一个先持有 mu1,第二个 goroutine 先持有 mu2,他们又分别要获取另一个锁,所以陷入了环路等待中")])]),t._v(" "),n("p",[n("img",{attrs:{src:a(431),alt:"h"}})]),t._v(" "),n("p",[t._v("所以这个案例中,发生了死锁")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[t._v("fatal "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" all goroutines are asleep "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v(" deadlock"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("\n\ngoroutine "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("semacquire"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\nsync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("runtime_Semacquire")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0xc0000a4020")]),t._v("?"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("usr"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("local"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("faketime"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("src"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("runtime"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("sema"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("62")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x25")]),t._v("\nsync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("WaitGroup"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Wait")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x0")]),t._v("?"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("usr"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("local"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("faketime"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("src"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("sync"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("waitgroup"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("116")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x48")]),t._v("\nmain"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("tmp"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("sandbox1712910389"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("prog"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("31")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x125")]),t._v("\n\ngoroutine "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("17")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Mutex"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Lock"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\nsync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("runtime_SemacquireMutex")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x1")]),t._v("?"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x58")]),t._v("?"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x459218")]),t._v("?"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("usr"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("local"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("faketime"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("src"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("runtime"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("sema"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("77")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x25")]),t._v("\nsync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Mutex"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("lockSlow")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0xc0000a2028")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("usr"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("local"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("faketime"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("src"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("sync"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("mutex"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("171")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x15d")]),t._v("\nsync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Mutex"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("usr"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("local"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("faketime"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("src"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("sync"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("mutex"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("90")]),t._v("\nmain"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("main"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("func1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("tmp"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("sandbox1712910389"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("prog"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("20")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0xd1")]),t._v("\ncreated by main"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("main in goroutine "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("tmp"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("sandbox1712910389"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("prog"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("15")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0xb9")]),t._v("\n\ngoroutine "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("18")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Mutex"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Lock"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\nsync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("runtime_SemacquireMutex")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x1")]),t._v("?"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x58")]),t._v("?"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x459218")]),t._v("?"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("usr"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("local"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("faketime"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("src"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("runtime"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("sema"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("77")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x25")]),t._v("\nsync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Mutex"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("lockSlow")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0xc0000a2020")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("usr"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("local"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("faketime"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("src"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("sync"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("mutex"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("171")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x15d")]),t._v("\nsync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Mutex"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("usr"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("local"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("faketime"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("src"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("sync"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("mutex"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("90")]),t._v("\nmain"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("main"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("func2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("tmp"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("sandbox1712910389"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("prog"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("28")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0xd1")]),t._v("\ncreated by main"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("main in goroutine "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("tmp"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("sandbox1712910389"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("prog"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("23")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x119")]),t._v("\n\n")])])]),n("h3",{attrs:{id:"sync-mutex-扩展"}},[t._v("sync.Mutex 扩展")]),t._v(" "),n("p",[t._v("这里主要讲解如何对 sync.Mutex 进行功能扩展")]),t._v(" "),n("p",[t._v("基本的原理就是"),n("strong",[t._v("将 sync.Mutex 嵌入到一个新的接口体中,然后重载 Lock 和 Unlock 的方法进行改造")])]),t._v(" "),n("h4",{attrs:{id:"改造一个重入锁"}},[t._v("改造一个重入锁")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" RecursiveMutex "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Mutex\n owner "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int64")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// goroutine id")]),t._v("\n recursion "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int64")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//当前goroutine重入次数")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("lock 操作")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("m "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("RecursiveMutex"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 第三方包,目的是获取正在获取锁的lock 操作的 runtime id")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// github.com/petermattis/goid")]),t._v("\n gid "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" goid"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 判断当前goroutine是否是要重入的goroutine")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" atomic"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("LoadInt64")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("owner"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" gid "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 重入指标+1")]),t._v("\n atomic"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("AddInt64")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("recursion"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Mutex"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 获取得到锁的goroutine的id")]),t._v("\n atomic"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("StoreInt64")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("owner"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" gid"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n atomic"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("StoreInt64")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("recursion"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("unlock 操作")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("m "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("RecursiveMutex"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n gid "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" goid"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 非持有锁的goroutine去释放锁直接panic")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" atomic"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("LoadInt64")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("owner"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" gid "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("panic")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"wrong"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("owner"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("gid"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 重入指标-1")]),t._v("\n atomic"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("StoreInt64")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("recursion"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("recursion "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" atomic"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("LoadInt64")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("recursion"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 持有的goroutine还没有全部unlock")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" \n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 将 重入指标设置为-1")]),t._v("\n atomic"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("StoreInt64")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("recursion"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Mutex"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h2",{attrs:{id:"sync-rwmutex"}},[t._v("sync.RWMutex")]),t._v(" "),n("p",[t._v("上文我们提到了互斥锁,互斥锁是真的锁,不论是读还是写都只能有一个 goroutine 去操作,所以说互斥锁才是真的锁,那么我们这里讲的读写锁是什么含义呢?")]),t._v(" "),n("p",[t._v("读写锁:允许多个 goroutine 同时去读一个数据,但是这个时候是不允许写的;只允许一个 goroutine 去写一个数据,并且不允许其它 goroutine 去读这个数据。")]),t._v(" "),n("p",[t._v("所以,读写锁跟互斥锁相比,把读的权限给增大了,但是写的权限不变。")]),t._v(" "),n("p",[t._v("当我们遇到一个读多写少的场景,那么使用读写锁的效率要比互斥锁的效率高的多。")]),t._v(" "),n("p",[t._v("读写锁拥有以下几个方法:")]),t._v(" "),n("ul",[n("li",[t._v("Lock/Unlock:写操作时加的锁")]),t._v(" "),n("li",[t._v("Rlock/RUnlock:读操作时加的锁")]),t._v(" "),n("li",[t._v("Rlocker:为读操作返回一个 Locker 接口的对象")])]),t._v(" "),n("p",[t._v("RWMutex 跟 mutex 一样,初始值都是未加锁的状态,当然他们都是有状态的结构体,所以也不能复制锁,因为初始值就是未加锁,所以直接声明即可。")]),t._v(" "),n("p",[t._v("总结一下读写锁的几个注意事项")]),t._v(" "),n("ul",[n("li",[t._v("上文提到了,不可复制")]),t._v(" "),n("li",[t._v("lock 和 unlock 要成对出现;Rlock 和 RUnlock 也要成对出现")])]),t._v(" "),n("p",[t._v("下面我们看一下,使用读写锁造成的死锁问题")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这个写法出问题的原因是:读锁还没结束就开启了写锁")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" mu sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("RWMutex\n\n\tmu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("RLock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tmu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tmu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tmu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("RUnlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("出现死锁的原因就是出现了环形等待,读锁等待写锁解锁,写锁等待读锁解锁")]),t._v(" "),n("h2",{attrs:{id:"sync-waitgroup"}},[t._v("sync.WaitGroup")]),t._v(" "),n("p",[t._v("它的作用是任务的编排")]),t._v(" "),n("p",[t._v("waitgroup 一共有三个方法:")]),t._v(" "),n("ul",[n("li",[t._v("Add(delta int):增加 delta 个任务")]),t._v(" "),n("li",[t._v("Done():任务完成,减少一个任务")]),t._v(" "),n("li",[t._v("Wait():阻塞等待,直到所有任务完成")])]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" wg sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("WaitGroup\n wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Done")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Wait")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("wg "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("WaitGroup"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Done")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h3",{attrs:{id:"常见错误"}},[t._v("常见错误")]),t._v(" "),n("ul",[n("li",[t._v("add 的时候数值传入负值")]),t._v(" "),n("li",[t._v("done 的次数超过 add 中的次数")]),t._v(" "),n("li",[t._v("在 add 之前调用了 wait")]),t._v(" "),n("li",[t._v("当前一个 waitgroup 还没有完结就开始重用了 waitgroup,也就是说,必须等到上一轮的 wait 执行完毕了才能开启新的一轮")])]),t._v(" "),n("p",[t._v("最后这个错误我们看一个案例:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" wg sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("WaitGroup\n wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Done")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// AA")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Wait")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("在这个案例中,AA 处的 add 行为有可能发生在主 goroutine 之后,那么相当于一轮未结束又开启了新的一轮,就会发生 panic 行为")]),t._v(" "),n("p",[t._v("当然了,我不是说 add 只能调用一次,但是 add 虽然能调用多次,但是不能发生在 wait 之后")]),t._v(" "),n("p",[t._v("正确的多次调用 add 的案例:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" wg sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("WaitGroup\n\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Done")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Wait")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("注意,我们这里每次循环都调用了一次 add,但是 add 的调用始终发生在 wait 之前,这还是属于同一轮的多次 add 调用,这符合 waigroup 的规定")]),t._v(" "),n("h2",{attrs:{id:"singleflight"}},[t._v("SingleFlight")]),t._v(" "),n("blockquote",[n("p",[t._v("著名缓存库 "),n("a",{attrs:{href:"https://github.com/golang/groupcache",target:"_blank",rel:"noopener noreferrer"}},[t._v("groupcache"),n("OutboundLink")],1),t._v(" 就使用了 singleflight 去通过缓存来减少后端查询数据库的请求")])]),t._v(" "),n("p",[t._v("在处理多个 goroutine 同时调用同一个函数的时候,只让一个 goroutine 去调用这个函数,等到这个 goroutine 返回结果的时候,再把结果返回给这几个同时调用的 goroutine")]),t._v(" "),n("p",[t._v("在面对多个 goroutine 并发去读一个数据的时候,使用 SingleFlight 可以大大降低请求量,从 n 的请求量降低到 1,比如在秒杀的场景下,n 个 goroutine 去请求数据,那么我们使用 SingleFlight 就能大大提高读的性能")]),t._v(" "),n("p",[t._v("SingleFlight 提供了三个公开方法:")]),t._v(" "),n("ul",[n("li",[n("p",[t._v("func (g *Group) Do(key string,fn func() (interface {},error)) (v interface {},err error,shared bool)")])]),t._v(" "),n("li",[n("p",[t._v("func (g *Group) DoChan(key string,fn func() (interface {},error)) <-chan Result")])]),t._v(" "),n("li",[n("p",[t._v("func (g *Group) Forget(key string)")])]),t._v(" "),n("li",[n("p",[t._v("Do:Do 执行并返回函数的结果,同样 key 值下的只能有一个 goroutine 持有的 do 方法会执行,其它的都会等待,直到唯一一个执行完毕有了结果,那么大家 (指都执行 do 的众多 goroutine) 都有了结果")])]),t._v(" "),n("li",[n("p",[t._v("DoChan:跟 do 类似,返回值是一个 channel")])]),t._v(" "),n("li",[n("p",[t._v("Forget:告诉这个 group,忘记 key,之后,这个 key 请求回执行 f 函数,而不是等待前一个未完成的 fn 函数的结果")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[t._v(" sf "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("SingleFlight"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" \n\n sf"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Do")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"param"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ...function logic")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" result \n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n \tsf"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Do")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"param"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n \t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//...")]),t._v("\n \t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" result\n \t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n \t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 对于同一个key 下(这里是param)这些动作只执行一次,后续注册的函数会直接返回第一个函数的结果。")]),t._v("\n \t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//")]),t._v("\n\n\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 后续想重新执行")]),t._v("\n sf"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Forget")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"param"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n\n sf"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Do")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"param"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这次会再次执行函数逻辑")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])])]),t._v(" "),n("p",[t._v("下面我们看一下 DoChan 的基本使用方法:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"golang.org/x/sync/singleflight"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tg "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("new")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("singleflight"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Group"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\tblock "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\tres1c "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" g"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("DoChan")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"key"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n\t\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("block\n\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"func 1"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\tres2c "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" g"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("DoChan")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"key"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n\t\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("block\n\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"func 2"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("block"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\tres1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("res1c\n\n\tres2 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("res2c\n\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 使用相同的key执行的函数共享结果")]),t._v("\n\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Shared:"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" res2"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Shared"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v('// 只有第一个函数被执行:它使用"key"被注册和启动,在第二个函数被注册相同的key之前就完成了执行。')]),t._v("\n\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Equal results:"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" res1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Val"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" res2"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Val"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Result:"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" res1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Val"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("div",{staticClass:"language-bash extra-class"},[n("pre",{pre:!0,attrs:{class:"language-bash"}},[n("code",[t._v("Shared: "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),t._v("\nEqual results: "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),t._v("\nResult: func "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n")])])]),n("p",[t._v("g.DoChan / g.Do 都会在内部会启动一个新的 goroutine 来执行传入的函数。")]),t._v(" "),n("p",[t._v("g.DoChan 的签名如下:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("g "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Group"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("DoChan")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("key "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fn "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" Result\n")])])]),n("p",[t._v("它接受一个 key 和一个函数 fn,并返回一个 Result 类型的 channel。")]),t._v(" "),n("p",[t._v("在 g.DoChan 内部,会首先根据 key 在内部 map 中查找是否已经有相同 key 的函数在执行:")]),t._v(" "),n("ul",[n("li",[t._v("如果存在,则直接返回已有的 Result channel")]),t._v(" "),n("li",[t._v("如果不存在,则启动一个新的 goroutine 执行 fn 函数,并将 Result 发送到返回的 channel 中")])]),t._v(" "),n("p",[t._v("所以 g.DoChan 会确保对于相同的 key,最多只有一个 goroutine 在执行 fn,后续的调用会直接复用已有的结果。")]),t._v(" "),n("p",[t._v("这就实现了 key 去重重复执行的语义。")]),t._v(" "),n("p",[t._v("所以,g.DoChan 会隐式地启动 goroutine 来运行函数,这是它实现并发和协调的基础。")]),t._v(" "),n("p",[t._v("我们在处理缓存击穿的问题时,通常采用 singleflight 会有比较好的适用,所谓缓存击穿就是大量请求在请求一个 key 值时,key 值失效了,大量数据开始请求数据库,使用 singleflight 时,大量数据只需要一次请求,完美解决了缓存击穿问题")]),t._v(" "),n("blockquote",[n("p",[t._v("缓存击穿:指一个 key 非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个 key 在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像弹丸穿透目标一样。当某个 key 在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存,会对数据库造成压力。")])]),t._v(" "),n("blockquote",[n("p",[t._v("缓存穿透:指查询一个数据库一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。")])]),t._v(" "),n("blockquote",[n("p",[t._v("缓存雪崩:指在某一个时间段,缓存集中过期失效。所有访问都落到数据库上,对数据库造成巨大压力。这通常因为缓存服务器宕机或缓存时效过短导致,可通过服务器保护或增大缓存过期时间来避免。")])]),t._v(" "),n("h2",{attrs:{id:"cyclicbarrier"}},[t._v("cyclicBarrier")]),t._v(" "),n("blockquote",[n("p",[t._v("github.com/marusama/cyclicbarrier")])]),t._v(" "),n("p",[t._v("允许一组 goroutine 彼此等待,到达一个共同的执行点。同时,因为它可\n以被重复使用,所以叫循环栅栏")]),t._v(" "),n("p",[t._v("基本用法就是 Await 方法,等待所有的参与者到达,到达了就往下走,然后开始新的循环")]),t._v(" "),n("p",[t._v("那么看起来很像 waitgroup,那么为什么不使用 wg 呢?")]),t._v(" "),n("p",[t._v("在一种场景下 wg 通常很难使用,也就是循环这个含义,因为你需要在 wait 之后再次调用 add 方法充值,然后继续 done wait,麻烦,并且万一在继续 add 的时候发生了并发问题就跟灾难了")]),t._v(" "),n("ul",[n("li",[n("p",[t._v("WaitGroup 更适合用在 “一个 goroutine 等待一组 goroutine 到达同一个执行点” 的场景中,或者是不需要重用的场景中。")])]),t._v(" "),n("li",[n("p",[t._v("CyclicBarrier 更适合用在 “固定数量的 goroutine 等待同一个执行点” 的场景中,而且在放行 goroutine 之后,CyclicBarrier 可以重复利用,不像 WaitGroup 重用的时候,必须小心翼翼避免 panic。")])])]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 数字代表执行任务的 goroutine 的个数")]),t._v("\nb1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" cyclicbarrier"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("New")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 或者带有方法的循环栅栏")]),t._v("\nb2 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" cyclicbarrier"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewWithAction")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n\nb"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Await")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// await other parties")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 将循环栅栏重置")]),t._v("\nb"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Reset")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// reset the barrier")]),t._v("\n")])])]),n("p",[t._v("我们可以发现循环栅栏会一直循环的执行,虽然它有 await 方法,但是每次到这个 await 都会继续执行下一轮的循环,那么该如何跳出循环呢?")]),t._v(" "),n("p",[t._v("通常我们会配合 sync.WaitGroup 一起执行")]),t._v(" "),n("h2",{attrs:{id:"errgroup"}},[t._v("errgroup")]),t._v(" "),n("p",[t._v("将一个通用的父任务,拆成几个小任务并发执行的场景")]),t._v(" "),n("ul",[n("li",[t._v("WithContex 表示创建一个 group 实例,并且创建一个 withcancel 的上下文 context,一旦子任务返回错误,或者 wait 调用返回,context 就会被 cancel")]),t._v(" "),n("li",[t._v("Go Go(f func()error) 传入的子任务,一旦 error,就会促发 withcancel 的 cancel 操作")]),t._v(" "),n("li",[t._v("Wait 等待所有任务都完成后,wait 才会执行")])]),t._v(" "),n("h2",{attrs:{id:"sync-once"}},[t._v("sync.Once")]),t._v(" "),n("p",[t._v("once 用来执行仅发生一次的动作,常用与单例模式,对象初始化的行为,并且经常在 init 函数中使用")]),t._v(" "),n("p",[t._v("sync.Once 仅仅暴漏了一个 do 方法,而且多次调用 do,仅有第一次的无返回值的 f 函数可以执行,即便 f 不同:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" once sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Once\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("init")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n once"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Do")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 仅执行一次")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这次不会执行")]),t._v("\n once"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Do")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n \n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h2",{attrs:{id:"讨论线程安全的-map-在多线程中的使用"}},[t._v("讨论线程安全的 map 在多线程中的使用")]),t._v(" "),n("p",[t._v("go 语言中的 map 并不是并发安全的,一个 map 如果不加锁的去处理数据的时候就会出现 panic 的情况,比如:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"sync"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"time"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" m "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 初始化一个map")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("100000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\tm"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//设置key")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("100000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//访问这个map")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ttime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1000000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("这种写法就会发生 panic,原因是 go 语言不支持并发读写 map,必须加锁")]),t._v(" "),n("p",[t._v("其实我们如果分析这段代码,并没有说对同一个 key 值进行读写,也没有涉及到扩容的问题,但是仍然会 panic,go 在操作 map 时会进行 data race 的检测,只要检测有,就会直接 panic")]),t._v(" "),n("h3",{attrs:{id:"直接加锁"}},[t._v("直接加锁")]),t._v(" "),n("p",[t._v("我们可以人为的加锁,这样就可以避免 data race 的行为")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"sync"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"time"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" mu sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Mutex\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" m "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 初始化一个map")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("100000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\tmu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\tm"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//设置key")]),t._v("\n\t\t\tmu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("100000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\tmu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//访问这个map")]),t._v("\n\t\t\tmu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ttime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1000000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("如果数据量比较低的话,这么做毫无问题,如果数据量较大,或者每次操作都比较耗时,读写公用一锁就比较浪费了。")]),t._v(" "),n("p",[t._v("那么可以使用读写锁吗?当然可以啦,我们使用读写锁可以更优秀的去解决这个问题")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"sync"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"time"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" mu sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("RWMutex\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" m "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 初始化一个map")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("100000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\tmu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\tm"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//设置key")]),t._v("\n\t\t\tmu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("100000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\tmu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("RLock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//访问这个map")]),t._v("\n\t\t\tmu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("RUnlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ttime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1000000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("RWMutex 在同时有读写需求时,会优先获取写锁,读锁需要等待")]),t._v(" "),n("p",[t._v("如果当前有读锁,则后续的写锁请求会被阻塞,但读锁可以继续获取,")]),t._v(" "),n("p",[t._v("如果当前有写锁,则后续的读锁和写锁请求都会被阻塞。")]),t._v(" "),n("p",[t._v("所以,如果读多写少,使用读写锁是非常方便的,假如读和写都异常的高,那么读和写其实是不能同时进行的,如果读贼多,写就可能被阻塞等待了。")]),t._v(" "),n("h3",{attrs:{id:"细颗粒度并发安全-map"}},[t._v("细颗粒度并发安全 Map")]),t._v(" "),n("p",[t._v("我们都知道,锁对于性能的影响是特别大的,尤其是线程非常多的时候,那么多线程公用一个锁,各种等待,能不影响效率吗,那么怎么做能提高效率呢?")]),t._v(" "),n("p",[t._v("降低锁的颗粒度就可以提高效率,换言之就是本来 1000 个线程公用一个,现在,我们把数据分为 10 份,100 个线程用一个锁,性能就能大范围的提高")]),t._v(" "),n("p",[t._v("我们可以使用 https://github.com/orcaman/concurrent-map 这个分片儿锁去替代互斥锁")]),t._v(" "),n("p",[t._v("分片儿锁的基本原理就是将一个大的 map 的内容,变成 10 个或者是更多个 map 的内容,我们可以这么做:")]),t._v(" "),n("p",[t._v("本身需要一个 map 的数据结构,我们改成一个 slice,slice 含有 10 个 map 的数据结构\n我们还需要一个定位分片的算法,基本上都是使用一个哈希算法先定位分片,然后后续就跟一般的互斥锁一致了。\n用法如下:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Create a new map.")]),t._v("\n\tm "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" cmap"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("New"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v('// Sets item within map, sets "bar" under key "foo"')]),t._v("\n\tm"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Set")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"foo"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"bar"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Retrieve item from map.")]),t._v("\n\tbar"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ok "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"foo"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v('// Removes item under key "foo"')]),t._v("\n\tm"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Remove")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"foo"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),n("h3",{attrs:{id:"sync-map"}},[t._v("sync.Map")]),t._v(" "),n("p",[t._v("这是 go 官方提供的一个线程安全的 map,先说使用场景,只写一次,大量读的场景。")]),t._v(" "),n("p",[t._v("sync.Map 跟分片锁不同,分片锁是直接降低颗粒度,sync.Map 它的基本原理是读写分离,用空间换时间。通过一个只读的数据结构来提高读取速度。")]),t._v(" "),n("p",[t._v("当读少写多的时候,它的效率甚至比直接使用互斥锁还低,总之如果不是写少读多的场景,千万不要用,这个包的使用率挺低的。")]),t._v(" "),n("h2",{attrs:{id:"sync-pool"}},[t._v("sync.Pool")]),t._v(" "),n("p",[t._v("sync.Pool 是一个对象池,如果我们有一些重复使用的,并且需要频繁的申请和释放的临时对象,那么可以用这个对象池来提高性能。不过这个池子里的对象有可能会被垃圾回收,所以非常重要的数据不能使用这种方法")]),t._v(" "),n("p",[t._v("我来描述一种场景,我们有数据需要被 goroutine 去处理,但是谁处理都行,不 care 是哪位,那么我们就可以创建很多的 goroutine,然后放入到 goroutine 池中 (就跟外包一样。。。)")]),t._v(" "),n("p",[t._v("sync.Pool 有两个注意事项,首先,它线程安全,其次,不能复制 sync.Pool,如果你复制一个 sync.Pool,实际上得到的只是一个指针的拷贝,并不会复制本地池子,所以多个拷贝的 sync.Pool 指针指向的是同一个本地池子,达不到复用的目的。应该定义一个全局的 sync.Pool 实例,不同的 goroutine 都使用这个实例,才能达到对象复用和减少内存分配的目的")]),t._v(" "),n("p",[t._v("pool 包拥有三个方法")]),t._v(" "),n("ul",[n("li",[t._v("New,pool 的 new 方法后面跟的是创建的内容,一旦 pool 里面空了,就会调用这个 new 方法,当然你也可以不指定这个参数,那么创建的就是 nil 了")]),t._v(" "),n("li",[t._v("Get 从 pool 中获取一个对象")]),t._v(" "),n("li",[t._v("Set 将对象送回 pool 中")])]),t._v(" "),n("p",[t._v("下面举一个演示的例子:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" buffer "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Pool"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n New"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" any "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("new")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("bytes"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Buffer"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("GetBuffer")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("bytes"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Buffer "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" buffer"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("bytes"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Buffer"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("PutBuffer")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("buf "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("bytes"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Buffer"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Reset会将缓冲区重置为空,但它会保留底层存储以供将来写入使用")]),t._v("\n buf"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Reset")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n buffers"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Put")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("buf"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("这段代码是会有内存泄露的风险的,原因也很简单,buf.Reset() 并不会删除底层 slice 的容量,它会保存底层数据结构中的容量,所以 gc 的时候这个 buffer 就非常有可能不会被回收,造成内存泄露")]),t._v(" "),n("p",[t._v("改正方法也很简单,底层数据太大了直接丢弃 gc 就 ok 了,小的放到池子里")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("PutBuffer")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("buf "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("bytes"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Buffer"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Reset会将缓冲区重置为空,但它会保留底层存储以供将来写入使用")]),t._v("\n buf"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Reset")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" buf"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Cap")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" maxSize"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" \n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n buffers"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Put")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("buf"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h3",{attrs:{id:"pool-内存浪费"}},[t._v("pool 内存浪费")]),t._v(" "),n("p",[t._v("当我们需求的内存比池子中的内存小很多的时候,就会造成内存浪费,解决方法就是多造几个池子,比如小池子,中池子,大池子,使用这种方案合理的使用内存")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n readerPool sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Pool\n reader2kPool sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Pool\n reader4kPool sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Pool\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),n("p",[t._v("推荐一个三方库,特点是能更加高效的发挥系统性能节约内存以及避免内存泄露问题等,他可以动态的去调节池子的 dafault size 和 max size")]),t._v(" "),n("p",[t._v("https://github.com/valyala/bytebufferpool")]),t._v(" "),n("p",[t._v("连接池也是一个很常见的需求,但是通常不会使用 pool,因为 pool 会被 gc,所以并不靠谱,通常我们使用 map 或者 slice 去实现一个需要真的长时间稳定连接的连接池比如:")]),t._v(" "),n("ul",[n("li",[t._v("http client 池")]),t._v(" "),n("li",[t._v("tcp 连接池,推荐三方库:https://github.com/fatih/pool")]),t._v(" "),n("li",[t._v("数据库连接池")]),t._v(" "),n("li",[t._v("memcached client 连接池")])]),t._v(" "),n("h3",{attrs:{id:"worker-pool"}},[t._v("worker pool")]),t._v(" "),n("p",[t._v("worker pool 其实就是 goroutine 的池子,虽然 go 语言的 goroutine 非常的轻量化,但是如果几十万上百万的 goroutine 被创建还是会出问题的")]),t._v(" "),n("p",[t._v("因为 goroutine 是一种要求长期存在的资源,所以一般不使用 pool,而是使用 channel 去作为池子,毕竟 channel 自带线程安全")]),t._v(" "),n("p",[t._v("Worker pool 的目的是为了重用 goroutine。但它重用的不是 goroutine 本身,而是通过 goroutine 执行任务的能力。")]),t._v(" "),n("p",[t._v("如果直接在 channel 中传递 goroutine,那么每个 goroutine 只能执行唯一的一个任务,执行完就退出了。这并没有实现重用。")]),t._v(" "),n("p",[t._v("而 worker pool 的设计是:")]),t._v(" "),n("p",[t._v("创建固定数量的 goroutine 作为 workers。\nworkers 在 loop 中持续从任务 channel 接收任务并执行。\n向任务 channel 不断发送不同的任务。\n这样,每个 worker(goroutine) 就可以执行多个任务,实现重用。")]),t._v(" "),n("p",[t._v("所以 channel 中的内容不是 goroutine,而是任务信息,用于指导 goroutine 执行什么任务。goroutine 从 channel 收任务,利用自身的执行能力反复执行不同的任务。")]),t._v(" "),n("p",[t._v("这才是 worker pool 设计的核心思想 - 通过固定数量的 goroutine 反复执行不同的任务,以重用 goroutine 实现高效调度。")]),t._v(" "),n("p",[t._v("所以,worker pool 的目的是重用 goroutine 的执行能力,而不是重用 goroutine 本身。这是通过 channel+goroutine 的组合实现的,channel 用于传递任务信息,goroutine 负责执行任务")]),t._v(" "),n("p",[t._v("让我们简单的实现一个方案:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 任务类型")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Task "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n f "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 任务函数")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 执行任务")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Task"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Execute")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 单个worker池 ")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Worker "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n TaskChan "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Task\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("w "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Worker"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Start")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n task "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("w"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("TaskChan\n task"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Execute")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 为worker 池 分配 goroutine ,这得结合下面的 Newpool 才能看出来")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Pool "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n TaskChan "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Task\n Workers "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Worker\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" \n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 创建worker池")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewPool")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("numWorkers "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Pool "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n \n taskChan "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Task"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 初始化workers")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" workers "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Worker\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" numWorkers"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n worker "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Worker"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n TaskChan"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" taskChan"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n workers "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("workers"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" worker"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" worker"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Start")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Pool"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n TaskChan"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" taskChan"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Workers"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" workers"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 分发任务")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("p "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Pool"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Schedule")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("task "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Task"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n p"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("TaskChan "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" task\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("使用方式")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 定义任务")]),t._v("\ntask1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Task"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" \n f"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// do something")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\npool "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewPool")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 分发任务")]),t._v("\npool"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Schedule")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("task1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("提供一些个优秀的 worker pool 方案")]),t._v(" "),n("ul",[n("li",[t._v("gammazero/workerpool")]),t._v(" "),n("li",[t._v("ivpusic/grpool")]),t._v(" "),n("li",[t._v("dpaks/goworkers")]),t._v(" "),n("li",[t._v("https://github.com/alitto/pond")])]),t._v(" "),n("h2",{attrs:{id:"semaphore"}},[t._v("semaphore")]),t._v(" "),n("p",[t._v("信号量 (英语:semaphore) 又称为信号标,是一个同步对象,用于保持在 0 至指定最大值之间的一个计数值。")]),t._v(" "),n("p",[t._v("在系统中,给予每一个进程一个信号量,代表每个进程目前的状态,未得到控制权的进程会在特定地方被强迫停下来,等待可以继续进行的信号到来")]),t._v(" "),n("p",[t._v("根据信号量的不同可以分为计数信号量和二进制信号量,前者使用一个整数作为信号量,后者使用一个二进制 0 1 作为信号量")]),t._v(" "),n("p",[t._v("信号量拥有两个操作:")]),t._v(" "),n("ul",[n("li",[t._v("p 操作会减少信号量的数值")]),t._v(" "),n("li",[t._v("v 操作会增加信号量的数值")])]),t._v(" "),n("p",[t._v("其中二进制信号量是特殊的信号量,它就是互斥锁的功能")]),t._v(" "),n("p",[t._v("go 语言在 x/sync 中提供了一个 weighted 的包,它就是提供的信号量的功能")]),t._v(" "),n("ul",[n("li",[t._v("Acquire p 操作,减少信号量的数值,表示获取了资源 -1")]),t._v(" "),n("li",[t._v("Release v 操作,增加信号量的数值,表示释放了资源 +1")]),t._v(" "),n("li",[t._v("TryAcquire 尝试获取资源,如果获取成功,则返回 true,否则返回 false\n它类似于 trylock 锁,也就是失败直接返回 false,并不会阻塞")])]),t._v(" "),n("p",[t._v("注意,信号量为了简洁的设计要求,pv 操作使用 +1 和 -1 这种非常简单的递增递减设计,是有意为之的。这种简单性使得信号量在各种同步情况下都很容易理解和正确使用。更复杂的操作可能会引入难以发现的 bug 或误用场景。")]),t._v(" "),n("p",[t._v("让我们使用信号量来实现一个 worker pool")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"context"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"log"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"runtime"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"time"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 最大的worker数量")]),t._v("\n\tmaxWorkers "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" runtime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("GOMAXPROCS")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tsema "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" semaphore"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewWeighted")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("int64")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("maxWorkers"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//信号量")]),t._v("\n\ttask "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" maxWorkers"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 任务数,是worker的四倍")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tctx "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" context"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Background")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" task "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 如果没有worker可用,会阻塞在这里,直到某个worker被释放")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" sema"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Acquire")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 启动worker goroutine")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" sema"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Release")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\ttime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("100")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Millisecond"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 模拟一个耗时操作")]),t._v("\n\t\t\ttask"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 请求所有的worker,这样能确保前面的worker都执行完")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" sema"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Acquire")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ctx"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("int64")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("maxWorkers"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tlog"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Printf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"获取所有的worker失败: %v"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("task"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h3",{attrs:{id:"使用信号量时的注意事项"}},[t._v("使用信号量时的注意事项")]),t._v(" "),n("ul",[n("li",[t._v("请求了资源,忘记了释放")]),t._v(" "),n("li",[t._v("释放了从未请求的资源")]),t._v(" "),n("li",[t._v("长时间持有一个资源但是不使用它")]),t._v(" "),n("li",[t._v("不持有一个资源,但是直接使用了它")])]),t._v(" "),n("h3",{attrs:{id:"使用-channel-去实现一个信号量"}},[t._v("使用 channel 去实现一个信号量")]),t._v(" "),n("p",[t._v("使用一个缓存为 n 的 channel 去实现一个信号量")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"sync"')]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Semaphore 数据结构,并且还实现了Locker接口")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" semaphore "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tch "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 创建一个新的信号量")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewSemaphore")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("capacity "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Locker "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" capacity "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tcapacity "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 容量为1就变成了一个互斥锁")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("semaphore"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("ch"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" capacity"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 请求一个资源")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("semaphore"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ts"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ch "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 释放资源")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("semaphore"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ch\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("h2",{attrs:{id:"issues"}},[t._v("issues")]),t._v(" "),n("h3",{attrs:{id:"问题一-有互斥锁就一定有临界区吗"}},[t._v("问题一:有互斥锁就一定有临界区吗?")]),t._v(" "),n("p",[t._v("互斥锁的存在不等于必须存在临界区。")]),t._v(" "),n("p",[t._v("构成一个合理的临界区,需要满足:")]),t._v(" "),n("ul",[n("li",[t._v("有真正需要互斥访问的共享资源 (比如共享内存,变量等)")]),t._v(" "),n("li",[t._v("通过加锁,在访问该资源前后形成互斥的代码区域")]),t._v(" "),n("li",[t._v("确保同一时间只有一个线程/goroutine 可以进入该互斥区域")])]),t._v(" "),n("p",[t._v("所以互斥锁只是手段之一,用于保证临界区互斥性的需要。")]),t._v(" "),n("p",[t._v("如果没有需要保护的共享资源,或者互斥逻辑不严密,那么使用再多的锁也不等于形成了临界区")]),t._v(" "),n("h3",{attrs:{id:"问题二-如果-mutex-已经被一个-goroutine-获取了锁-其它等待中的-goroutine-们只能一直等待。那么-等这个锁释放后-等待中的-goroutine-中哪一个会优先获取-mutex-呢"}},[t._v("问题二:如果 Mutex 已经被一个 goroutine 获取了锁,其它等待中的 goroutine 们只能一直等待。那么,等这个锁释放后,等待中的 goroutine 中哪一个会优先获取 Mutex 呢?")]),t._v(" "),n("p",[t._v("上文中的锁的饥饿模式和正常模式可以解释这个问题。")]),t._v(" "),n("p",[t._v("如果是正常的模式下,就是按照正常队列 FIFO 的顺序去获取锁,除非这个时候有新的 goroutine 生成,那么这个 goroutine 会优先获取锁。")]),t._v(" "),n("p",[t._v("但是如果一个队头的 goroutine 等待时间过长超过了 1ms,那么它就会将互斥锁的模式变成饥饿模式,自动获取锁")]),t._v(" "),n("h3",{attrs:{id:"问题三-mutext-的底层中-使用-state-和-sema-来表示锁的状态-sema-是信号量-为什么在有信号量表示锁的状态之后还需要一个-state-表示锁是否上锁呢"}},[t._v("问题三:Mutext 的底层中,使用 state 和 sema 来表示锁的状态,sema 是信号量,为什么在有信号量表示锁的状态之后还需要一个 state 表示锁是否上锁呢?")]),t._v(" "),n("ul",[n("li",[t._v("state 作为一个 boolean 变量,可以非常简单直观地表示锁的基本状态。")]),t._v(" "),n("li",[t._v("sema 是一个整数计数器,可以灵活地表示多种状态,如等待队列长度等。")]),t._v(" "),n("li",[t._v("将两者分开,可以清晰地分离基本锁状态和高级同步语义,符合分而治之的设计思想。")]),t._v(" "),n("li",[t._v("sema 可直接 reused 现成的信号量实现代码,state 又足够轻量不需要复杂机制。")]),t._v(" "),n("li",[t._v("将两者组合可以充分发挥各自的优势,实现一个功能完备但设计简单的 mutex。")]),t._v(" "),n("li",[t._v("如果全部只依赖 sema 来表示所有状态,实现可能会更复杂,语义也不够清晰。")])]),t._v(" "),n("h3",{attrs:{id:"问题四-使用循环栅栏-信号量去完成经典并发题-水的制造工厂"}},[t._v("问题四:使用循环栅栏,信号量去完成经典并发题:水的制造工厂")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//并发趣题:一氧化二氢制造工厂")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//题目是这样的:")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//有一个名叫大自然的搬运工的工厂,生产一种叫做一氧化二氢的神秘液体。这种液体的分子是由一个氧原子和两个氢原子组成的,也就是水。")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//这个工厂有多条生产线,每条生产线负责生产氧原子或者是氢原子,每条生产线由一个 goroutine 负责。")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//这些生产线会通过一个栅栏,只有一个氧原子生产线和两个氢原子生产线都准备好,才能生成出一个水分子,")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//否则所有的生产线都会处于等待状态。也就是说,一个水分子必须由三个不同的生产线提供原子,而且水分子是一个一个按照顺序产生的,")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//每生产一个水分子,就会打印出 HHO、HOH、OHH 三种形式的其中一种。HHH、OOH、OHO、HOO、OOO 都是不允许的。")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//生产线中氢原子的生产线为 2N 条,氧原子的生产线为 N 条。")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"context"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"github.com/marusama/cyclicbarrier"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"golang.org/x/sync/semaphore"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"math/rand"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"sort"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"sync"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"time"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// h2o 水的组成,其中我们需要俩h一个o所以我们给定他们信号量,来对他们的任务进行控制。")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" H2O "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 控制的h的信号量")]),t._v("\n\tseaH "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("semaphore"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Weighted\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 控制O的信号量")]),t._v("\n\tseaO "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("semaphore"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Weighted\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 栅栏,这里也就是重复的使用栅栏,也就是 重复栅栏。")]),t._v("\n\tcyc cyclicbarrier"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("CyclicBarrier\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewH2O")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("H2O "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("H2O"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// h 两个")]),t._v("\n\t\tseaH"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" semaphore"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewWeighted")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// o 需要一个")]),t._v("\n\t\tseaO"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" semaphore"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewWeighted")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 我们要控制的循环栅栏就是3个,因为一共需要三个嘛。")]),t._v("\n\t\tcyc"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" cyclicbarrier"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("New")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 处理h")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("o "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("H2O"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("dealH")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("outH "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 将这个信号量给拿出来1,因为h充盈来2,所以会有俩线程做这个动作")]),t._v("\n\to"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("seaH"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Acquire")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Background")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 输出 h")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("outH")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// wait的意思就是不等到三个线程,我就不走")]),t._v("\n\to"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("cyc"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Await")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Background")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 走动完毕后再把资源塞进去。")]),t._v("\n\to"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("seaH"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Release")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 处理 o")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("o "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("H2O"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("dealO")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("outO "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 氧气将信号量中的信号取出来,")]),t._v("\n\to"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("seaO"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Acquire")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Background")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 输出o")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("outO")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 等待三个线程跟上一个函数一样意思,也不用担心用两次不行,随便用。这个函数调用几次都OK。")]),t._v("\n\to"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("cyc"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Await")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Background")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 释放掉。")]),t._v("\n\to"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("seaO"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Release")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// channel 传递信息。")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" ch "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" outO "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tch "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"O"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" outH "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tch "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"H"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 一共有 300个channel需要。")]),t._v("\n\tch "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("300")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// wg是为了控制这300个线程,栅栏是为了控制生成水的这个控制器,两者的作用不同哦。")]),t._v("\n\twg "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("new")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("WaitGroup"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\twg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("300")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\th "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewH2O")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("100")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Done")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\ttime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Duration")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("rand"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Intn")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("100")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Millisecond"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\th"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("dealO")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("outO"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("200")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Done")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\ttime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Duration")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("rand"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Intn")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("100")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Millisecond"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\th"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("dealH")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("outH"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\twg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Wait")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ch"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("300")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ch"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("panic")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"❌"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\ts "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("100")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\ts"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("ch\n\t\ts"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("ch\n\t\ts"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("ch\n\t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里,对于hho进行排序了,不然也不一定是hho这个顺序")]),t._v("\n\t\tsort"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Strings")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\tresult "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" result "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"HHO"')]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"错误 ❌ :"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" result"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),n("ul",[n("li",[t._v("https://mp.weixin.qq.com/s/iPpWd8vjyaN2sJFwxzN9Bg")]),t._v(" "),n("li",[t._v("https://draveness.me/golang/docs/part3-runtime/ch06-concurrency/golang-sync-primitives/")]),t._v(" "),n("li",[t._v("https://time.geekbang.org/column/intro/100061801")]),t._v(" "),n("li",[t._v("https://colobu.com/2018/12/18/dive-into-sync-mutex/")]),t._v(" "),n("li",[t._v("《go 语言精进之路》")])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/110.638aa560.js b/assets/js/110.638aa560.js new file mode 100644 index 000000000..68bdbab02 --- /dev/null +++ b/assets/js/110.638aa560.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[110],{542:function(t,s,n){"use strict";n.r(s);var a=n(36),r=Object(a.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"go-并发模型"}},[t._v("go 并发模型")]),t._v(" "),n("h2",{attrs:{id:"并发和并行的关系"}},[t._v("并发和并行的关系")]),t._v(" "),n("p",[t._v("并发是问题域的一种概念,它强调处理多个同时 (或者"),n("strong",[t._v("近似同时")]),t._v(") 发生的事件。")]),t._v(" "),n("p",[t._v("并行是方法域的一种概念,将问题分解为多个部分,同时并行执行来加速解决问题。")]),t._v(" "),n("p",[t._v("并发可以不是同时进行的,但是并行强调的就是必须同时进行。")]),t._v(" "),n("blockquote",[n("p",[t._v("rob pike:并发不是并行,并发关乎结构,并行关乎执行")])]),t._v(" "),n("p",[t._v("并发但不并行:一位老师,在听一个学生朗读的时候,她可以暂停学生的朗读,然后回答学生的问题,再次开始学生的朗读,虽然她一次只能干一件事 (所以不满足并行),但可以处理近乎同时发生的***多个事件***")]),t._v(" "),n("p",[t._v("并行但不是并发:让全班同学制作贺卡,全班同学每个学生制作五枚,全班同学同时开始做 (满足并行),但不是并发,因为只有一个事件 (不满足并发提出的同时处理多个事件)")]),t._v(" "),n("p",[t._v("并发和并行:两位老师,一个老师提问,一个老师解决学生的问题,这就是满足了并行和并发:同时 (这里是近似同时) 处理多个事件:提问和解决问题 (满足了并发),两个老师同时开始这满足了物理层面的同时进行 (满足了并行)")]),t._v(" "),n("p",[t._v("可以看出来,并发强调多个事件,并行强调物理层面的同时执行")]),t._v(" "),n("p",[t._v("获得真正的并行,必须在具有多个物理处理器的计算机上运行程序,针对单个处理器的计算机,只能实现并发执行")]),t._v(" "),n("p",[t._v("go 在执行并发任务时,如果所在物理机器为多核,那么并行的数量就等于 "),n("code",[t._v("runtime.GOMAXPROCS")]),t._v(" 的值,如果机器为单核,那么只能执行并发操作了")]),t._v(" "),n("p",[t._v("在使用中,***并发且并行***是最常见的行为,首先,我们通常不会只执行一个事件,并且所用机器不太可能是单核")]),t._v(" "),n("h2",{attrs:{id:"多线程并发模型"}},[t._v("多线程并发模型")]),t._v(" "),n("p",[t._v("使用共享内存的方式去完成并发就是多线程并发模型,它的核心就是使用锁的方法,让某个线程单独拥有某块内存,其他线程只能访问该内存,从而实现了并发。")]),t._v(" "),n("p",[t._v("go 语言中的锁就是 sync.Mutex,这也是 go 语言实现多线程并发的核心,一共有:")]),t._v(" "),n("ul",[n("li",[t._v("sync.Mutex 互斥锁,可以同时对一个资源进行读写操作,但是只能有一个线程可以对该资源进行写操作。")]),t._v(" "),n("li",[t._v("sync.RWMutex 读写锁,可以同时对一个资源进行读操作,但是只能有一个写操作")]),t._v(" "),n("li",[t._v("sync.Cond 条件变量,可以让一个线程等待另一个线程满足某个条件")]),t._v(" "),n("li",[t._v("sync.Once 单例模式,保证某个资源只被初始化一次")]),t._v(" "),n("li",[t._v("sync.Pool 资源池,可以让一个资源在内存中被复用,避免了重复创建资源的开销")]),t._v(" "),n("li",[t._v("sync.Map 线程安全的 map,可以让多个线程安全的对 map 进行读写操作")]),t._v(" "),n("li",[t._v("sync.Pool 资源池,可以让一个资源在内存中被复用,避免了重复创建资源的开销")]),t._v(" "),n("li",[t._v("sync.WaitGroup 等待组,可以让多个线程等待,直到某个线程完成某个任务")]),t._v(" "),n("li",[t._v("golang.org/x/sync/errgroup 为处理公共任务的子任务的 goroutine 组提供同步、错误传播和上下文取消")]),t._v(" "),n("li",[t._v("golang.org/x/sync/semaphore 提供了一个加权信号量实现。")]),t._v(" "),n("li",[t._v("golang.org/x/sync/singleflight 提供了重复函数调用抑制机制,中文叫栅栏机制")]),t._v(" "),n("li",[t._v("golang.org/x/sync/syncmap 提供了一个并发映射实现")])]),t._v(" "),n("h2",{attrs:{id:"csp"}},[t._v("csp")]),t._v(" "),n("p",[t._v("go 语言推荐的并发模型使用的就是 csp 模型,csp 的核心思想就是讲各个任务等同于进程,进程顺序执行互不牵连,进程可以收发信息,使用通道的方式进行信息的通信。")]),t._v(" "),n("p",[t._v("所以如果使用 channel 的方式进行通信就是使用的 csp 并发模型")]),t._v(" "),n("p",[t._v("CSP 模型中的进程通信原语包括:")]),t._v(" "),n("ul",[n("li",[t._v("发送消息:一个进程可以通过发送消息到另一个进程来与之进行通信。")]),t._v(" "),n("li",[t._v("接收消息:一个进程可以通过接收消息来获取另一个进程发送的消息。")]),t._v(" "),n("li",[t._v("原子发送-接收:一个进程可以通过原子发送-接收操作来发送消息并等待接收消息,这相当于发送和接收两个操作的组合。")])]),t._v(" "),n("p",[t._v("这些进程通信的都是通过内置的 channel 对象去实现的。")]),t._v(" "),n("h2",{attrs:{id:"了解-goroutine-的基本信息"}},[t._v("了解 goroutine 的基本信息")]),t._v(" "),n("p",[t._v("这里我们现简单的了解一些基本的使用 goroutne 的方法,后面的 channel 篇和并发原语 context atomic 定时器会进行更加详细的介绍。")]),t._v(" "),n("p",[t._v("我们知道 go 使用了用户线程也就是 goroutine 去替代了传统的线程,所以在 go 语言中我们能操作的线程就是 goroutine,我们无法去触及真实的线程,线程和 goroutine 之间的关系是 go 语言运行时的调度器去调度的。")]),t._v(" "),n("p",[t._v("goroutine 是一种轻量的可以被大量创建的用户态线程。")]),t._v(" "),n("h3",{attrs:{id:"创建-goroutine"}},[t._v("创建 goroutine")]),t._v(" "),n("p",[t._v("使用 go 关键字加上函数去创建一个 goroutine,当然后面跟方法也可以。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 注意后面跟的是一个函数的运行,这跟 defer 一致")]),t._v("\n\tc "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\ttime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\tc "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\td "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("c\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("d"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("我们可以看到,这里使用了 csp 的并发模型,下面我们看一下使用传统的共享内存的并发模式")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 注意后面跟的是一个函数的运行,这跟 defer 一致")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" mu sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Mutex\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\tmu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" mu"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\ttime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("200")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h3",{attrs:{id:"异步函数决定权交给函数调用方"}},[t._v("异步函数决定权交给函数调用方")]),t._v(" "),n("p",[t._v("我们看一个场景:")]),t._v(" "),n("p",[t._v("我们要读取一个目录下的路径,首先我们可以这么写函数:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("ListDirectory")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("dir "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),n("p",[t._v("这是一个同步函数,我们传入的是目录的地址,返回的是值和错误,只不过我们需要阻塞的等待所有的路径全部扫描完成才能返回")]),t._v(" "),n("p",[t._v("如果我们不想让业务阻塞到这里,可以改造成异步函数:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("ListDirectoryAsync")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("dir "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" c\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("将数据传递给 channel,只需要不断的去读取 channel 就可以变成非阻塞的业务。")]),t._v(" "),n("p",[t._v("不过这里我们发现还是会有一些问题,比如,如果我读取到了想要的数据想结束这个函数,该如何操作呢?读取过程中我如何分辨是读取完成了 close 掉了这个 channel 还是出现了错误 close 这个 channel 呢,所以这个函数还是需要改造")]),t._v(" "),n("p",[t._v("我们可以将这个函数设置成一个同步函数,让调用者来决定是否异步的启动新 goroutine 去调用这个函数,这给了程序更大的灵活性")]),t._v(" "),n("p",[t._v("***将异步执行函数的异步权交给调用方***是更好的设计思想,")]),t._v(" "),n("p",[t._v("因为如果这个函数内部启动了一个 goroutine,但是它并没有提供给你详细的退出机制,那么非常容易出现 goroutine 的泄漏问题")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("ListDirectory")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("dir "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fn "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" info os"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("FileInfo"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tinfo"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" os"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lstat")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("dir"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\terr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("fn")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("root"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\terr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("walk")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("root"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fn"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" SkipDir "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("||")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" SkipAll "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 同步调用")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("retrieveData")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("root "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 使用一个切片来存储结果")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" result "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 调用ListDirectory,这里不再使用goroutine")]),t._v("\n\terr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("ListDirectory")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("root"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" info os"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("FileInfo"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 如果文件不是普通文件,直接返回nil")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Mode")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("IsRegular")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 将路径添加到结果切片中")]),t._v("\n\t\tresult "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" path"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 如果没有错误,返回结果切片")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" result"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 如果有错误,返回错误")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 异步调用调用:")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("retrieveData")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("root "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\terr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tvalue "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 调用时再决定是同步还是异步")]),t._v("\n\t\terr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("ListDirectory")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("root"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" info os"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("FileInfo"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// if the file is noe regular, it mean the file is done,you should return")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Mode")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("IsRegular")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t\tvalue "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" path\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("p",[t._v("可以看上文,同步操作也可以,我们也可以使用 "),n("code",[t._v("go func")]),t._v(" 的方式异步执行它,因为要传入一个函数,所以如果我们使用了异步,就在函数中使用 channel 来传递结果,如果我们是同步,那么就不再使用 channel,使用一个切片即可")]),t._v(" "),n("h3",{attrs:{id:"goroutine-退出"}},[t._v("goroutine 退出")]),t._v(" "),n("p",[t._v("goroutine 使用代价很低,因为它并不是操作系统的线程,创建成本非常低,go 推荐可以多多使用 goroutine")]),t._v(" "),n("p",[t._v("goroutine 退出有两种方式:")]),t._v(" "),n("ul",[n("li",[t._v("主动退出:使用 return 或者 panic 关键字退出")]),t._v(" "),n("li",[t._v("非主动退出:使用 sync.WaitGroup,context 等方法,当所有的 goroutine 都退出后,等待组会自动退出")])]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" wg sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("WaitGroup\n\twg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Done")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n wg"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Wait")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("go goroutine 执行完毕就会直接退出,")]),t._v(" "),n("p",[n("em",[n("strong",[t._v("程序的退出跟主 goroutine 有关,只要主 goroutine 不退出程序就会正常执行下去,反之,主 goroutine 如果退出了,其它的 goroutine 即便没有执行完毕,整个程序还是会结束。")])])]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ttime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hi there"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("这个程序将无法保证能输出 hi there")]),t._v(" "),n("p",[t._v("要想让 age 输出正常的值,必须保证主 goroutine 不能退出,比如使用 sync.WaitGroup,比如直接让主 goroutine 休眠")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("20000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ttime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hi there"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h2",{attrs:{id:"使用-goroutine-时的注意事项"}},[t._v("使用 goroutine 时的注意事项")]),t._v(" "),n("p",[t._v("当我们使用 goroutine 时,下面几点是注意事项:")]),t._v(" "),n("ol",[n("li",[n("em",[n("strong",[t._v("确定一个 goroutine 什么时候会结束")])])]),t._v(" "),n("li",[n("em",[n("strong",[t._v("是否有手段能停止 goroutine")])])])]),t._v(" "),n("p",[t._v("只有搞清楚这两个问题,使用 goroutine 的时候才不会有泄露的风险,所以这两点是使用 gorotine 的安全哲学")]),t._v(" "),n("ul",[n("li",[t._v("确定一个 goroutine 什么时候会结束")]),t._v(" "),n("li",[t._v("是否有手段能停止 goroutine")])]),t._v(" "),n("p",[t._v("下面让我们正式学习 go 并发语言,channel 等相关知识!")]),t._v(" "),n("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),n("ul",[n("li",[t._v("https://mp.weixin.qq.com/s/TvHY2i1FX1zS_WHdCvK-wA")]),t._v(" "),n("li",[t._v("https://book.douban.com/subject/26337939/")]),t._v(" "),n("li",[t._v("https://book.douban.com/subject/35720728/ 315 页 - 317 页")]),t._v(" "),n("li",[t._v("极客时间《go 进阶训练营》")])])])}),[],!1,null,null,null);s.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/111.800f7506.js b/assets/js/111.800f7506.js new file mode 100644 index 000000000..ab19382ea --- /dev/null +++ b/assets/js/111.800f7506.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[111],{543:function(t,n,v){"use strict";v.r(n);var _=v(36),s=Object(_.a)({},(function(){var t=this,n=t.$createElement,v=t._self._c||n;return v("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[v("h1",{attrs:{id:"go-编译器"}},[t._v("go 编译器")]),t._v(" "),v("h2",{attrs:{id:"编译器概述"}},[t._v("编译器概述")]),t._v(" "),v("p",[t._v("默认编译器 gc 介绍\n编译原理和过程\n编译器组件工具链\n编译优化技术")]),t._v(" "),v("h2",{attrs:{id:"编译器特性"}},[t._v("编译器特性")]),t._v(" "),v("p",[t._v("编译速度快\n生成自包含可执行文件\n内存安全保证\n跨平台支持\n模块化支持")]),t._v(" "),v("h2",{attrs:{id:"编译器实战"}},[t._v("编译器实战")]),t._v(" "),v("p",[t._v("常见编译错误和解决方法\n编译参数和定制\n增量编译\nhook 编译过程\n交叉编译")]),t._v(" "),v("h2",{attrs:{id:"编译器源码"}},[t._v("编译器源码")]),t._v(" "),v("p",[t._v("gc 编译器源码解析\n编译器前端实现\n编译器后端实现")]),t._v(" "),v("h2",{attrs:{id:"其他编译器"}},[t._v("其他编译器")]),t._v(" "),v("p",[t._v("gccgo\ngollvm\n编译器比较")])])}),[],!1,null,null,null);n.default=s.exports}}]); \ No newline at end of file diff --git a/assets/js/112.d3ef22bf.js b/assets/js/112.d3ef22bf.js new file mode 100644 index 000000000..34c9095f2 --- /dev/null +++ b/assets/js/112.d3ef22bf.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[112],{545:function(t,n,s){"use strict";s.r(n);var a=s(36),e=Object(a.a)({},(function(){var t=this,n=t.$createElement,s=t._self._c||n;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h1",{attrs:{id:"go-语言官方编译器-gc"}},[t._v("Go 语言官方编译器 gc")]),t._v(" "),s("h2",{attrs:{id:"前端组件"}},[t._v("前端组件")]),t._v(" "),s("h3",{attrs:{id:"_1。lexer-词法分析器"}},[t._v("1。Lexer (词法分析器)")]),t._v(" "),s("p",[t._v("Lexer 是编译器前端的第一个重要组件。它的主要职责是对源代码进行词法分析,将源代码分割成一个个词法单元 (tokens)。这些词法单元包括关键字、标识符、运算符、字面量等基本单元。")]),t._v(" "),s("p",[t._v("在 Go 语言中,Lexer 的实现在 "),s("code",[t._v("src/go/scanner")]),t._v(" 包中。它使用了一种高效的 DFA (确定有限状态自动机) 算法进行词法分析。同时,它还支持 Unicode 编码,可以对包含多语种字符的源代码进行正确扫描。")]),t._v(" "),s("h3",{attrs:{id:"_2。parser-语法分析器"}},[t._v("2。Parser (语法分析器)")]),t._v(" "),s("p",[t._v("Parser 接收 Lexer 输出的词法单元,根据语言的语法规则构造抽象语法树 (AST)。AST 是源代码的树状表示形式,反映了代码的层次结构和语义信息。")]),t._v(" "),s("p",[t._v("Go 的语法分析器实现在 "),s("code",[t._v("src/go/parser")]),t._v(" 包中。它采用 LL(1) 自顶向下分析算法,支持对 Go 语言所有语法结构的解析。Parser 会进行语法检查,如果发现语法错误,会报告相应的错误信息。")]),t._v(" "),s("h3",{attrs:{id:"_3。type-checker-类型检查器"}},[t._v("3。Type Checker (类型检查器)")]),t._v(" "),s("p",[t._v("Type Checker 对 AST 进一步进行语义分析,尤其是类型信息的收集和一致性检查。它会为所有节点附加类型信息,检查变量使用是否正确,函数调用参数是否匹配等。如果发现类型错误,会输出相应的错误信息。")]),t._v(" "),s("p",[t._v("Type Checker 的实现位于 "),s("code",[t._v("src/go/types")]),t._v(" 包中。除了类型检查,它还可以解析导入路径、收集方法信息等。")]),t._v(" "),s("h2",{attrs:{id:"中端组件"}},[t._v("中端组件")]),t._v(" "),s("h3",{attrs:{id:"_1。ssa-静态单赋值形式"}},[t._v("1。SSA (静态单赋值形式)")]),t._v(" "),s("p",[t._v("SSA 是一种将程序的控制流展开的中间表示形式。Go 编译器在 Type Checker 之后,会将 AST 转换为 SSA 形式,以方便后续的优化分析和转换。")]),t._v(" "),s("p",[t._v("SSA 的实现位于 "),s("code",[t._v("src/cmd/compile/internal/ssa")]),t._v(" 包。在 SSA 中,每个变量只会被赋值一次,所有复杂的控制流都被展开成基本块的形式。这种表示形式方便了大量的编译器优化,如值传播、常量折叠等。")]),t._v(" "),s("h3",{attrs:{id:"_2。optimizer-优化器"}},[t._v("2。Optimizer (优化器)")]),t._v(" "),s("p",[t._v("Go 编译器内置了多种优化器模块,用于对 SSA 形式的程序进行各种形式的优化转换。主要优化器包括:")]),t._v(" "),s("ul",[s("li",[t._v("逃逸分析器 ("),s("code",[t._v("src/cmd/compile/internal/escape")]),t._v(")")]),t._v(" "),s("li",[t._v("内联器 ("),s("code",[t._v("src/cmd/compile/internal/inliner")]),t._v(")")]),t._v(" "),s("li",[t._v("死码消除 ("),s("code",[t._v("src/cmd/compile/internal/deadcode")]),t._v(")")]),t._v(" "),s("li",[t._v("常量传播和折叠 ("),s("code",[t._v("src/cmd/compile/internal/opt")]),t._v(")")]),t._v(" "),s("li",[t._v("控制流优化 ("),s("code",[t._v("src/cmd/compile/internal/opt")]),t._v(")")])]),t._v(" "),s("p",[t._v("其中,逃逸分析是 Go 编译器最重要的优化环节。它可以分析变量是否会逃逸到堆上,进而决定是否可以优化为堆分配或栈分配。这对于 Go 这样的带有垃圾回收的语言而言至关重要,能极大减少内存分配和垃圾回收的压力。")]),t._v(" "),s("h2",{attrs:{id:"后端组件"}},[t._v("后端组件")]),t._v(" "),s("h3",{attrs:{id:"_1。code-generator-代码生成器"}},[t._v("1。Code Generator (代码生成器)")]),t._v(" "),s("p",[t._v("代码生成器会将优化后的 SSA 表示形式转换为对应的机器码指令序列。")]),t._v(" "),s("p",[t._v("Go 编译器采用了自定义的高效代码生成器,其实现位于 "),s("code",[t._v("src/cmd/compile/internal/gc")]),t._v(" 中。它不仅支持多种常用硬件架构 (x86、ARM、RISC-V 等),还支持硬件特性加速。例如在 ARM64 架构上支持了利用 SVE 向量指令集进行矢量化优化。")]),t._v(" "),s("h3",{attrs:{id:"_2。assembler-汇编器"}},[t._v("2。Assembler (汇编器)")]),t._v(" "),s("p",[t._v("汇编器将前端生成的机器码经过进一步处理,生成目标平台的二进制代码。Go 编译器目前使用 GNU 汇编器进行汇编。")]),t._v(" "),s("h3",{attrs:{id:"_3。linker-链接器"}},[t._v("3。Linker (链接器)")]),t._v(" "),s("p",[t._v("链接器将全部目标文件 (object files) 以及需要的系统库文件链接合并,生成最终的可执行目标文件。可执行文件是完全独立无需外部依赖的自包含程序文件。")]),t._v(" "),s("p",[t._v("Go 语言自带小型高效的链接器,实现位于 "),s("code",[t._v("src/cmd/link")]),t._v(" 包中。它支持静态链接和动态链接两种方式,默认采用静态链接生成全自包含的可执行文件。")]),t._v(" "),s("h3",{attrs:{id:"_4。runtime"}},[t._v("4。Runtime")]),t._v(" "),s("p",[t._v("Runtime 库提供了垃圾回收、goroutine 调度、处理系统调用等运行时支持。Go 语言运行时高度优化,垃圾回收器采用了三色标记-压缩算法,并行和并发的处理提高了效率。")]),t._v(" "),s("p",[t._v("Goroutine 调度器使用了 M:N 调度模型,将 goroutine 和系统线程进行高效多路复用。这使得 Go 语言可以轻松创建大量并发任务,并拥有优秀的并发性能。")]),t._v(" "),s("h3",{attrs:{id:"其他特性"}},[t._v("其他特性")]),t._v(" "),s("p",[t._v("除了高效完整的编译器支持,Go 编译器还具备以下特性:")]),t._v(" "),s("ol",[s("li",[s("p",[t._v("多核并行编译:gc 编译器利用并发支持,使用可用的所有 CPU 核心并行编译源文件,提高整体编译速度。")])]),t._v(" "),s("li",[s("p",[t._v("增量编译:只编译被修改的源文件和依赖它们的源文件,避免对无关代码进行重新编译。")])]),t._v(" "),s("li",[s("p",[t._v("类型检查缓存:将类型检查结果缓存,以加速后续编译过程。")])]),t._v(" "),s("li",[s("p",[t._v("Go 编写:gc 编译器本身就是使用 Go 编写的,这使得它可以自举并确保与 Go 语言保持高度契合。")])])]),t._v(" "),s("p",[t._v("Go 编译器与 Go 语言及其工具链源码一并开源发布,方便社区贡献者阅读理解和改进编译器实现。")]),t._v(" "),s("h3",{attrs:{id:"总结"}},[t._v("总结")]),t._v(" "),s("p",[t._v("gc 是 Go 语言默认的官方编译器,它提供了完整的前端、优化器和后端支持,能够将 Go 语言源代码编译为高效的机器码。通过采用先进的编译器技术和算法,如 SSA 表示、逃逸分析等,gc 编译器可以生成高度优化的执行程序。")]),t._v(" "),s("p",[t._v("与此同时,gc 编译器也提供了强大的运行时支持,包括垃圾回收、goroutine 调度等核心功能。这使得 gc 不仅仅是一个简单的编译器,更是 Go 语言全栈的编译和执行解决方案。")]),t._v(" "),s("p",[t._v("Go 开发团队一直在持续改进完善 gc,以支持更多最新的语言特性和平台,满足日益增长的性能和兼容性要求。gc 编译器的持续进化有力地支撑了 Go 语言工程级应用的快速发展。")]),t._v(" "),s("h2",{attrs:{id:"真实案例的分析"}},[t._v("真实案例的分析")]),t._v(" "),s("p",[t._v("好的,我们来补充一些实际的例子,帮助理解编译器的工作原理。")]),t._v(" "),s("p",[t._v("编译器前端示例:")]),t._v(" "),s("h3",{attrs:{id:"lexer-示例"}},[t._v("Lexer 示例")]),t._v(" "),s("p",[t._v("假设有以下 Go 源代码:")]),t._v(" "),s("div",{staticClass:"language-go extra-class"},[s("pre",{pre:!0,attrs:{class:"language-go"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fmt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Hello, World!"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("Lexer 会将该源代码分割为以下词法单元:")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v('package\nmain\nimport\n"fmt"\nfunc\nmain\n(\n)\n{\nfmt\n.\nPrintln\n(\n"Hello, World!"\n)\n}\n')])])]),s("h3",{attrs:{id:"parser-示例"}},[t._v("Parser 示例")]),t._v(" "),s("p",[t._v("经过词法分析后,Parser 会将这些词法单元构造成抽象语法树 (AST):")]),t._v(" "),s("div",{staticClass:"language-go extra-class"},[s("pre",{pre:!0,attrs:{class:"language-go"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// FileNode")]),t._v("\nFile"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Package"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" main\n Imports"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("ImportSpec"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("ImportSpec"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("BasicLit"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Value"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n Decls"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("Decl"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("FuncDecl"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Ident"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("Name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"main"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n Body"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("BlockStmt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n List"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("Stmt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("ExprStmt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n X"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("CallExpr"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Fun"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("SelectorExpr"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n X"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Ident"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("Name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n Sel"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Ident"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("Name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Println"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n Args"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("Expr"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("BasicLit"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Value"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Hello, World!"')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("h3",{attrs:{id:"type-checker-示例"}},[t._v("Type Checker 示例")]),t._v(" "),s("p",[t._v("Type Checker 会对 AST 进行类型分析,并附加类型信息:")]),t._v(" "),s("div",{staticClass:"language-go extra-class"},[s("pre",{pre:!0,attrs:{class:"language-go"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// File Node with Type Information")]),t._v("\nFile"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Package"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" main\n Imports"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("ImportSpec"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("ImportSpec"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Path"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("BasicLit"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Value"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n Decls"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("Decl"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("FuncDecl"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Ident"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("Name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"main"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Type"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n Body"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("BlockStmt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n List"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("Stmt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("ExprStmt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n X"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("CallExpr"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Fun"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("SelectorExpr"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n X"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Ident"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("Name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Type"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("fmt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n Sel"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Ident"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("Name"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Println"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Type"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("n "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n Args"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("Expr"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("BasicLit"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Value"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Hello, World!"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n Type"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("编译器中端示例:")]),t._v(" "),s("h3",{attrs:{id:"ssa-示例"}},[t._v("SSA 示例")]),t._v(" "),s("p",[t._v("在 SSA 中,代码被转换为基本块的形式,控制流完全展开:")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v('# Example SSA representation\nfunc main():\nbb0: \n t0 = new [1]string { /* str */ }\n t1 = &t0[0]\n *t1 = "Hello, World!"\n t2 = staticbytes(&)\n t3 = &t2[0]\n t4 = slice t2[:]\n t5 = make([]interface{}, 1)\n t6 = &t5[0]\n t7 = &t4\n t8 = *t7\n *t6 = t8\n t9 = fmt.Println(&t5...)\n return\n\n# Optimized SSA representation\nfunc main():\nbb0:\n t0 = staticbytes(&)\n t1 = &t0[0] \n t2 = slice t0[:]\n t3 = make([]interface{}, 1)\n t4 = &t3[0]\n t5 = &t2\n t6 = *t5\n *t4 = t6\n t7 = fmt.Println(&t3...)\n return\n')])])]),s("p",[t._v("可以看到,在优化后的 SSA 表示中,一些冗余的内存分配和字符串构造操作都被优化掉了。")]),t._v(" "),s("p",[t._v("编译器后端示例:")]),t._v(" "),s("h3",{attrs:{id:"code-generator-示例"}},[t._v("Code Generator 示例")]),t._v(" "),s("p",[t._v("在 "),s("code",[t._v("amd64")]),t._v(" 平台上,优化后的 "),s("code",[t._v('fmt.Println("Hello, World!")')]),t._v(" 会被生成如下汇编代码:")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v('0x001b TEXT "".main(SB), ABIInternal, $24-0\n MOVQ $go.string."Hello, World!"(SB), AX // 加载字符串常量\n MOVQ AX, (SP) // 将字符串常量传入参数区\n MOVQ $1, 8(SP) // 设置 slice 长度\n MOVQ $1, 16(SP) // 设置 slice 容量\n CALL runtime.convTstring(SB) // 调用 convTstring\n MOVQ 8(SP), AX // 加载转换后的 slice\n MOVQ AX, (SP) // 将 slice 作为参数\n CALL fmt.Println(SB) // 调用 fmt.Println\n MOVQ 24(SP), BP // 恢复 BP\n ADDQ $24, SP // 调整栈指针\n RET // 返回\n')])])]),s("p",[t._v("可以看到,Go 编译器后端生成了紧凑高效的汇编指令,包括字符串常量加载、参数传递、函数调用等操作。")]),t._v(" "),s("h3",{attrs:{id:"linker-示例"}},[t._v("Linker 示例")]),t._v(" "),s("p",[t._v("假设我们编译了以下两个 Go 源文件:")]),t._v(" "),s("div",{staticClass:"language-go extra-class"},[s("pre",{pre:!0,attrs:{class:"language-go"}},[s("code",[s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// file1.go")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fmt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Hello"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("sayHi")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// file2.go ")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("sayHi")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fmt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Hi"')]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("Go 编译器会先将它们分别编译为目标文件 "),s("code",[t._v("file1.o")]),t._v(" 和 "),s("code",[t._v("file2.o")]),t._v("。链接器 ("),s("code",[t._v("cmd/link")]),t._v(") 会将这两个目标文件以及需要的运行时库链接合并,生成最终的可执行文件 "),s("code",[t._v("main")]),t._v("。")]),t._v(" "),s("p",[t._v("该过程类似于:")]),t._v(" "),s("div",{staticClass:"language- extra-class"},[s("pre",{pre:!0,attrs:{class:"language-text"}},[s("code",[t._v("$ go tool compile -o file1.o file1.go\n$ go tool compile -o file2.o file2.go \n$ go tool link -o main file1.o file2.o\n")])])]),s("p",[t._v("最终输出的 "),s("code",[t._v("main")]),t._v(" 可执行文件中,包含了 "),s("code",[t._v("main")]),t._v(" 包定义、"),s("code",[t._v("fmt.Println")]),t._v(" 导入符号以及 "),s("code",[t._v("sayHi")]),t._v(" 函数实现等所有需要的代码和元数据。")]),t._v(" "),s("p",[t._v("通过这些实例,我们可以更好地理解 Go 编译器各组件的具体工作方式,以及它们是如何高效协作将 Go 源代码转换为机器码的。这种透明度和可阅读性也是 Go 编译器的一大优势。")]),t._v(" "),s("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),s("ul",[s("li",[t._v("https://go.dev/src/cmd/compile/README")]),t._v(" "),s("li",[t._v("https://golang.org/doc/ssa")]),t._v(" "),s("li",[t._v("https://go101.org/article/compiler.html")]),t._v(" "),s("li",[t._v("https://dmitri.shuralyov.com/idiomatic-go#compiler")])])])}),[],!1,null,null,null);n.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/113.befe1449.js b/assets/js/113.befe1449.js new file mode 100644 index 000000000..b93e01760 --- /dev/null +++ b/assets/js/113.befe1449.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[113],{544:function(c,g,t){"use strict";t.r(g);var n=t(36),o=Object(n.a)({},(function(){var c=this,g=c.$createElement,t=c._self._c||g;return t("ContentSlotsDistributor",{attrs:{"slot-key":c.$parent.slotKey}},[t("h1",{attrs:{id:"gccgo"}},[c._v("gccgo")]),c._v(" "),t("p",[c._v("gccgo 是 Gо 语言到 C 语言的编译器,它将 Gо 语言源代码编译成 C 语言源代码。")]),c._v(" "),t("p",[c._v("gccgo 本身只是一个编译器前端,负责解析 Gо 语言代码并生成 C 语言代码。要生成最终的可执行文件,还需要一个编译器后端。")]),c._v(" "),t("p",[c._v("gccgo 常见的后端有:")]),c._v(" "),t("p",[c._v("gcc - GNU 编译器,可以将 gccgo 生成的 C 语言代码进一步编译成机器代码,生成最终的可执行文件。\nclang - LLVM 编译器,同样可以将 C 语言编译成机器代码。\ntcc - Tiny C Compiler,一个小型快速的 C 语言编译器。\n所以简单来说,gccgo 编译器的常见后端是 gcc 和 clang。完整的编译流程是:")]),c._v(" "),t("p",[c._v("gccgo 前端 → C 语言代码 → gcc/clang 后端 → 机器代码 (可执行文件)")]),c._v(" "),t("p",[c._v("开发者可以根据需要选择不同的编译器后端,来编译 gccgo 生成的 C 语言代码。")])])}),[],!1,null,null,null);g.default=o.exports}}]); \ No newline at end of file diff --git a/assets/js/114.c126b120.js b/assets/js/114.c126b120.js new file mode 100644 index 000000000..9e70ff92a --- /dev/null +++ b/assets/js/114.c126b120.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[114],{546:function(l,v,_){"use strict";_.r(v);var o=_(36),L=Object(o.a)({},(function(){var l=this,v=l.$createElement,_=l._self._c||v;return _("ContentSlotsDistributor",{attrs:{"slot-key":l.$parent.slotKey}},[_("h1",{attrs:{id:"llvm"}},[l._v("llvm")]),l._v(" "),_("p",[l._v("LLVM 可以通过以下两种主要方式为 Go 语言代码提供编译支持:")]),l._v(" "),_("h2",{attrs:{id:"gollvm"}},[l._v("gollvm")]),l._v(" "),_("p",[l._v("gollvm 项目实现了将 Go 代码编译为 LLVM IR 的 frontend。它可以直接生成 LLVM IR,然后通过 LLVM 进行后端代码生成。")]),l._v(" "),_("p",[l._v("主要步骤是:")]),l._v(" "),_("ul",[_("li",[l._v("gollvm 解析 Go 代码,生成对应的 LLVM IR")]),l._v(" "),_("li",[l._v("执行 LLVM 优化流水线进行优化")]),l._v(" "),_("li",[l._v("LLVM 后端生成目标平台的机器码")]),l._v(" "),_("li",[l._v("嵌入 LLVM pass")])]),l._v(" "),_("h2",{attrs:{id:"可以通过修改-go-compiler-工具链-在编译过程中调用-llvm-pass-执行优化。"}},[l._v("可以通过修改 Go Compiler 工具链,在编译过程中调用 LLVM Pass 执行优化。")]),l._v(" "),_("p",[l._v("主要步骤是:")]),l._v(" "),_("ul",[_("li",[l._v("Go Compiler 前端生成 initial IR")]),l._v(" "),_("li",[l._v("将 IR 传递给 LLVM Pass 执行优化")]),l._v(" "),_("li",[l._v("LLVM Pass 输出优化后的 IR")]),l._v(" "),_("li",[l._v("Go Compiler 后端根据 IR 生成机器码")])]),l._v(" "),_("p",[l._v("这种方式需要 invasive 修改 Go 编译器,较为复杂。")]),l._v(" "),_("p",[l._v("总结:")]),l._v(" "),_("p",[l._v("gollvm 通过完全 External 的方式引入 LLVM,而嵌入 LLVM Pass 需要修改 Go Compiler。")]),l._v(" "),_("p",[l._v("gollvm 方式集成更简单,但是需要保证 IR 的转换正确性;嵌入 Pass 可以利用 Go Compiler 的 Context 信息进行更准确的优化。")]),l._v(" "),_("p",[l._v("根据需求和成本进行抉择。")]),l._v(" "),_("h2",{attrs:{id:"llvm-golang"}},[l._v("llvm -golang")]),l._v(" "),_("p",[l._v("llvm-golang 是另一个用于 Go 语言编译的 LLVM 集成项目。")]),l._v(" "),_("p",[l._v("它与 gollvm 的主要区别有:")]),l._v(" "),_("p",[l._v("实现方式不同")]),l._v(" "),_("ul",[_("li",[l._v("gollvm 是独立的 Go frontend,将 Go 代码编译成 LLVM IR")]),l._v(" "),_("li",[l._v("llvm-golang 直接使用 LLVM 对 Go 代码进行编译\n编译入口不同")]),l._v(" "),_("li",[l._v("gollvm 通过调用 gollvm 命令,传入 Go 源文件")]),l._v(" "),_("li",[l._v("llvm-golang 编译时调用 clang 命令,并使用-femulate-llvm-golang 参数\n编译过程不同")]),l._v(" "),_("li",[l._v("gollvm 编译生成完整的 LLVM IR 然后优化")]),l._v(" "),_("li",[l._v("llvm-golang 是逐函数生成 LLVM IR 并编译\n项目状态不同")]),l._v(" "),_("li",[l._v("gollvm 活跃维护,可正常使用")]),l._v(" "),_("li",[l._v("llvm-golang 最后更新在5年前,未维护\n总之,llvm-golang 是直接使用 LLVM 编译 Go 的早期尝试,但维护情况不佳。gollvm 作为独立 frontend 集成 LLVM,是更可靠的解决方案。")])]),l._v(" "),_("p",[l._v("但 llvm-golang 的直接编译方式也具有借鉴意义,合理结合两种方式可以获得更好的 LLVM 集成效果。")])])}),[],!1,null,null,null);v.default=L.exports}}]); \ No newline at end of file diff --git a/assets/js/115.773ca7ce.js b/assets/js/115.773ca7ce.js new file mode 100644 index 000000000..e5e6edd27 --- /dev/null +++ b/assets/js/115.773ca7ce.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[115],{547:function(t,n,i){"use strict";i.r(n);var l=i(36),r=Object(l.a)({},(function(){var t=this,n=t.$createElement,i=t._self._c||n;return i("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[i("h1",{attrs:{id:"实现一个编程语言-lulu"}},[t._v("实现一个编程语言 lulu")]),t._v(" "),i("h2",{attrs:{id:"编程语言前端"}},[t._v("编程语言前端")]),t._v(" "),i("h2",{attrs:{id:"编程语言后端"}},[t._v("编程语言后端")]),t._v(" "),i("h2",{attrs:{id:"参考文章"}},[t._v("参考文章")]),t._v(" "),i("ul",[i("li",[t._v("https://github.com/karminski/write-a-programming-language-in-450-lines")])])])}),[],!1,null,null,null);n.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/12.521ef871.js b/assets/js/12.521ef871.js new file mode 100644 index 000000000..d9005c7a5 --- /dev/null +++ b/assets/js/12.521ef871.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[12],{436:function(t,e,s){"use strict";s.r(e);var n=["There's nothing here.","How did we get here?","That's a Four-Oh-Four.","Looks like we've got some broken links."],o={methods:{getMsg:function(){return n[Math.floor(Math.random()*n.length)]}}},i=s(36),h=Object(i.a)(o,(function(){var t=this.$createElement,e=this._self._c||t;return e("div",{staticClass:"theme-container"},[e("div",{staticClass:"theme-default-content"},[e("h1",[this._v("404")]),this._v(" "),e("blockquote",[this._v(this._s(this.getMsg()))]),this._v(" "),e("RouterLink",{attrs:{to:"/"}},[this._v("\n Take me home.\n ")])],1)])}),[],!1,null,null,null);e.default=h.exports}}]); \ No newline at end of file diff --git a/assets/js/13.6a579e8b.js b/assets/js/13.6a579e8b.js new file mode 100644 index 000000000..ab5f3b89c --- /dev/null +++ b/assets/js/13.6a579e8b.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[13],{439:function(t,s,e){"use strict";e.r(s);var i=e(36),n=Object(i.a)({},(function(){var t=this.$createElement,s=this._self._c||t;return s("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}},[s("p",[s("a",{attrs:{name:"v0.3.0"}})]),this._v(" "),s("h2",{attrs:{id:"v0-3-0-2022-03-02"}},[this._v("v0.3.0 (2022-03-02)")]),this._v(" "),s("h2",{attrs:{id:"features"}},[this._v("Features")]),this._v(" "),s("p",[this._v("引入 ci")])])}),[],!1,null,null,null);s.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/14.46f64113.js b/assets/js/14.46f64113.js new file mode 100644 index 000000000..905e405e7 --- /dev/null +++ b/assets/js/14.46f64113.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[14],{440:function(t,r,e){"use strict";e.r(r);var E=e(36),a=Object(E.a)({},(function(){var t=this,r=t.$createElement,e=t._self._c||r;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("h1",{attrs:{id:"runtime"}},[t._v("runtime")]),t._v(" "),e("ul",[e("li",[e("a",{attrs:{href:"./%E4%B8%89%E8%89%B2gc%E7%AE%97%E6%B3%95"}},[t._v("三色 gc 算法")])]),t._v(" "),e("li",[e("a",{attrs:{href:"./%E5%A0%86%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D"}},[t._v("堆内存分配")])]),t._v(" "),e("li",[e("a",{attrs:{href:"./%E6%A0%88%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86"}},[t._v("栈内存管理")])]),t._v(" "),e("li",[e("a",{attrs:{href:"./%E7%B3%BB%E7%BB%9F%E7%9B%91%E6%8E%A7"}},[t._v("系统监控")])]),t._v(" "),e("li",[e("a",{attrs:{href:"./gmp"}},[t._v("G:M:P")])]),t._v(" "),e("li",[e("a",{attrs:{href:"./%E5%AE%9A%E6%97%B6%E5%99%A8"}},[t._v("定时器")])]),t._v(" "),e("li",[e("a",{attrs:{href:"./netpool"}},[t._v("netpool")])])])])}),[],!1,null,null,null);r.default=a.exports}}]); \ No newline at end of file diff --git a/assets/js/15.ccbbdb7a.js b/assets/js/15.ccbbdb7a.js new file mode 100644 index 000000000..c5f08e61b --- /dev/null +++ b/assets/js/15.ccbbdb7a.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[15],{442:function(t,i,e){"use strict";e.r(i);var n=e(36),s=Object(n.a)({},(function(){var t=this,i=t.$createElement,e=t._self._c||i;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("h1",{attrs:{id:"g-m-p"}},[t._v("G:M:P")]),t._v(" "),e("h2",{attrs:{id:"参考文章"}},[t._v("参考文章")]),t._v(" "),e("ul",[e("li",[t._v("https://mp.weixin.qq.com/s/iAy9ReQhnmCYUFvwYroGPA")]),t._v(" "),e("li",[t._v("https://www.bilibili.com/video/BV19r4y1w7Nx/")])])])}),[],!1,null,null,null);i.default=s.exports}}]); \ No newline at end of file diff --git a/assets/js/16.2a7384f8.js b/assets/js/16.2a7384f8.js new file mode 100644 index 000000000..a8f358aa0 --- /dev/null +++ b/assets/js/16.2a7384f8.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[16],{443:function(t,s,i){"use strict";i.r(s);var e=i(36),n=Object(e.a)({},(function(){var t=this.$createElement,s=this._self._c||t;return s("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}},[s("h1",{attrs:{id:"go-语言网络模型"}},[this._v("go 语言网络模型")]),this._v(" "),s("h2",{attrs:{id:"参考资料"}},[this._v("参考资料")]),this._v(" "),s("ul",[s("li",[this._v("https://mp.weixin.qq.com/s/jGrPM0UgqjZ10cXafoC0wQ")])])])}),[],!1,null,null,null);s.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/17.50442aef.js b/assets/js/17.50442aef.js new file mode 100644 index 000000000..25e4d159a --- /dev/null +++ b/assets/js/17.50442aef.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[17],{444:function(t,s,e){"use strict";e.r(s);var n=e(36),i=Object(n.a)({},(function(){var t=this.$createElement,s=this._self._c||t;return s("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}},[s("h2",{attrs:{id:"参考资料"}},[this._v("参考资料")]),this._v(" "),s("ul",[s("li",[this._v("https://draveness.me/golang/")])])])}),[],!1,null,null,null);s.default=i.exports}}]); \ No newline at end of file diff --git a/assets/js/18.4b8a1599.js b/assets/js/18.4b8a1599.js new file mode 100644 index 000000000..df2dce647 --- /dev/null +++ b/assets/js/18.4b8a1599.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[18],{445:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/19.d153f9b9.js b/assets/js/19.d153f9b9.js new file mode 100644 index 000000000..cea08861f --- /dev/null +++ b/assets/js/19.d153f9b9.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[19],{448:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/2.5c331e53.js b/assets/js/2.5c331e53.js new file mode 100644 index 000000000..a51712ce6 --- /dev/null +++ b/assets/js/2.5c331e53.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[2],{376:function(t,e,n){"use strict";n.d(e,"d",(function(){return i})),n.d(e,"a",(function(){return a})),n.d(e,"i",(function(){return s})),n.d(e,"f",(function(){return u})),n.d(e,"g",(function(){return l})),n.d(e,"h",(function(){return c})),n.d(e,"b",(function(){return h})),n.d(e,"e",(function(){return p})),n.d(e,"k",(function(){return f})),n.d(e,"l",(function(){return d})),n.d(e,"c",(function(){return v})),n.d(e,"j",(function(){return m}));n(32),n(97),n(377),n(127),n(209),n(206),n(96),n(99),n(10),n(100),n(45),n(130),n(204);var i=/#.*$/,r=/\.(md|html)$/,a=/\/$/,s=/^[a-z]+:/i;function o(t){return decodeURI(t).replace(i,"").replace(r,"")}function u(t){return s.test(t)}function l(t){return/^mailto:/.test(t)}function c(t){return/^tel:/.test(t)}function h(t){if(u(t))return t;var e=t.match(i),n=e?e[0]:"",r=o(t);return a.test(r)?t:r+".html"+n}function p(t,e){var n=decodeURIComponent(t.hash),r=function(t){var e=t.match(i);if(e)return e[0]}(e);return(!r||n===r)&&o(t.path)===o(e)}function f(t,e,n){if(u(e))return{type:"external",path:e};n&&(e=function(t,e,n){var i=t.charAt(0);if("/"===i)return t;if("?"===i||"#"===i)return e+t;var r=e.split("/");n&&r[r.length-1]||r.pop();for(var a=t.replace(/^\//,"").split("/"),s=0;s3&&void 0!==arguments[3]?arguments[3]:1;if("string"==typeof e)return f(n,e,i);if(Array.isArray(e))return Object.assign(f(n,e[0],i),{title:e[1]});var a=e.children||[];return 0===a.length&&e.path?Object.assign(f(n,e.path,i),{title:e.title}):{type:"group",path:e.path,title:e.title,sidebarDepth:e.sidebarDepth,initialOpenGroupIndex:e.initialOpenGroupIndex,children:a.map((function(e){return t(e,n,i,r+1)})),collapsable:!1!==e.collapsable}}(t,r,l)})):[]}return[]}function g(t){var e=v(t.headers||[]);return[{type:"group",collapsable:!1,title:t.title,path:null,children:e.map((function(e){return{type:"auto",title:e.title,basePath:t.path,path:t.path+"#"+e.slug,children:e.children||[]}}))}]}function v(t){var e;return(t=t.map((function(t){return Object.assign({},t)}))).forEach((function(t){2===t.level?e=t:e&&(e.children||(e.children=[])).push(t)})),t.filter((function(t){return 2===t.level}))}function m(t){return Object.assign(t,{type:t.items&&t.items.length?"links":"link"})}},377:function(t,e,n){"use strict";var i=n(6),r=n(137),a=n(11),s=n(69),o=n(15),u=n(25),l=n(46),c=n(138),h=n(139);r("match",(function(t,e,n){return[function(e){var n=u(this),r=null==e?void 0:l(e,t);return r?i(r,e,n):new RegExp(e)[t](o(n))},function(t){var i=a(this),r=o(t),u=n(e,i,r);if(u.done)return u.value;if(!i.global)return h(i,r);var l=i.unicode;i.lastIndex=0;for(var p,f=[],d=0;null!==(p=h(i,r));){var g=o(p[0]);f[d]=g,""===g&&(i.lastIndex=c(r,s(i.lastIndex),l)),d++}return 0===d?null:f}]}))},378:function(t,e,n){},379:function(t,e,n){"use strict";var i=n(1),r=n(207).trim;i({target:"String",proto:!0,forced:n(410)("trim")},{trim:function(){return r(this)}})},380:function(t,e,n){var i=n(7),r=n(0),a=n(2),s=n(101),o=n(133),u=n(20),l=n(47).f,c=n(33),h=n(135),p=n(15),f=n(381),d=n(131),g=n(212),v=n(14),m=n(3),b=n(9),k=n(37).enforce,_=n(211),x=n(5),C=n(205),y=n(214),$=x("match"),L=r.RegExp,w=L.prototype,O=r.SyntaxError,S=a(w.exec),I=a("".charAt),E=a("".replace),j=a("".indexOf),T=a("".slice),R=/^\?<[^\s\d!#%&*+<=>@^][^\s!#%&*+<=>@^]*>/,A=/a/g,N=/a/g,P=new L(A)!==A,D=d.MISSED_STICKY,U=d.UNSUPPORTED_Y,H=i&&(!P||D||C||y||m((function(){return N[$]=!1,L(A)!=A||L(N)==N||"/a/i"!=L(A,"i")})));if(s("RegExp",H)){for(var W=function(t,e){var n,i,r,a,s,l,d=c(w,this),g=h(t),v=void 0===e,m=[],_=t;if(!d&&g&&v&&t.constructor===W)return t;if((g||c(w,t))&&(t=t.source,v&&(e=f(_))),t=void 0===t?"":p(t),e=void 0===e?"":p(e),_=t,C&&"dotAll"in A&&(i=!!e&&j(e,"s")>-1)&&(e=E(e,/s/g,"")),n=e,D&&"sticky"in A&&(r=!!e&&j(e,"y")>-1)&&U&&(e=E(e,/y/g,"")),y&&(t=(a=function(t){for(var e,n=t.length,i=0,r="",a=[],s={},o=!1,u=!1,l=0,c="";i<=n;i++){if("\\"===(e=I(t,i)))e+=I(t,++i);else if("]"===e)o=!1;else if(!o)switch(!0){case"["===e:o=!0;break;case"("===e:S(R,T(t,i+1))&&(i+=2,u=!0),r+=e,l++;continue;case">"===e&&u:if(""===c||b(s,c))throw new O("Invalid capture group name");s[c]=!0,a[a.length]=[c,l],u=!1,c="";continue}u?c+=e:r+=e}return[r,a]}(t))[0],m=a[1]),s=o(L(t,e),d?this:w,W),(i||r||m.length)&&(l=k(s),i&&(l.dotAll=!0,l.raw=W(function(t){for(var e,n=t.length,i=0,r="",a=!1;i<=n;i++)"\\"!==(e=I(t,i))?a||"."!==e?("["===e?a=!0:"]"===e&&(a=!1),r+=e):r+="[\\s\\S]":r+=e+I(t,++i);return r}(t),n)),r&&(l.sticky=!0),m.length&&(l.groups=m)),t!==_)try{u(s,"source",""===_?"(?:)":_)}catch(t){}return s},G=l(L),B=0;G.length>B;)g(W,L,G[B++]);w.constructor=W,W.prototype=w,v(r,"RegExp",W,{constructor:!0})}_("RegExp")},381:function(t,e,n){var i=n(6),r=n(9),a=n(33),s=n(213),o=RegExp.prototype;t.exports=function(t){var e=t.flags;return void 0!==e||"flags"in o||r(t,"flags")||!a(o,t)?e:i(s,t)}},382:function(t,e,n){var i=n(0),r=n(7),a=n(205),s=n(26),o=n(383),u=n(37).get,l=RegExp.prototype,c=i.TypeError;r&&a&&o(l,"dotAll",{configurable:!0,get:function(){if(this!==l){if("RegExp"===s(this))return!!u(this).dotAll;throw c("Incompatible receiver, RegExp required")}}})},383:function(t,e,n){var i=n(210),r=n(12);t.exports=function(t,e,n){return n.get&&i(n.get,e,{getter:!0}),n.set&&i(n.set,e,{setter:!0}),r.f(t,e,n)}},384:function(t,e,n){var i=n(0),r=n(7),a=n(131).MISSED_STICKY,s=n(26),o=n(383),u=n(37).get,l=RegExp.prototype,c=i.TypeError;r&&a&&o(l,"sticky",{configurable:!0,get:function(){if(this!==l){if("RegExp"===s(this))return!!u(this).sticky;throw c("Incompatible receiver, RegExp required")}}})},385:function(t,e,n){"use strict";var i=n(98).PROPER,r=n(14),a=n(11),s=n(15),o=n(3),u=n(381),l=RegExp.prototype.toString,c=o((function(){return"/a/b"!=l.call({source:"a",flags:"b"})})),h=i&&"toString"!=l.name;(c||h)&&r(RegExp.prototype,"toString",(function(){var t=a(this);return"/"+s(t.source)+"/"+s(u(t))}),{unsafe:!0})},386:function(t,e,n){},387:function(t,e,n){},388:function(t,e,n){},389:function(t,e,n){},390:function(t,e,n){},391:function(t,e,n){},392:function(t,e){t.exports=function(t){return null==t}},393:function(t,e,n){},394:function(t,e,n){},395:function(t,e,n){},396:function(t,e,n){},397:function(t,e,n){},398:function(t,e,n){},402:function(t,e,n){"use strict";n.r(e);n(128),n(10);var i=n(376),r={name:"SidebarGroup",components:{DropdownTransition:n(403).a},props:["item","open","collapsable","depth"],beforeCreate:function(){this.$options.components.SidebarLinks=n(402).default},methods:{isActive:i.e}},a=(n(422),n(36)),s=Object(a.a)(r,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("section",{staticClass:"sidebar-group",class:[{collapsable:t.collapsable,"is-sub-group":0!==t.depth},"depth-"+t.depth]},[t.item.path?n("RouterLink",{staticClass:"sidebar-heading clickable",class:{open:t.open,active:t.isActive(t.$route,t.item.path)},attrs:{to:t.item.path},nativeOn:{click:function(e){return t.$emit("toggle")}}},[n("span",[t._v(t._s(t.item.title))]),t._v(" "),t.collapsable?n("span",{staticClass:"arrow",class:t.open?"down":"right"}):t._e()]):n("p",{staticClass:"sidebar-heading",class:{open:t.open},on:{click:function(e){return t.$emit("toggle")}}},[n("span",[t._v(t._s(t.item.title))]),t._v(" "),t.collapsable?n("span",{staticClass:"arrow",class:t.open?"down":"right"}):t._e()]),t._v(" "),n("DropdownTransition",[t.open||!t.collapsable?n("SidebarLinks",{staticClass:"sidebar-group-items",attrs:{items:t.item.children,"sidebar-depth":t.item.sidebarDepth,"initial-open-group-index":t.item.initialOpenGroupIndex,depth:t.depth+1}}):t._e()],1)],1)}),[],!1,null,null,null).exports;n(423),n(32),n(127),n(96);function o(t,e,n,i,r){var a={props:{to:e,activeClass:"",exactActiveClass:""},class:{active:i,"sidebar-link":!0}};return r>2&&(a.style={"padding-left":r+"rem"}),t("RouterLink",a,n)}function u(t,e,n,r,a){var s=arguments.length>5&&void 0!==arguments[5]?arguments[5]:1;return!e||s>a?null:t("ul",{class:"sidebar-sub-headers"},e.map((function(e){var l=Object(i.e)(r,n+"#"+e.slug);return t("li",{class:"sidebar-sub-header"},[o(t,n+"#"+e.slug,e.title,l,e.level-1),u(t,e.children,n,r,a,s+1)])})))}var l={functional:!0,props:["item","sidebarDepth"],render:function(t,e){var n=e.parent,r=n.$page,a=(n.$site,n.$route),s=n.$themeConfig,l=n.$themeLocaleConfig,c=e.props,h=c.item,p=c.sidebarDepth,f=Object(i.e)(a,h.path),d="auto"===h.type?f||h.children.some((function(t){return Object(i.e)(a,h.basePath+"#"+t.slug)})):f,g="external"===h.type?function(t,e,n){return t("a",{attrs:{href:e,target:"_blank",rel:"noopener noreferrer"},class:{"sidebar-link":!0}},[n,t("OutboundLink")])}(t,h.path,h.title||h.path):o(t,h.path,h.title||h.path,d),v=[r.frontmatter.sidebarDepth,p,l.sidebarDepth,s.sidebarDepth,1].find((function(t){return void 0!==t})),m=l.displayAllHeaders||s.displayAllHeaders;return"auto"===h.type?[g,u(t,h.children,h.basePath,a,v)]:(d||m)&&h.headers&&!i.d.test(h.path)?[g,u(t,Object(i.c)(h.headers),h.path,a,v)]:g}};n(424);function c(t,e){if("group"===e.type){var n=e.path&&Object(i.e)(t,e.path),r=e.children.some((function(e){return"group"===e.type?c(t,e):"page"===e.type&&Object(i.e)(t,e.path)}));return n||r}return!1}var h={name:"SidebarLinks",components:{SidebarGroup:s,SidebarLink:Object(a.a)(l,void 0,void 0,!1,null,null,null).exports},props:["items","depth","sidebarDepth","initialOpenGroupIndex"],data:function(){return{openGroupIndex:this.initialOpenGroupIndex||0}},watch:{$route:function(){this.refreshIndex()}},created:function(){this.refreshIndex()},methods:{refreshIndex:function(){var t=function(t,e){for(var n=0;n-1&&(this.openGroupIndex=t)},toggleGroup:function(t){this.openGroupIndex=t===this.openGroupIndex?-1:t},isActive:function(t){return Object(i.e)(this.$route,t.regularPath)}}},p=Object(a.a)(h,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return t.items.length?n("ul",{staticClass:"sidebar-links"},t._l(t.items,(function(e,i){return n("li",{key:i},["group"===e.type?n("SidebarGroup",{attrs:{item:e,open:i===t.openGroupIndex,collapsable:e.collapsable||e.collapsible,depth:t.depth},on:{toggle:function(e){return t.toggleGroup(i)}}}):n("SidebarLink",{attrs:{"sidebar-depth":t.sidebarDepth,item:e}})],1)})),0):t._e()}),[],!1,null,null,null);e.default=p.exports},403:function(t,e,n){"use strict";var i={name:"DropdownTransition",methods:{setHeight:function(t){t.style.height=t.scrollHeight+"px"},unsetHeight:function(t){t.style.height=""}}},r=(n(414),n(36)),a=Object(r.a)(i,(function(){var t=this.$createElement;return(this._self._c||t)("transition",{attrs:{name:"dropdown"},on:{enter:this.setHeight,"after-enter":this.unsetHeight,"before-leave":this.setHeight}},[this._t("default")],2)}),[],!1,null,null,null);e.a=a.exports},404:function(t,e,n){"use strict";var i=n(1),r=n(405);i({target:"String",proto:!0,forced:n(406)("link")},{link:function(t){return r(this,"a","href",t)}})},405:function(t,e,n){var i=n(2),r=n(25),a=n(15),s=/"/g,o=i("".replace);t.exports=function(t,e,n,i){var u=a(r(t)),l="<"+e;return""!==n&&(l+=" "+n+'="'+o(a(i),s,""")+'"'),l+">"+u+""}},406:function(t,e,n){var i=n(3);t.exports=function(t){return i((function(){var e=""[t]('"');return e!==e.toLowerCase()||e.split('"').length>3}))}},407:function(t,e,n){"use strict";n(378)},408:function(t,e,n){var i=n(1),r=n(409);i({global:!0,forced:parseInt!=r},{parseInt:r})},409:function(t,e,n){var i=n(0),r=n(3),a=n(2),s=n(15),o=n(207).trim,u=n(208),l=i.parseInt,c=i.Symbol,h=c&&c.iterator,p=/^[+-]?0x/i,f=a(p.exec),d=8!==l(u+"08")||22!==l(u+"0x16")||h&&!r((function(){l(Object(h))}));t.exports=d?function(t,e){var n=o(s(t));return l(n,e>>>0||(f(p,n)?16:10))}:l},410:function(t,e,n){var i=n(98).PROPER,r=n(3),a=n(208);t.exports=function(t){return r((function(){return!!a[t]()||"​…᠎"!=="​…᠎"[t]()||i&&a[t].name!==t}))}},411:function(t,e,n){"use strict";var i,r=n(1),a=n(2),s=n(34).f,o=n(69),u=n(15),l=n(134),c=n(25),h=n(136),p=n(16),f=a("".endsWith),d=a("".slice),g=Math.min,v=h("endsWith");r({target:"String",proto:!0,forced:!!(p||v||(i=s(String.prototype,"endsWith"),!i||i.writable))&&!v},{endsWith:function(t){var e=u(c(this));l(t);var n=arguments.length>1?arguments[1]:void 0,i=e.length,r=void 0===n?i:g(o(n),i),a=u(t);return f?f(e,a,r):d(e,r-a.length,r)===a}})},412:function(t,e,n){"use strict";n(386)},413:function(t,e,n){"use strict";n(387)},414:function(t,e,n){"use strict";n(388)},415:function(t,e,n){"use strict";n(389)},416:function(t,e,n){"use strict";n(390)},417:function(t,e,n){"use strict";n(391)},418:function(t,e,n){"use strict";n(393)},419:function(t,e,n){var i=n(49),r=n(21),a=n(38);t.exports=function(t){return"string"==typeof t||!r(t)&&a(t)&&"[object String]"==i(t)}},420:function(t,e,n){"use strict";n(394)},421:function(t,e,n){"use strict";n(395)},422:function(t,e,n){"use strict";n(396)},423:function(t,e,n){"use strict";var i=n(1),r=n(48).find,a=n(132),s=!0;"find"in[]&&Array(1).find((function(){s=!1})),i({target:"Array",proto:!0,forced:s},{find:function(t){return r(this,t,arguments.length>1?arguments[1]:void 0)}}),a("find")},424:function(t,e,n){"use strict";n(397)},425:function(t,e,n){"use strict";n(398)},435:function(t,e,n){"use strict";n.r(e);n(404),n(128),n(10),n(129);var i=n(376),r={name:"NavLink",props:{item:{required:!0}},computed:{link:function(){return Object(i.b)(this.item.link)},exact:function(){var t=this;return this.$site.locales?Object.keys(this.$site.locales).some((function(e){return e===t.link})):"/"===this.link},isNonHttpURI:function(){return Object(i.g)(this.link)||Object(i.h)(this.link)},isBlankTarget:function(){return"_blank"===this.target},isInternal:function(){return!Object(i.f)(this.link)&&!this.isBlankTarget},target:function(){return this.isNonHttpURI?null:this.item.target?this.item.target:Object(i.f)(this.link)?"_blank":""},rel:function(){return this.isNonHttpURI||!1===this.item.rel?null:this.item.rel?this.item.rel:this.isBlankTarget?"noopener noreferrer":null}},methods:{focusoutAction:function(){this.$emit("focusout")}}},a=n(36),s=Object(a.a)(r,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return t.isInternal?n("RouterLink",{staticClass:"nav-link",attrs:{to:t.link,exact:t.exact},nativeOn:{focusout:function(e){return t.focusoutAction.apply(null,arguments)}}},[t._v("\n "+t._s(t.item.text)+"\n")]):n("a",{staticClass:"nav-link external",attrs:{href:t.link,target:t.target,rel:t.rel},on:{focusout:t.focusoutAction}},[t._v("\n "+t._s(t.item.text)+"\n "),t.isBlankTarget?n("OutboundLink"):t._e()],1)}),[],!1,null,null,null).exports,o={name:"Home",components:{NavLink:s},computed:{data:function(){return this.$page.frontmatter},actionLink:function(){return{link:this.data.actionLink,text:this.data.actionText}}}},u=(n(407),Object(a.a)(o,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("main",{staticClass:"home",attrs:{"aria-labelledby":null!==t.data.heroText?"main-title":null}},[n("header",{staticClass:"hero"},[t.data.heroImage?n("img",{attrs:{src:t.$withBase(t.data.heroImage),alt:t.data.heroAlt||"hero"}}):t._e(),t._v(" "),null!==t.data.heroText?n("h1",{attrs:{id:"main-title"}},[t._v("\n "+t._s(t.data.heroText||t.$title||"Hello")+"\n ")]):t._e(),t._v(" "),null!==t.data.tagline?n("p",{staticClass:"description"},[t._v("\n "+t._s(t.data.tagline||t.$description||"Welcome to your VuePress site")+"\n ")]):t._e(),t._v(" "),t.data.actionText&&t.data.actionLink?n("p",{staticClass:"action"},[n("NavLink",{staticClass:"action-button",attrs:{item:t.actionLink}})],1):t._e()]),t._v(" "),t.data.features&&t.data.features.length?n("div",{staticClass:"features"},t._l(t.data.features,(function(e,i){return n("div",{key:i,staticClass:"feature"},[n("h2",[t._v(t._s(e.title))]),t._v(" "),n("p",[t._v(t._s(e.details))])])})),0):t._e(),t._v(" "),n("Content",{staticClass:"theme-default-content custom"}),t._v(" "),t.data.footer?n("div",{staticClass:"footer"},[t._v("\n "+t._s(t.data.footer)+"\n ")]):n("Content",{staticClass:"footer",attrs:{"slot-key":"footer"}})],1)}),[],!1,null,null,null).exports),l=(n(408),n(379),n(204),n(130),n(45),n(32),n(377),n(215),n(216),n(206),n(97),n(380),n(382),n(384),n(385),n(96),n(209),n(127),n(411),n(218)),c=n.n(l),h=function(t,e){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null,i=c()(e,"title","");return c()(e,"frontmatter.tags")&&(i+=" ".concat(e.frontmatter.tags.join(" "))),n&&(i+=" ".concat(n)),p(t,i)},p=function(t,e){var n=function(t){return t.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&")},i=new RegExp("[^\0-]"),r=t.split(/\s+/g).map((function(t){return t.trim()})).filter((function(t){return!!t}));if(i.test(t))return r.some((function(t){return e.toLowerCase().indexOf(t)>-1}));var a=t.endsWith(" ");return new RegExp(r.map((function(t,e){return r.length!==e+1||a?"(?=.*\\b".concat(n(t),"\\b)"):"(?=.*\\b".concat(n(t),")")})).join("")+".+","gi").test(e)},f={name:"SearchBox",data:function(){return{query:"",focused:!1,focusIndex:0,placeholder:void 0}},computed:{showSuggestions:function(){return this.focused&&this.suggestions&&this.suggestions.length},suggestions:function(){var t=this.query.trim().toLowerCase();if(t){for(var e=this.$site.pages,n=this.$site.themeConfig.searchMaxSuggestions||5,i=this.$localePath,r=[],a=0;a=n);a++){var s=e[a];if(this.getPageLocalePath(s)===i&&this.isSearchable(s))if(h(t,s))r.push(s);else if(s.headers)for(var o=0;o=n);o++){var u=s.headers[o];u.title&&h(t,s,u.title)&&r.push(Object.assign({},s,{path:s.path+"#"+u.slug,header:u}))}}return r}},alignRight:function(){return(this.$site.themeConfig.nav||[]).length+(this.$site.repo?1:0)<=2}},mounted:function(){this.placeholder=this.$site.themeConfig.searchPlaceholder||"",document.addEventListener("keydown",this.onHotkey)},beforeDestroy:function(){document.removeEventListener("keydown",this.onHotkey)},methods:{getPageLocalePath:function(t){for(var e in this.$site.locales||{})if("/"!==e&&0===t.path.indexOf(e))return e;return"/"},isSearchable:function(t){var e=null;return null===e||(e=Array.isArray(e)?e:new Array(e)).filter((function(e){return t.path.match(e)})).length>0},onHotkey:function(t){t.srcElement===document.body&&["s","/"].includes(t.key)&&(this.$refs.input.focus(),t.preventDefault())},onUp:function(){this.showSuggestions&&(this.focusIndex>0?this.focusIndex--:this.focusIndex=this.suggestions.length-1)},onDown:function(){this.showSuggestions&&(this.focusIndex "+t._s(e.header.title))]):t._e()])])})),0):t._e()])}),[],!1,null,null,null).exports),g=(n(413),Object(a.a)({},(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"sidebar-button",on:{click:function(e){return t.$emit("toggle-sidebar")}}},[n("svg",{staticClass:"icon",attrs:{xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",role:"img",viewBox:"0 0 448 512"}},[n("path",{attrs:{fill:"currentColor",d:"M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"}})])])}),[],!1,null,null,null).exports),v=n(67),m=(n(217),n(403)),b=n(219),k=n.n(b),_={name:"DropdownLink",components:{NavLink:s,DropdownTransition:m.a},props:{item:{required:!0}},data:function(){return{open:!1}},computed:{dropdownAriaLabel:function(){return this.item.ariaLabel||this.item.text}},watch:{$route:function(){this.open=!1}},methods:{setOpen:function(t){this.open=t},isLastItemOfArray:function(t,e){return k()(e)===t},handleDropdown:function(){0===event.detail&&this.setOpen(!this.open)}}},x=(n(415),{name:"NavLinks",components:{NavLink:s,DropdownLink:Object(a.a)(_,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"dropdown-wrapper",class:{open:t.open}},[n("button",{staticClass:"dropdown-title",attrs:{type:"button","aria-label":t.dropdownAriaLabel},on:{click:t.handleDropdown}},[n("span",{staticClass:"title"},[t._v(t._s(t.item.text))]),t._v(" "),n("span",{staticClass:"arrow down"})]),t._v(" "),n("button",{staticClass:"mobile-dropdown-title",attrs:{type:"button","aria-label":t.dropdownAriaLabel},on:{click:function(e){return t.setOpen(!t.open)}}},[n("span",{staticClass:"title"},[t._v(t._s(t.item.text))]),t._v(" "),n("span",{staticClass:"arrow",class:t.open?"down":"right"})]),t._v(" "),n("DropdownTransition",[n("ul",{directives:[{name:"show",rawName:"v-show",value:t.open,expression:"open"}],staticClass:"nav-dropdown"},t._l(t.item.items,(function(e,i){return n("li",{key:e.link||i,staticClass:"dropdown-item"},["links"===e.type?n("h4",[t._v("\n "+t._s(e.text)+"\n ")]):t._e(),t._v(" "),"links"===e.type?n("ul",{staticClass:"dropdown-subitem-wrapper"},t._l(e.items,(function(i){return n("li",{key:i.link,staticClass:"dropdown-subitem"},[n("NavLink",{attrs:{item:i},on:{focusout:function(n){t.isLastItemOfArray(i,e.items)&&t.isLastItemOfArray(e,t.item.items)&&t.setOpen(!1)}}})],1)})),0):n("NavLink",{attrs:{item:e},on:{focusout:function(n){t.isLastItemOfArray(e,t.item.items)&&t.setOpen(!1)}}})],1)})),0)])],1)}),[],!1,null,null,null).exports},computed:{userNav:function(){return this.$themeLocaleConfig.nav||this.$site.themeConfig.nav||[]},nav:function(){var t=this,e=this.$site.locales;if(e&&Object.keys(e).length>1){var n=this.$page.path,i=this.$router.options.routes,r=this.$site.themeConfig.locales||{},a={text:this.$themeLocaleConfig.selectText||"Languages",ariaLabel:this.$themeLocaleConfig.ariaLabel||"Select language",items:Object.keys(e).map((function(a){var s,o=e[a],u=r[a]&&r[a].label||o.lang;return o.lang===t.$lang?s=n:(s=n.replace(t.$localeConfig.path,a),i.some((function(t){return t.path===s}))||(s=a)),{text:u,link:s}}))};return[].concat(Object(v.a)(this.userNav),[a])}return this.userNav},userLinks:function(){return(this.nav||[]).map((function(t){return Object.assign(Object(i.j)(t),{items:(t.items||[]).map(i.j)})}))},repoLink:function(){var t=this.$site.themeConfig.repo;return t?/^https?:/.test(t)?t:"https://github.com/".concat(t):null},repoLabel:function(){if(this.repoLink){if(this.$site.themeConfig.repoLabel)return this.$site.themeConfig.repoLabel;for(var t=this.repoLink.match(/^https?:\/\/[^/]+/)[0],e=["GitHub","GitLab","Bitbucket"],n=0;nMath.abs(n)&&Math.abs(e)>40&&(e>0&&this.touchStart.x<=80?this.toggleSidebar(!0):this.toggleSidebar(!1))}}}),G=Object(a.a)(W,(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("div",{staticClass:"theme-container",class:t.pageClasses,on:{touchstart:t.onTouchStart,touchend:t.onTouchEnd}},[t.shouldShowNavbar?n("Navbar",{on:{"toggle-sidebar":t.toggleSidebar}}):t._e(),t._v(" "),n("div",{staticClass:"sidebar-mask",on:{click:function(e){return t.toggleSidebar(!1)}}}),t._v(" "),n("Sidebar",{attrs:{items:t.sidebarItems},on:{"toggle-sidebar":t.toggleSidebar},scopedSlots:t._u([{key:"top",fn:function(){return[t._t("sidebar-top")]},proxy:!0},{key:"bottom",fn:function(){return[t._t("sidebar-bottom")]},proxy:!0}],null,!0)}),t._v(" "),t.$page.frontmatter.home?n("Home"):n("Page",{attrs:{"sidebar-items":t.sidebarItems},scopedSlots:t._u([{key:"top",fn:function(){return[t._t("page-top")]},proxy:!0},{key:"bottom",fn:function(){return[t._t("page-bottom")]},proxy:!0}],null,!0)})],1)}),[],!1,null,null,null);e.default=G.exports}}]); \ No newline at end of file diff --git a/assets/js/20.fea9a837.js b/assets/js/20.fea9a837.js new file mode 100644 index 000000000..6a2956ba4 --- /dev/null +++ b/assets/js/20.fea9a837.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[20],{447:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/21.6c80f263.js b/assets/js/21.6c80f263.js new file mode 100644 index 000000000..add7ed4d0 --- /dev/null +++ b/assets/js/21.6c80f263.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[21],{446:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/22.821a2e8d.js b/assets/js/22.821a2e8d.js new file mode 100644 index 000000000..543efeebe --- /dev/null +++ b/assets/js/22.821a2e8d.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[22],{449:function(t,E,r){"use strict";r.r(E);var a=r(36),e=Object(a.a)({},(function(){var t=this,E=t.$createElement,r=t._self._c||E;return r("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[r("h1",{attrs:{id:"基础"}},[t._v("基础")]),t._v(" "),r("ul",[r("li",[r("RouterLink",{attrs:{to:"/基础/helloWorld/"}},[t._v("hello world")])],1),t._v(" "),r("li",[r("a",{attrs:{href:"./%E5%8F%98%E9%87%8F%E5%A3%B0%E6%98%8E"}},[t._v("变量声明")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./%E5%B8%B8%E9%87%8F%E5%A3%B0%E6%98%8E"}},[t._v("常量声明")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./%E9%9B%B6%E5%80%BC"}},[t._v("零值")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./%E5%A4%8D%E5%90%88%E5%AD%97%E9%9D%A2%E9%87%8F"}},[t._v("复合字面量")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./%E6%95%B0%E5%AD%97%E7%B1%BB%E5%9E%8B"}},[t._v("数字类型")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./%E7%BB%93%E6%9E%84%E4%BD%93"}},[t._v("结构体")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./slice"}},[t._v("slice")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./string"}},[t._v("string")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./map"}},[t._v("map")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./%E6%B1%82%E5%80%BC%E9%A1%BA%E5%BA%8F"}},[t._v("求值顺序")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./%E4%BD%9C%E7%94%A8%E5%9F%9F"}},[t._v("作用域")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./%E5%87%BD%E6%95%B0%E6%96%B9%E6%B3%95"}},[t._v("函数方法")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./interface"}},[t._v("接口")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./%E9%80%BB%E8%BE%91%E5%92%8C%E5%88%A4%E6%96%AD%E8%AF%AD%E5%8F%A5"}},[t._v("逻辑和判断语句")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./%E6%B3%9B%E5%9E%8B"}},[t._v("泛型")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86"}},[t._v("错误处理")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./%E5%85%B6%E4%BB%96%E5%86%85%E5%AE%B9"}},[t._v("其他内容")])])])])}),[],!1,null,null,null);E.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/23.6633ca8a.js b/assets/js/23.6633ca8a.js new file mode 100644 index 000000000..56cc5fce3 --- /dev/null +++ b/assets/js/23.6633ca8a.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[23],{450:function(t,s,n){"use strict";n.r(s);var a=n(36),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"hello-world"}},[t._v("hello world")]),t._v(" "),n("h2",{attrs:{id:"诞生背景"}},[t._v("诞生背景")]),t._v(" "),n("p",[t._v("Go 语言是 Google 的 Robert Griesemer,Rob Pike 和 Ken Thompson 于 2007 年开始设计开发的一门编程语言。Go 语言吸收了 C 语言的表达能力,并添加了像动态语言中的并发性等特性,目的是让编程更简单高效。")]),t._v(" "),n("h2",{attrs:{id:"语言哲学"}},[t._v("语言哲学")]),t._v(" "),n("p",[t._v("Go 语言编程哲学如下:")]),t._v(" "),n("ul",[n("li",[t._v("面向接口编程")]),t._v(" "),n("li",[t._v("组合替换继承")]),t._v(" "),n("li",[t._v("简单优于复杂")]),t._v(" "),n("li",[t._v("内置并发支持")])]),t._v(" "),n("h2",{attrs:{id:"语言特点"}},[t._v("语言特点")]),t._v(" "),n("p",[t._v("Go 语言的主要特点有:")]),t._v(" "),n("ul",[n("li",[t._v("语法简单,容易学习使用")]),t._v(" "),n("li",[t._v("编译非常迅速,执行速度较快")]),t._v(" "),n("li",[t._v("静态类型语言 - 变量和函数需要声明类型")]),t._v(" "),n("li",[t._v("垃圾回收 - 自动的内存管理,不需要手动释放内存")]),t._v(" "),n("li",[t._v("并发支持 - 原生支持 goroutine 和 channel")]),t._v(" "),n("li",[t._v("简洁规范 - 语法简单一致,容易阅读理解")]),t._v(" "),n("li",[t._v("代码风格统一 - 提供了标准的代码风格")])]),t._v(" "),n("h2",{attrs:{id:"安装-go"}},[t._v("安装 Go")]),t._v(" "),n("p",[t._v("要安装 Go 语言,可以去官网 https://go.dev 下载最新的安装包。")]),t._v(" "),n("p",[t._v("安装非常简单,一般直接使用默认设置即可完成安装。")]),t._v(" "),n("p",[t._v("安装完成后,可以使用 "),n("code",[t._v("go version")]),t._v(" 命令检查安装是否成功。")]),t._v(" "),n("h2",{attrs:{id:"hello-world-2"}},[t._v("hello world")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tch "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Hello World"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\tch "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("ch\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/24.5a95c868.js b/assets/js/24.5a95c868.js new file mode 100644 index 000000000..f917f38f1 --- /dev/null +++ b/assets/js/24.5a95c868.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[24],{452:function(t,s,n){"use strict";n.r(s);var a=n(36),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"接口"}},[t._v("接口")]),t._v(" "),n("blockquote",[n("p",[t._v("有关泛型相关的约束内容均放置在"),n("a",{attrs:{href:"../%E6%B3%9B%E5%9E%8B"}},[t._v("泛型")]),t._v("这一章节,这里不再赘述")])]),t._v(" "),n("p",[t._v("重点内容前情提要")]),t._v(" "),n("ul",[n("li",[t._v("方法集合决定接口的实现")]),t._v(" "),n("li",[t._v("接口的嵌入")]),t._v(" "),n("li",[t._v("类型断言")]),t._v(" "),n("li",[t._v("接口类型的底层")]),t._v(" "),n("li",[t._v("如何判断接口类型的相等")]),t._v(" "),n("li",[t._v("论述 “nil error != nil” 的原因")]),t._v(" "),n("li",[t._v("小接口的意义")]),t._v(" "),n("li",[t._v("不可滥用空接口")]),t._v(" "),n("li",[t._v("接口作为程序水平组合的连接点,提供程序的可扩展性")]),t._v(" "),n("li",[t._v("接口提供程序的可测试性")]),t._v(" "),n("li",[t._v("接口的严格对比函数的宽松")])]),t._v(" "),n("p",[t._v("接口,go 语言提供的,用于抽象以及数据解耦的组件,"),n("strong",[t._v("在操作接口时,go 语言要求的严格程度远大于函数和方法。")])]),t._v(" "),n("p",[t._v("在 go1.18+ 中接口的概念从包含抽象的方法变成了类型,但是我们在外部可以操作的接口仍然只能是经典接口,不过经典接口是可以当做约束在内部使用的,不过官方不推荐经典接口当做一般约束那种使用方式。")]),t._v(" "),n("p",[t._v("go 语言为了区分,将传统接口称之为接口,将扩展的接口称之为约束,实际上传统接口是约束概念的子集。扩展的约束并不能在函数或者方法以及类型之外使用。")]),t._v(" "),n("p",[t._v("这一章我们只介绍经典接口的基本概念和使用方法。")]),t._v(" "),n("p",[t._v("接口具有静态语言和动态语言的共同特点。")]),t._v(" "),n("p",[t._v("静态类型特点:")]),t._v(" "),n("ul",[n("li",[t._v("接口可以作为类型:"),n("code",[t._v("var e error")])]),t._v(" "),n("li",[t._v("支持在编译期进行类型检查:在编译器期间会对右边的变量进行类型的检查,检查是否实现了接口定义的方法类型中的所有方法")])]),t._v(" "),n("p",[t._v("动态类型的特点:")]),t._v(" "),n("ul",[n("li",[t._v("在运行时可以对接口进行动态赋值")]),t._v(" "),n("li",[t._v("接口类型可以在运行时被赋予不同的动态类型变量,从而进行 “多态”")])]),t._v(" "),n("h2",{attrs:{id:"方法集合决定接口的实现"}},[t._v("方法集合决定接口的实现")]),t._v(" "),n("p",[t._v("通常,我们使用下面这种形式去完成接口的实现")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Writer "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Write")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" BufferWrite "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tvalue bytes"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Buffer\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("BufferWrite"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Write")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("p "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("我们知道,go 语言规定,不可跨包去实现类型的方法,所以我们只讨论自定义的类型与接口之间的关系,首先抛出结论:松耦合关系。")]),t._v(" "),n("p",[t._v("go 语言使用一种叫做鸭子理论的方式去实现接口:只要实现了接口的方法 (go 泛型,go1.18+, 理论从方法改为了类型) 就算是实现了这个接口,这属于隐式接口实现。即:接口和它的实现者之间为正交关系。")]),t._v(" "),n("p",[t._v("通常来说,例如 es6,Java 等都是要显式的说明实现了哪个接口的,但是 go 不需要,它只需要实现方法即可,而且,它还可以实现多个接口,毕竟多实现几个方法就可以算是实现了这个接口。由此还可以推断出,go 可以多实现方法,而不会影响对接口的实现。")]),t._v(" "),n("p",[t._v("这里要说明一下,go 语言对于定义在指针类型上的变量有语法糖:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Writer "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("set")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Student "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s Student"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Student"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("set")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" t Student\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" tp "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("new")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Student"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" w Writer \n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ❌")]),t._v("\n\tw "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" t\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ✅")]),t._v("\n\tw "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" tp\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("我们可以看到,get 和 set 分别是一个值类型和一个指针类型上实现的,这里我们的结论是:当实现接口时,"),n("strong",[t._v("类型的指针变量")]),t._v("在实现方法上可以包括"),n("em",[n("strong",[t._v("定义在类型指针上的方法以及定义在值类型上的方法***,但是")]),t._v("值类型变量")]),n("em",[t._v("只包含定义在")]),n("em",[t._v("值类型")]),t._v("*上的方法")]),t._v(" "),n("p",[t._v("这里是提示信息:")]),t._v(" "),n("div",{staticClass:"language-bash extra-class"},[n("pre",{pre:!0,attrs:{class:"language-bash"}},[n("code",[t._v("cannot use t "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("variable of "),n("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("type")]),t._v(" Student"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" as Writer value "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" assignment: Student does not implement Writer "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("method "),n("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("set")]),t._v(" has pointer receiver"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),n("p",[t._v("通常来说,这不应该成为程序员的烦恼,所以想用谁就好好的定义在谁上面的方法即可,完全不会出错。")]),t._v(" "),n("h2",{attrs:{id:"接口嵌入"}},[t._v("接口嵌入")]),t._v(" "),n("h3",{attrs:{id:"在接口中嵌入接口类型"}},[t._v("在接口中嵌入接口类型")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Writer "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Write")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Error1 "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" WriterError "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tWriter\n\tError1\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("这样的组合就可以组合成一个新的接口,并且嵌入的接口还可以有方法上的交集,go 是不介意的 (go1.14+)。")]),t._v(" "),n("h3",{attrs:{id:"在结构体中嵌入接口类型"}},[t._v("在结构体中嵌入接口类型")]),t._v(" "),n("p",[t._v("在结构体中嵌入一个接口,就相当于实现了这个接口 (当然结构体的指针类型也实现了这个接口),拥有了它的方法,但是注意,拥有的是这种抽象方法,那么我们为什么要将一个接口类型嵌入到一个结构体中呢?")]),t._v(" "),n("p",[t._v("因为 go 语言规定,嵌入接口,结构体相当于实现了这些接口,这里注意,这里必须是直接嵌入,如果是接口类型作为变量类型的方式是不能拥有接口的方法的。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("set")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ✅")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A1 "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tA\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ❌")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A2 "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tA A\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ❌")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A3 "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ta A\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[n("em",[n("strong",[t._v("当结构体本身也实现了方法时,优先调用结构体的方法。")])])]),t._v(" "),n("p",[t._v("这个场景是这样的,在某个函数中,它的参数是一个接口类型,并且这个函数调用的只是这个接口类型的某个,或几个方法,并不是全部,那么我们作为结构体,想实现这个接口,又不想多实现额外的方法,那么这种方法就很好用了。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("new")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("A"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("D")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tBI\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" BI "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("set")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("A"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hi"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 如果想调用被嵌入的接口的方法可以这么用,")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 我们知道调用直接嵌入的对象时候,变量名称默等于后面的类型名称,")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 看下面演示")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// var a A")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// a.BI.get()")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("D")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b BI"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tb"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("p",[t._v("注意上文提到了,接口中内嵌接口的时候,内嵌的接口方法可以重复,但是结构体中内嵌的接口,不允许出现方法重复的问题:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ❌ : ambiguous selector a.get")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tBI\n\tBI1\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" BI "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("set")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" BI1 "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("err")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("不过,要想解决这个问题,我们只需要让结构体实现这种重复的方法即可,这样,优先级就提升到了结构体,接口的方法就不会被调用了。比如:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a A\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("D")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("D1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tBI\n\tBI1\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" BI "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("set")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" BI1 "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("err")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("A"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hi"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("D")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b BI"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tb"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("D1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b BI1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tb"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("p",[t._v("下面让我们看一下,这种用法在单元测试场景中的应用")]),t._v(" "),n("p",[t._v("让我们描述一个场景:")]),t._v(" "),n("p",[t._v("有一个函数,它接受一个接口类型作为参数,我们要对它进行单元测试,而且我们要伪造一些数据。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 函数体")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("MaleCount")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s stmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tresult"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Exec")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"SELECT count(*) FROM exployee_tab WHERE gender=?"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"1"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" \n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" result"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 抽象接口")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" stmt "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tClose "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("NumInput")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Exec")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("stmt "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("args "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Result"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Query")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("args "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Rows"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 接口相关的一些数据")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Result "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tCount "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("r Result"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" r"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Count"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Rows "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("我们可以看到,要想对这个 MaleCount 函数进行处理,那么一个实现了 stmt 接口的动态类型必不可少,但是我们并不需要所有的方法,仅仅需要 Exec 方法。")]),t._v(" "),n("p",[t._v("所以我们第一步就是设置一个 fake 类型,并且将接口内嵌来完成 “继承”。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" fakeStmtForMaleCount "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tstmt\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里实际上只是简写,")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//真正的测试要对smt和arg进行测试的")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f fakeStmtForMaleCount"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Exec")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("stmt "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("args "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Result"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" Result"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("当我们内嵌完成继承之后,我们相当于拥有了这些抽象方法,然后我们在这个接口体上自行实现 Exec,这样就可以将结构体的 Exec 优先级提前。")]),t._v(" "),n("p",[t._v("那么让我们开始使用虚假数据 "),n("code",[t._v("Result{1}")]),t._v(" 开始测试 MaleCount 函数")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("TestEmployeeMaleCount")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("testing"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("T"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tfs "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" fakeStmtForMaleCount"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tv"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("MaleCount")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("fs"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"error is :"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"we want %d, actual is %d"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h2",{attrs:{id:"匿名接口"}},[t._v("匿名接口")]),t._v(" "),n("p",[t._v("与普通命名接口不同,匿名接口没有类型名,只通过方法集来定义接口")]),t._v(" "),n("p",[t._v("匿名接口的作用主要有:")]),t._v(" "),n("ul",[n("li",[t._v("临时使用,不需要命名")]),t._v(" "),n("li",[t._v("作为参数或返回值,减少接口命名")])]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("doSomething")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ...")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("returnInterface")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Foo")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ...")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" x\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h2",{attrs:{id:"类型断言"}},[t._v("类型断言")]),t._v(" "),n("p",[t._v("当使用空接口作为类型参数的时候,空接口已经充盈了一个动态类型,如果我们要将这个空接口类型转化为原来的类型就需要断言。")]),t._v(" "),n("h3",{attrs:{id:"常规的断言方式"}},[t._v("常规的断言方式:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a any\n\ta "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 第二个参数 ok 变量可以省略")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// v := a.(int)")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// fmt.Println(v)")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ok "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" ok "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h3",{attrs:{id:"存在于-switch-中的断言方式"}},[t._v("存在于 switch 中的断言方式:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" f any\n\tf "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"12"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// (type)里面的type为固定用法,不能更改。")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("switch")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" f"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("default")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"NO"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h3",{attrs:{id:"断言类型也可以是接口"}},[t._v("断言类型也可以是接口")]),t._v(" "),n("p",[t._v("断言中的类型不止是实际类型比如 int 比如一个具体的 struct,还能是接口类型,比如一个接口类型:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" MyAget "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Age")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" \n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("IsMyAget")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("ok"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v("e"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("MyAget"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("ok"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("这个函数的意义是这样的,接受一个 error 接口类型的对象,然后断言看它是否是 MyAget 接口类型。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Age "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tvalue "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\te "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Age"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Age")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Age"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("value\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("IsMyAget")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Age"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"10"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h2",{attrs:{id:"接口类型的底层"}},[t._v("接口类型的底层")]),t._v(" "),n("p",[t._v("这是接口的底层数据:")]),t._v(" "),n("p",[t._v("一般接口:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// interface")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" iface "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ttab "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("itab\n\tdata unsafe"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Pointer\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("空接口:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// empty interface")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" eface "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t_type "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("_type\n\tdata unsafe"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Pointer\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("data 表示的意思一样,值是动态类型的地址。我们比较 value 时,比较的是地址指向的数据是否相同而不是地址本身。")]),t._v(" "),n("p",[t._v("空接口并没有定义接口的方法,因此 "),n("code",[t._v("_type")]),t._v(" 定义的均为动态类型的元数据")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" _type "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tsize "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uintptr")]),t._v("\n\tptrdata "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uintptr")]),t._v("\n\thash "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint32")]),t._v("\n\ttflag tflag\n\talign "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint8")]),t._v("\n\tfieldalign "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint8")]),t._v("\n\tkind "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint8")]),t._v("\n\talg "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("typeAlg\n\tgcdata "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),t._v("\n\tstr nameOff\n\tptrToThis typeOff\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("一般接口因为本身定义了方法,因此它需要定义自己的方法,以及动态类型的数据,因此它除了 "),n("code",[t._v("_type")]),t._v(" 外,还定义了 "),n("code",[t._v("interfacetype")]),t._v(" 用来存储自己定义的方法元数据。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" itab "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 非空接口本身的信息")]),t._v("\n\tinter "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("interfacetype\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 动态类型数据")]),t._v("\n\t_type "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("_type\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// _type.hash的copy,用于 switch 判断类型")]),t._v("\n\thash "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint32")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 动态类型已实现接口方法的调用地址数组")]),t._v("\n\tfun "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uintptr")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("注意这里的 fun 数组,这里定义的 "),n("code",[t._v("[1]uintptr")]),t._v(" 在实际使用时,可能不是 [1],这里的数据时可变的,如果是 2,就表示实现了两个方法。")]),t._v(" "),n("p",[t._v("原文的注释是这样的 "),n("code",[t._v("// variable sized. fun[0]==0 means _type does not implement inter.")])]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" interfacetype "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 接口本身的类型信息")]),t._v("\n\ttyp _type\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 接口所在的包路径")]),t._v("\n\tpkgpath name\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 接口方法集合")]),t._v("\n\tmhdr "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("imethod\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("h2",{attrs:{id:"如何判断接口类型的相等"}},[t._v("如何判断接口类型的相等")]),t._v(" "),n("p",[n("em",[n("strong",[t._v("当接口类型未被赋予动态类型时")])]),t._v(",它的两个字段,即:动态类型字段和动态类型 value 字段均为 nil,那么这个未初始化的接口变量就恒等于 "),n("code",[t._v("nil")])]),t._v(" "),n("p",[t._v("当接口类型被赋予了动态类型,那么如果判断这时候的接口类型,必须为类型相同以及值相同,接下来我们看一个案例:")]),t._v(" "),n("p",[n("em",[n("strong",[t._v("两个非空非 nil 接口变量比较:")])])]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("printNonEmptyInterface1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" T "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t T"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"bad error"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("printNonEmptyInterface1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" err1 "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 非空接口类型")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" err1ptr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 非空接口类型")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" err2 "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 非空接口类型")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" err2ptr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 非空接口类型")]),t._v("\n\n err1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" T"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"eden"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n err1ptr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("T"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"eden"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n err2 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" T"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"eden"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n err2ptr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("T"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"eden"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"err1:"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"err2:"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err2"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"err1 = err2:"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" err2"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// true")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"err1ptr:"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err1ptr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"err2ptr:"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err2ptr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"err1ptr = err2ptr:"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err1ptr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" err2ptr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// false")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[t._v("err1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x104c959a8")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x1400004c748")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nerr2"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x104c959a8")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x1400004c728")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nerr1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" err2"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),t._v("\nerr1ptr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x104c95988")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x1400004c738")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nerr2ptr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x104c95988")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x1400004c758")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nerr1ptr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" err2ptr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),t._v("\n")])])]),n("blockquote",[n("p",[t._v("println,预定义函数,在编译期间,会由编译器根据要输出的参数的类型,将 println 替换为特定的函数,这些预定义函数定义在 "),n("a",{attrs:{href:"https://github.com/golang/go/blob/96d16803c2aae5407e99c2a1db79bb51d9e1c8da/src/runtime/print.go#L255",target:"_blank",rel:"noopener noreferrer"}},[t._v("runtime/print.go"),n("OutboundLink")],1),t._v(" 中,针对 eface 和 iface 的打印函数是:\n"),n("code",[t._v("go \tfunc printeface(e eface){ \t\tprint(e._type, e.data) \t}")]),t._v(" "),n("code",[t._v("go \tfunc printiface(i iface){ \t\tprint(i.tab, i.data) \t}")])])]),t._v(" "),n("p",[t._v("如代码所示,我们要判断的是,非空接口,并且已经实现了动态类型的两组接口类型,答案已经写在代码里了,即:"),n("code",[t._v("err1 == err2")]),t._v(" "),n("code",[t._v("err1ptr != err2ptr")])]),t._v(" "),n("p",[t._v("现在就让我们从源码出发来探究一下原因。")]),t._v(" "),n("p",[t._v("首先,我们知道"),n("strong",[t._v("类型相同以及值相同才是真的相等")]),t._v(",err1 和 err2 的动态类型均为 "),n("code",[t._v("T")]),t._v(",值也均为 "),n("code",[t._v('T{"eden"}')]),t._v(",所以他们相等;err1ptr,和 err2ptr 的类型均为 "),n("code",[t._v("*T")]),t._v(",值均为 "),n("code",[t._v('&T{"eden"}')]),t._v(",但是系统却判断他们不相等,从源码来看,二者的 "),n("code",[t._v("tab *itab")]),t._v(",因为动态类型的元数据相同,这个字段一致,所以类型一致,从第二个字段 data 来说,"),n("em",[n("strong",[t._v("data 存储了 "),n("code",[t._v('&T{"edent"}')]),t._v(" 的地址,这个地址指向的内容仍为地址")])]),t._v(",从内容上来说地址指向的地址值并不相同,所以这就可以解释为什么结果是 false 了。")]),t._v(" "),n("p",[n("em",[n("strong",[t._v("接下来我们看一下两个空非 nil 接口类型的比较:")])])]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a any\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" b any\n\ta "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("S"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"1"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tb "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("S"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"1"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//(0x102f8aaa0,0x1400004c758)")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//(0x102f8aaa0,0x1400004c748)")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// false")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" S "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tname "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("根据源码所知,a 和 b 的 "),n("code",[t._v("_type")]),t._v(" 是完全相同的,然而地址指向的地址不相同,所以结果是 false,下面让我们稍微改动一下:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a any\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" b any\n\ta "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("S"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tb "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("S"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// (0x104422aa0,0x1400004c767)")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// (0x104422aa0,0x1400004c767)")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// true")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" S "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("这个时候你惊奇的发现,结果竟然是 true,这是为什么呢?不是说,地址指向的地址应该是不同的吗?nonono,并不是所有的情况都是那样,如果结果是空接口,那么空接口的所有变量指向的都是同一个地址,所以从结果上来说,data 其实地址是相同的,指向的是同一个数据,所以答案是 true。")]),t._v(" "),n("blockquote",[n("p",[t._v("在 Go 中,空数据结构 (比如 struct {}) 不占用任何内存空间,因此在创建空数据结构时,它们实际上是指向同一个地址的。这是因为在 Go 中,每个变量都需要分配内存空间,以便可以存储它们的值。但是,由于空结构体没有任何字段,因此它们不需要分配任何内存空间。因此,在创建空结构体时,它们实际上是指向同一个已经分配的零大小内存块的指针。")])]),t._v(" "),n("p",[n("em",[n("strong",[t._v("一个非空接口类型和一个空接口类型一定不相等吗?")])])]),t._v(" "),n("p",[t._v("如果你根据源码来看,第一个字段本身就不一样,肯定不相等了,但是 go 在比较相等时,比较的是 "),n("code",[t._v("_type")]),t._v(" 字段,并不是全部的 tab 数据,所以当两者字段中的 "),n("code",[t._v("_type")]),t._v(" 相同就表示类型相同:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a any "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" S"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("6")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" b B "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" S"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("6")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//(0x1040dfae0,0x1400004c760) ")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//(0x1040e5a68,0x1400004c758)")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//true")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" S "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" B "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("S"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("所以从结果来看,_type 字段相同均为 S,data 也是一致的,所以答案是 true")]),t._v(" "),n("p",[n("em",[n("strong",[t._v("nil 接口类型:")])])]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" e "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a any\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//(0x0,0x0)")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//(0x0,0x0)")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" e"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("当一个接口是未给定动态类型的接口类型,它就是 nil 接口,那么它的类型和 data 值均为空,所以只要是 nil 接口,他们均相等,并且等于 nil。")]),t._v(" "),n("p",[t._v("最后说明一下,"),n("em",[n("strong",[t._v("当接口类型获取动态类型的时候,绝大多数情况下,会将动态类型的值复制,并且放置在一个新的内存空间里,所以原始数据跟接口类型的数据再无瓜葛,指针类型除外")])]),t._v(",不过为了节省空间,有一种情况,go 编译器就会放弃这个动作,并不会每次都重新分配。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" x any "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("34")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" y any "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" x\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" z any "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" x\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// (0x1023343e0,0x10232c1b0)")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// (0x1023343e0,0x10232c1b0)")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// (0x1023343e0,0x10232c1b0)")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("y"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("z"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("可以看到,go 判断,x y z 三个空接口类型的动态类型,类型均相同都是 int,并且 data 指向同一块内存地址。")]),t._v(" "),n("p",[t._v("非空接口也是一样:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a1 a "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a2 a "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" a1\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// \t(0x102791a48,0x1400004c758)")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// (0x102791a48,0x1400004c758)")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a2"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" a "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" b "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tname "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("我们如果想获取关于接口的内部实现细节,可以看一下这个"),n("a",{attrs:{href:"https://github.com/bigwhite/GoProgrammingFromBeginnerToMaster/blob/main/chapter5/sources/dumpinterface.go",target:"_blank",rel:"noopener noreferrer"}},[t._v("项目"),n("OutboundLink")],1),t._v(",可以输出内部的信息")]),t._v(" "),n("h2",{attrs:{id:"小接口的意义"}},[t._v("小接口的意义")]),t._v(" "),n("p",[n("em",[n("strong",[t._v("接口越小,抽象程度越高,使用范围也就越大")])])]),t._v(" "),n("p",[t._v("一群飞禽走兽,我们可以给他们的行为抽象为 “飞行”,一群能游泳的动物我们可以给他们的行为抽象为 “游泳”。那么飞行和游泳涵盖的内容就会非常的多,使用范围就会很大,比如我们现在有一个函数,要求对所有能飞行的动物做出打印动作,那么众多飞行的动物就都可以使用这个函数。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\te1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" e"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"大鹅"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\ty1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" y"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"老鹰"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// {大鹅} 他们的具体行为模式是: 大鹅慢慢的飞")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("PrintFlyer")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// {老鹰} 他们的具体行为模式是: 老鹰迅速飞行")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("PrintFlyer")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("y1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Flyer "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Fly")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("flyMod "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("PrintFlyer")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f Flyer"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"他们的具体行为模式是:"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" f"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Fly")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 大鹅")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" e "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tname "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Fly")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"大鹅慢慢的飞"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 老鹰")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" y "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tname "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("y"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Fly")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"老鹰迅速飞行"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[n("em",[n("strong",[t._v("易于实现和测试")])])]),t._v(" "),n("p",[t._v("当接口的方法较少时,动态类型实现的方法就少了,必然容易实现以及容易测试。")]),t._v(" "),n("p",[n("em",[n("strong",[t._v("高內聚,易于复合组合")])])]),t._v(" "),n("p",[t._v("我们抽象程度很高的接口,接口做的事情就很单一,比如飞行类的接口方法就是飞行,游泳动物的接口方法就只有游泳,当有会游泳也会飞行的动物时,我们只需要成立一个新的接口,将飞机类和游泳类的接口嵌入到新接口中就形成了一个全新的会飞行也会游泳的接口了。")]),t._v(" "),n("p",[t._v("如果一个接口涵盖了各种方法,那么当组合接口的时候,势必某些方法是被弃用的,所以综上所述,设置单一的,高内聚的方法是好的设计方案。")]),t._v(" "),n("h3",{attrs:{id:"如何设计小接口"}},[t._v("如何设计小接口")]),t._v(" "),n("ol",[n("li",[n("p",[t._v("先初步抽象出接口,这个时候可以有耦合,也可以不够高抽象,但是你得先定义出一个初步的接口出来,与此同时我们也得清楚,越是业务代码,抽象出一个高内聚的接口越难。")])]),t._v(" "),n("li",[n("p",[t._v("将大接口拆分为小接口,使用一段时间以后,我们会发现某些操作是可以单出被提取出来的,比如 io 包的 writer 和 reader,那么我们就可以把这个动作单独抽象出来。抽象的最高程度就是只有一个方法,这就非常的內聚了,可以说,这种程度的抽象在日常业务中还是相对比较难的,需要在长时间的使用中,慢慢摸索。")])])]),t._v(" "),n("p",[t._v("综上所述:现搞出一个能用的大接口再说,以后慢慢解耦,形成抽象程度更高的小接口。")]),t._v(" "),n("h2",{attrs:{id:"不可滥用空接口"}},[t._v("不可滥用空接口")]),t._v(" "),n("p",[t._v("空接口和非空接口最大的差异性其实不止是底层数据的不同,我们知道他们一个是 eface 一个是 iface,最重要的差距是,非空接口在编译期是会对接口变量要赋值的动态类型做编译检查的,也就是说会对实参进行检查,来确定他真的实现了这个接口定义的方法,这就是一次安全的保护屏障")]),t._v(" "),n("p",[t._v("与此同时,空接口并没有提供任何的保护屏障,他没有给编译器提供要检查的参数,因此我们说不要滥用空接口,因为它让你的代码缺少编译期间的安全检查屏障。")]),t._v(" "),n("p",[t._v("因为空接口的不安全性,我们可以得出一下结论")]),t._v(" "),n("ul",[n("li",[t._v("尽量不使用空接口")]),t._v(" "),n("li",[t._v("仅仅在未知类型的时候使用空接口")]),t._v(" "),n("li",[t._v("如果存在已知类型,并且类型较多的情况下,可以使用泛型编程")]),t._v(" "),n("li",[t._v("尽可能的抽象出带有方法的接口,并使用非空接口去作为函数参数")])]),t._v(" "),n("h2",{attrs:{id:"接口作为程序水平组合的连接点-提供程序的可扩展性"}},[t._v("接口作为程序水平组合的连接点,提供程序的可扩展性")]),t._v(" "),n("p",[t._v("在 go 语言中,一切皆组合,不过组合分为两种:")]),t._v(" "),n("ol",[n("li",[t._v("垂直组合,也就是类似接口的嵌入,结构体的嵌入,这种类型的组合被称之为垂直组合")]),t._v(" "),n("li",[t._v("水平组合,接口是水平组合的关键,当函数使用接口作为参数之后,实际参数可以无限制的水平扩展,只要我们传入的变量实现了这个接口")])]),t._v(" "),n("p",[t._v("垂直组合的三种方法:")]),t._v(" "),n("ul",[n("li",[t._v("往接口中嵌入接口实现新接口")]),t._v(" "),n("li",[t._v("往结构体中嵌入接口,实现接口体实现接口这个操作")]),t._v(" "),n("li",[t._v("往接口体中嵌入结构体,实现新的结构体这个操作")])]),t._v(" "),n("p",[t._v("水平组合的几种形式:")]),t._v(" "),n("p",[n("em",[n("strong",[t._v("基本形式:")])])]),t._v(" "),n("p",[t._v("函数或者方法的参数是接口类型,接口类型作为连接点,将多个包的数据连接在一起。这种方法满足了 “依赖抽象”,“里氏替换原则”,“接口隔离” 等代码设计原则。")]),t._v(" "),n("blockquote",[n("p",[t._v("依赖抽象原则 (Dependency Inversion Principle,DIP):高层模块不应该依赖于低层模块,两者都应该依赖于抽象。抽象不应该依赖于具体实现,具体实现应该依赖于抽象。("),n("strong",[t._v("接口本身就是一种抽象,高层模块和底层模块都依赖接口这个抽象组件组合在一起")]),t._v(")\n里氏替换原则 (Liskov Substitution Principle,LSP):子类对象应该能够替换掉程序中的任何父类对象。也就是说,在任何需要父类对象的地方,都可以使用子类对象来替换,而不会影响程序的正确性。("),n("strong",[t._v("接口动态类型将替换接口变量")]),t._v(")\n接口隔离原则 (Interface Segregation Principle,ISP):客户端不应该依赖于它不需要的接口。也就是说,一个类对另外一个类的依赖应该建立在最小的接口上。("),n("strong",[t._v("go 推崇最小接口模式")]),t._v(")")])]),t._v(" "),n("p",[t._v("这些原则都是为了提高代码的可维护性、可扩展性和可重用性。它们可以帮助我们设计出更加灵活、健壮和易于维护的代码。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Read")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("r io"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Reader"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("cap")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int64")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Copy")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("r io"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Writer"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("src io"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Reader"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),n("p",[n("em",[n("strong",[t._v("包裹函数:")])])]),t._v(" "),n("p",[t._v("函数或者方法,接受一个接口类型的参数,返回值也是这个接口类型。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("LimitReader")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("r Reader"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("n "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("Reader"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("A"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tname "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("A"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Reade")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("p "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("n "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("我们可以定义类似的内容,来实现链式调用。例如")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token function"}},[t._v("CapReader")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("LimitReader")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("AReader")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("r"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),n("p",[n("em",[n("strong",[t._v("适配器函数类型:")])])]),t._v(" "),n("p",[t._v("将一个普通函数,转化为一个满足接口类型的动态类型:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[t._v("http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("ListenAndServe")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('":80"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("HandlerFunc")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("greeting"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("greeting")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("w http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ResponseWriter"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("r "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Request"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Handler "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("ServeHTTP")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ResponseWriter"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Request"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" HandlerFunc "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ResponseWriter"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Request"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f HandlerFunc"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("ServeHTTP")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("w ResponseWriter"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("r "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Request"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("w"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("r"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("p",[t._v("这就是适配器函数类型。http.HandlerFunc 就是这个适配器函数。")]),t._v(" "),n("p",[n("em",[n("strong",[t._v("中间件:")])])]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\thttp"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("ListenAndServe")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('":80"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("aHandler")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("bHandler")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("HandlerFunc")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("greeting"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("具体的实现过程可以参考"),n("RouterLink",{attrs:{to:"/基础/函数方法/3.html"}},[t._v("这里")])],1),t._v(" "),n("p",[t._v("通常来说,web 编程中的中间件就是这么用的,使用 pipline 的方法,使用修饰器模式 (包裹函数) 然后链式调用,使用 http.HandlerFunc 进行适配器 (也就是类型的转换) 进而实现中间件的功能。")]),t._v(" "),n("h2",{attrs:{id:"接口提供程序的可测试性"}},[t._v("接口提供程序的可测试性")]),t._v(" "),n("p",[t._v("主要思想就是从引入一个结构体变成引入一个接口,这样就可以完美的解耦上下的数据。")]),t._v(" "),n("p",[t._v("下面有一个场景,使用接口来降低耦合达到可以测试函数的目的。")]),t._v(" "),n("p",[t._v("首先我们先看一下原版:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"example.com/s/cache"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("AddA")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("name "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" year "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\n\tcache"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" year"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("我们发现,整个数据耦合在一起了,如果想替换掉 cache 包,那就是不可能的,下面我们使用接口将 AddA 改造一下。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Ader "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("name "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" year "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("AddA")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a Ader"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("name "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("year "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ta"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" year"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("下面我们实现一下这个接口")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" ExampleAd "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tname "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\tyear "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("d "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("ExampleAd"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("name "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" year "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\td"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" name\n\td"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("year "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" year\n\tcache"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("d"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" d"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("year"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("最后的运行")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[t._v("a "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("new")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ExampleAd"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("AddA")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"liming"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("13")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\n")])])]),n("h2",{attrs:{id:"接口的严格和函数的宽松对比"}},[t._v("接口的严格和函数的宽松对比")]),t._v(" "),n("p",[t._v("接口的实现是严格的:在实现接口的时候函数需要显示转换 (适配器模式)")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("ListenAndServe")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('":8080"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("HandlerFunc")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("hi"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("hi")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("w http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ResponseWriter"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" r "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Request"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Fprintf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("w"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hi"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("p",[t._v("这是正确的用法,不能因为 hi 跟 http.HandlerFunc 底层一样,就认为它俩相等,因为 http.HandlerFunc 实现了接口,并不代表 hi 实现了接口。")]),t._v(" "),n("p",[t._v("实际上是不等于的关系,需要显式的转换一下。")]),t._v(" "),n("p",[t._v("接下来让我们看一下刚才代码原理:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" b "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" a "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a1 a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("y "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("a1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("y"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" t "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("y "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" x"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v("y\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" bb b "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("a")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n bb"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("函数的使用是宽松的。当直接使用函数,以及 return 函数的的时候,(其它引用类型也一样:slice,map,func,interface,chan) 是不需要显式转换的,只有非引用类型比如 int,bool string struct ... 需要。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 不需要显示的转换")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 或者也可以说:系统自动把这个匿名函数推导为了b类型。")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 函数类型 return ")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" b "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" b "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 函数类型 直接使用")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" \n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" N "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("n N"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 当然你如果显式的转换一下也是没有问题的")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 或者说,你人为的帮它推导出了类型是b类型")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get3")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" b "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("b")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("不过除了函数等引用类型之外的非引用类型还是必须显示的转换的")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 整数类型")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" a "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a1 a\n\ta1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// struct 也需要显示的转换")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" b "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n i "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" b1 b\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get7")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("b1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" b1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 或者是")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get8")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" b1 "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("b1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("h2",{attrs:{id:"issues"}},[t._v("issues")]),t._v(" "),n("h3",{attrs:{id:"interface-如何判断-nil"}},[n("em",[n("strong",[t._v("interface 如何判断 nil")])])]),t._v(" "),n("p",[t._v("只有类型是 nil + value 是 nil 的接口类型才是 nil,否则它不等于 nil")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a1 a\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// true")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" b1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("b\n\ta1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" b1\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// false")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" a "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" b "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h3",{attrs:{id:"论述-nil-error-nil-的原因"}},[n("em",[n("strong",[t._v("论述 “nil error != nil” 的原因")])])]),t._v(" "),n("p",[t._v("nil error 通常可以用这种方法来输出:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//(0x102205988,0x0)")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//false,原因主要是动态类型赋予了这个指针类型具体的类型了,只是没有类型的值而已")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" b "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("p",[t._v("可以发现,nil error 的类型并不是 0x0,而 nil 接口变量是 0X0,0x0,所以这两者并不相同。")]),t._v(" "),n("h3",{attrs:{id:"eface-和-iface-的区别"}},[n("em",[n("strong",[t._v("eface 和 iface 的区别")])])]),t._v(" "),n("p",[t._v("eface 和 iface 的第二个字段相同均存储的是动态类型的地址,然而 eface 的第一个字段保存的是动态类型的元数据,即:_type 字段,而 iface 的第一个字段不仅仅保存了动态类型的元数据 _type,还保存了自己的方法集合的相关数据,以及动态类型实现的方法地址等数据。")]),t._v(" "),n("h3",{attrs:{id:"如何查找-interface-中的方法"}},[n("em",[n("strong",[t._v("如何查找 interface 中的方法")])])]),t._v(" "),n("p",[t._v("除了查找文档,以及查看源码,还可以通过反射来查找 interface 中的方法。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" MyInterface "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Method1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Method2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//这里就是将nil转化为*MyInterface类型,elem()是一个非常重要的方法, Elem返回接口 v 包含的值或指针 v 指向的值")]),t._v("\nt "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" reflect"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("TypeOf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("MyInterface"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Elem")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("NumMethod")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n m "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Method")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("使用反射,判断某个类型是否实现了某个接口:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"io"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"os"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"reflect"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// As interface types are only used for static typing, a")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// common idiom to find the reflection Type for an interface")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// type Foo is to use a *Foo value.")]),t._v("\n\twriterType "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" reflect"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("TypeOf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("io"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Writer"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Elem")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\tfileType "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" reflect"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("TypeOf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("os"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("File"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("fileType"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Implements")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("writerType"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h3",{attrs:{id:"interface-设计的优缺点"}},[n("em",[n("strong",[t._v("interface 设计的优缺点")])])]),t._v(" "),n("p",[t._v("优点:")]),t._v(" "),n("ul",[n("li",[t._v("可扩展性:程序拥有横向扩展的能力")]),t._v(" "),n("li",[t._v("松耦合:接口可以实现代码的松耦合,因为接口只是定义了一组方法,而不是实现,因此它们可以被许多不同的类型实现,这使得代码更加灵活。")]),t._v(" "),n("li",[t._v("可测试性:使用接口编写的代码更容易进行单元测试,因为可以提供模拟对象来模拟接口所定义的方法。这有助于在代码库中提供更高的测试覆盖率。")]),t._v(" "),n("li",[t._v("可读性:使用接口编写的代码更容易阅读和理解,因为可以通过接口名称和方法签名来查看类型所提供的功能。")])]),t._v(" "),n("p",[t._v("缺点:")]),t._v(" "),n("ul",[n("li",[t._v("过度设计:如果使用不当,接口可能会导致过度设计,这可能会令代码库变得复杂、难以理解和维护。因此,在设计接口时应该遵循简单性原则,"),n("strong",[t._v("仅定义必要的方法")]),t._v("。")]),t._v(" "),n("li",[t._v("性能损失:使用接口可能会导致一些性能损失,因为在运行时需要进行类型断言和方法查找。虽然这种影响通常很小,但在高性能场景下可能会有所不同。")]),t._v(" "),n("li",[t._v("难以理解:对于新手来说,理解接口的概念和使用可能会比较困难,这可能会导致一些代码可读性差的问题。")])]),t._v(" "),n("h3",{attrs:{id:"空接口类型和一般类型是从属关系吗"}},[n("em",[n("strong",[t._v("空接口类型和一般类型是从属关系吗?")])])]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" \n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ❌")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),n("p",[t._v("上面的代码就表示,"),n("em",[n("strong",[t._v("空接口类型和一般的类型是平级关系")])]),t._v(",不能说 any 被所有类型实现了,就说 any 是所有类型的父类,这是错误的。所以当我们的 age 中 value 是 any 类型,那么我们传入的数据的类型也得是 any 类型,不然不就是类型错误了吗")]),t._v(" "),n("p",[t._v("那么我们学的,所有类型都实现了空接口这句话如何使用呢,很简单,让任何类型转化为 any 类型即可,任何类型转化为 any 类型之后,就会变成 any 类型,并不是之前它的类型。")]),t._v(" "),n("p",[t._v("所以这里的代码应该这么写:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" b "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a any "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" b "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 所以这里的 true 从 布尔类型 直接变成了any类型 ")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ✅")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),n("p",[t._v("即便你直接传入字面量也可以:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里的 字面量 1 其实就是 any 类型 并不是所谓的int或者uint")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value any"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),n("ul",[n("li",[t._v("https://book.douban.com/subject/35720728/ 246 页 - 286 页")]),t._v(" "),n("li",[t._v("https://mp.weixin.qq.com/s/6_ygmyd64LP7rlkrOh-kRQ")]),t._v(" "),n("li",[t._v("https://github.com/golang/go/blob/master/src/runtime/runtime2.go")]),t._v(" "),n("li",[t._v("https://github.com/golang/go/blob/master/src/runtime/type.go")])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/25.c1fe94a5.js b/assets/js/25.c1fe94a5.js new file mode 100644 index 000000000..24636b137 --- /dev/null +++ b/assets/js/25.c1fe94a5.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[25],{453:function(t,s,a){"use strict";a.r(s);var n=a(36),p=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"切片"}},[t._v("切片")]),t._v(" "),a("p",[t._v("导读:")]),t._v(" "),a("ul",[a("li",[t._v("切片的基本操作")]),t._v(" "),a("li",[t._v("切片和数组的基本概念")]),t._v(" "),a("li",[t._v("切片数组的底层数据结构")]),t._v(" "),a("li",[t._v("issues")])]),t._v(" "),a("h2",{attrs:{id:"基本操作"}},[t._v("基本操作")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 初始化一个切片,设置长度为5,容量为10")]),t._v("\na "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 获取一个新的切片,遵循左闭右开的原则,取值时并且只看长度不能看容量")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// b切片,长度为1,容量为8(下文有讲,向右原则)")]),t._v("\nb"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 判断切片长度为0")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// append 数据")]),t._v("\na "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v('// 将切片内容清空(element设置为初始值,比如int就是0,string就是"")')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("clear")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 将一个切片转为一个数组,注意,新数组不能超过切片的长度,且是数据的深度拷贝")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" arr "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n")])])]),a("h2",{attrs:{id:"切片和数组的基本概念"}},[t._v("切片和数组的基本概念")]),t._v(" "),a("p",[t._v("数组是拥有一段连续内存的数据结构,切片是存储了这个数据结构地址,长度以及容量的 struct,这里俗称这种数据结构 (类似切片) 叫引用类型")]),t._v(" "),a("p",[t._v("生成数组有两种方式 "),a("code",[t._v("[3]int")]),t._v(" 和 "),a("code",[t._v("[...]int")]),t._v(" 但是后者其实只是一种语法题,go 会自动推断出容量,因为推断是在编译期搞定的,所以并不会影响数组运行时的效率。")]),t._v(" "),a("p",[t._v("不同长度的相同数据类型数组不是一个类型,比如 "),a("code",[t._v("[1]string{}")]),t._v(" 和 "),a("code",[t._v("[2]string{}")]),t._v(" 就是两个类型")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Extra "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Array"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("Elem"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" elem"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Bound"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" bound"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这段代码可以解释一下,它来自 go 的源代码,可以看出,生成数组的是一个 struct,那么显而易见了,里面的各项参数都必须一致的情况下 struct 才是一致的,所以,必须类型和容量都满足才行")]),t._v(" "),a("p",[t._v("但是切片没有这个烦恼,只要数据类型一致就是一种类型,因为它在编译期间的结构体中只有类型,并没有数量,数量需要在运行时才能确定")]),t._v(" "),a("p",[t._v("“切片的切片” 的容量是和 “切片的” 容量是不一致的 (比如这里的 a 和 b),我们来看一个例子:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ta "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("6")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tb "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("cap")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("cap")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("output:"),a("code",[t._v("6, 6, 3, 4")])]),t._v(" "),a("p",[t._v("我猜你肯定以为 b 的容量也是 6,但是不是,go 规定,"),a("em",[a("strong",[t._v("切片只能向右看")])]),t._v(",不能向左看,我们来说一下上面这个例子,a 就不用说了,a 的底层数据结构数组就是 6 个长度,所以 a 自然长度和容量都是 6,但是 b,它是切片的切片,遵从左闭右开的规则,它的长度是从 index 的 2 到 4,也就是说是 "),a("code",[t._v("[3 4 5]")]),t._v(",自然它的长度就是 3,这毫无疑问,又因为切片遵从 “只能向右看容量” 的规则,它的容量从 index 2 开始往后算,也就是 "),a("code",[t._v("6 - 2 = 4")]),t._v(" 所以它的长度是 3 容量是 4")]),t._v(" "),a("h2",{attrs:{id:"切片-数组的底层数据结构"}},[t._v("切片,数组的底层数据结构")]),t._v(" "),a("p",[t._v("严格意义来说,go 的切片不存在扩容,如果切片想要的数据量大于底层数组的容量时,那么系统会做两件事,开辟新的数组,给这个数组生成新的切片,之前的数组和切片并没有任何的改变,而且如果没有被引用了,还会被 gc 掉")]),t._v(" "),a("p",[t._v("数组在数量小于等于 4 的时候,直接分配在栈内存里,如果大于四且没有逃逸到堆上时,变量就会在静态存储区初始化然后拷贝到栈上")]),t._v(" "),a("blockquote",[a("p",[t._v("静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量。")])]),t._v(" "),a("blockquote",[a("p",[t._v("栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。任何临时变量都是处于栈区的,包括在 main () 函数中定义的变量")])]),t._v(" "),a("blockquote",[a("p",[t._v("堆区:亦称动态内存分配。程序在运行的时候用 malloc 或 new 申请任意大小的内存,程序员自己负责在适当的时候用 free 或 delete 释放内存。动态内存的生存期可以由我们决定,如果我们不释放内存,程序将在最后才释放掉动态内存。但是,良好的编程习惯是:如果某动态内存不再使用,需要将其释放掉,否则,我们认为发生了内存泄漏现象。")])]),t._v(" "),a("p",[a("strong",[t._v("切片的底层数据结构。")])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" SliceHeader "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tData "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uintptr")]),t._v("\n\tLen "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\tCap "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("实际上,切片就是一个 struct (struct 是 go 的基本组成单位,实际上很多东西都可以使用结构体来表达,比如接口底层也是结构体)")]),t._v(" "),a("p",[t._v("当我们使用字面量的方式去生成一个 slice 的时候,会在编译期间搞定,但是如果使用 make 去生成一个切片时,就会在运行时生成一个切片,在运行时生成切片,不仅仅会进行边界检查,而且还会看是否逃逸到堆上。")]),t._v(" "),a("p",[t._v("**slice 的扩容规律,**如果期望容量大于当前容量的两倍就会使用期望容量,如果当前切片的长度小于 "),a("a",{attrs:{href:"https://github.com/golang/go/blob/fd0ffedae2dd9e202efc2dd7f7937baa08600d26/src/runtime/slice.go#L178",target:"_blank",rel:"noopener noreferrer"}},[t._v("256"),a("OutboundLink")],1),t._v(" 就会将容量 x2,\n如果当前切片的长度大于 256 就会逐步 (按照这个公式 "),a("code",[t._v("cap += (cap + 768) / 4")]),t._v(") 从 2 倍的倍增速率调整到 1.25 的倍率,增加到新容量大于期望容量。不过这还不是全部")]),t._v(" "),a("p",[t._v("slice 扩容具体操作。扩容的时候并不是严格按照这个规律来的,这只是一个大致的规律,实际运行的时候会进行 "),a("code",[t._v("padding")]),t._v(" (填充),也就是内存的对齐,将容量字节数尽量靠近 2 的次方,比如说期望的新容量是 5,这时期望分配的内存大小为 40 字节 (具体看一个位置多少个字节,这里是按照 8 举例子),运行时会向上取整内存的大小到 48 字节 (取整到 go 的"),a("a",{attrs:{href:"https://github.com/golang/go/blob/b634f5d97a6e65f19057c00ed2095a1a872c7fa8/src/runtime/sizeclasses.go#L84",target:"_blank",rel:"noopener noreferrer"}},[t._v("推荐值"),a("OutboundLink")],1),t._v("),所以新切片的容量是 "),a("code",[t._v("48 / 8 = 6")])]),t._v(" "),a("p",[t._v("我们知道当切片扩容的时候,重新分配底层数组是会牵涉到内存的复制的,因此尽量减少内存复制就是我们要追求的事情了,当我们往后面追加数据的时候,如果可以提前预估要使用的容量,那么就不牵涉到多次的内存复制了")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" sliceSize "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1000")]),t._v("\n\tm "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" sliceSize"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" sliceSize"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tm "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("m"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[a("strong",[t._v("slice 的浅拷贝")]),t._v(" "),a("code",[t._v("s := make([]int, 3,12)")]),t._v(","),a("code",[t._v("s1 := s[1:2]")]),t._v(",那么 s1 和 s 是浅层拷贝,他们拥有一个共同的底层数组,更改任意一个另一个的值也会发生改变。")]),t._v(" "),a("p",[t._v("**slice 的深拷贝。**copy 内置函数会将源内存 data 直接一次性拷贝,所以慎用,很消耗资源。")]),t._v(" "),a("p",[a("strong",[t._v("slice 的边界检查消除以及优化。"),a("strong",[t._v("go 在编译数组或者切片的时候都会在编译期间进行边界越界的检查,不过也只是普通的检查,比如直接使用整数或者常量访问数组,对于")]),t._v("用变量去获取切片数组的情况")]),t._v("根本就检查不了,这个时候 go 的运行时就会起作用,发出 Panic,那么边界检查有什么缺点呢?就是会降低运行时的效率,当然这里指的是运行时的检查,因为编译器的检查首先本身能力有限,其次只会降低编译速度而已,所以 go 从 1.17 开始,就开始支持了边界检查的消除,为了就是在证明不会越界的情况下,提高代码的运行效率,下面我们看一下取消运行时边界检查的例子:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这种不能确定,所以还是会检查")]),t._v("\n\ta "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\ta"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这样,就可以消除边界检查,因为运行时确定不可能出现问题")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("--")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 或者这样也可以")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("A")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" b "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("256")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\ti "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("256")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里就是给运行时一个暗示,表示i的最大index不会超过 256")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" b "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// v的最大值也不会超过 256(byte最大值 255)")]),t._v("\n\t\t\t"),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("h2",{attrs:{id:"append-追加数据的操作"}},[t._v("append 追加数据的操作")]),t._v(" "),a("p",[t._v("前文我们知道 slice 会发生扩容情况,那么这种扩容一般是在 append 操作的时候发生的,通常来说操作是这样的:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\na "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 注意,append操作是追加的意思,所以这里不是 100,而是0001")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 0001")]),t._v("\n")])])]),a("p",[t._v("当 slice 底层数组不够 append 的时候,就会发生扩容:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\na"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\na"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v("\na"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里还是相同的数组,因为容量没有超过")]),t._v("\na "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里的a 指向的就不同于之前的那个数组了。")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//底层就变成了一个新的数组 1 2 3 4 5 0 0 0 ;fmt.Println(cap(a)) : 8")]),t._v("\na "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("同样底层数组的不同区域的切片 append 的时候经常会发生意想不到的结果:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ta "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ta1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("a1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ta1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("a1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ta "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("a1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ta1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("a1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//[0 0 0] [0]")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// [0 0 2] [0 2]")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// [0 0 2 4] [0 2]")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// [0 0 2 5] [0 2 5]")]),t._v("\n")])])]),a("p",[t._v("从上面的案例可以说明,首先,a1 a 指向同一个底层数组,其次,a1 和 a 在 append 的时候,都是在各自切片的后面添加数据,他们会互相影响,在写代码的时候容易出现 bug")]),t._v(" "),a("h3",{attrs:{id:"对-append-的优化"}},[t._v("对 append 的优化")]),t._v(" "),a("p",[t._v("对一个未知大小的切片进行 append 操作的最佳选择是初始化一个 nil 切片:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\ns "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"a"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("s 在初始化的时候没有分配内存,在 append 的时候分配了一个底层数组,下面这种方式就会浪费一次内存分配")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("s "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\ns "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"a"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("这种方式,在 s 初始化的时候会给予它一个底层长度为 0 的数组,即便长度为 0,go 并未分配实际的内存空间,但是仍然浪费了执行片段,append 的时候还要再次分配底层数组。")]),t._v(" "),a("p",[t._v("不过使用 make 的方式比较适合"),a("strong",[t._v("已知容量")]),t._v("的场景。")]),t._v(" "),a("p",[t._v("除此之外还有两种初始化的方式:")]),t._v(" "),a("ul",[a("li",[a("code",[t._v("[]int(nil)")])]),t._v(" "),a("li",[a("code",[t._v('[]string("a")')])])]),t._v(" "),a("p",[t._v("我们知道 "),a("code",[t._v("[]int{}")]),t._v(" 和 "),a("code",[t._v("var s []int")]),t._v(" 是两种皆然不同的初始化方式,虽然都是 0 长度,但是前者不是 nil,后者是 nil,不过 append 的时候不会介意是否是 nil,这也提醒了我们判断是否是空切片的方式,使用 len == 0 才是正确的方法,“是否等于 nil” 是错误用法。")]),t._v(" "),a("p",[a("code",[t._v("[]string(nil)")]),t._v(" 的用法非常少见,通常来说,使用场景就下面这么一个:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("src "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hi"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"there"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"!"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\ns "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" src"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("我们这里使用一个值为 nil 的切片主要是为了符合类型的需求。")]),t._v(" "),a("h2",{attrs:{id:"切片的-copy"}},[t._v("切片的 copy")]),t._v(" "),a("p",[t._v("copy 是 go 语言的内置函数,全局使用,"),a("code",[t._v("copy(a,b []Type)")]),t._v(",copy 是深度拷贝,它将后者的数据完全拷贝给前者。")]),t._v(" "),a("p",[t._v("要注意的是,将要被复制的元素能复制的量取决于前者的 length")]),t._v(" "),a("p",[t._v("比如下面这种情况,被复制的元素就是 0,但是并不会 panic")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("src "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" d "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// []")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("copy")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("d"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("src"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("一般来说,我们会使用相同的 length:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("src "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\nd "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("src"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("copy")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("d"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("src"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("或者直接使用 append 也能做到 copy")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("src "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\nd "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("src"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("h2",{attrs:{id:"切片未被合理-gc"}},[t._v("切片未被合理 gc")]),t._v(" "),a("p",[t._v("当切片完成自己的使命时,我们希望它可以正常的被 gc 掉,通常来说,我们可以手动使用 "),a("code",[t._v("runtime.GC()")]),t._v(" 来强制系统进行垃圾回收,下面我们看一种 bug,这种 bug 出现以后,我们手动的垃圾回收将会无效")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("s "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 此处原本的想法是只取两个数据")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 但是造成了10个数据都不能垃圾回收,8个浪费")]),t._v("\nb "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("8")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\nruntime"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("GC")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nruntime"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("KeepLive")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("本来我们期望 s 底层数组可以被垃圾回收,但是 b 也指向这个相同的底层数组,那么这个垃圾回收就无法执行。")]),t._v(" "),a("p",[t._v("正确的方法是")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[t._v("copy")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("res"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("8")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" res\n")])])]),a("p",[t._v("当然了,我们深究 gc 的本质就会发现,三色 gc 算法是看某个对象是否还被变量持有,是否可以通过变量的方式追踪到,来作为回收标准的,要么我们像前者一样,不再让变量持有值,要么将值变成 nil 即可:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tv "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\ns "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("A"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 此处原本的想法是只取两个数据")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 但是造成了10个数据都不能垃圾回收,8个浪费")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("8")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里是手动的将前八个数据丢弃掉")]),t._v("\n\ts"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\nb "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("8")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\nruntime"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("GC")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nruntime"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("KeepLive")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n")])])]),a("p",[t._v("这种多切片指向底层数组而造成的无法正常垃圾回收的行为很常见。在工作中还是应该检查好自己的代码,避免这种行为的发生。")]),t._v(" "),a("h2",{attrs:{id:"切片中的-range-注意事项"}},[t._v("切片中的 range 注意事项")]),t._v(" "),a("p",[t._v("range 时,我们直接修改返回的值是不会生效的,因为返回的值并不是原始的数据,而是数据的复制。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Student "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tyear "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tresult "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("Student"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("13")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" student "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" result "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tstudent"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("year"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 12 13")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("也就是说,这里的 "),a("code",[t._v("student :=")]),t._v(" student 的改变不会影响 result 中的任何数据,除非这里的 "),a("code",[t._v("[]Student")]),t._v(" 是 "),a("code",[t._v("[]*Student")])]),t._v(" "),a("p",[t._v("下面我们演示一下,正确的在 range 时的操作方式:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Student "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tyear "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tresult "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("Student"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("13")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" result "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tresult"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("year"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 13 14")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("正确的方式就是直接使用 result 本身进行操作,就可以真正的去改变 result 了。")]),t._v(" "),a("p",[t._v("并且,在 range 的时候,range 后面的数据其实也是复制品,也就是说,这里的 "),a("code",[t._v(":= range result")]),t._v(" result 也是复制品,原有的 result 如何变化都不会影响 range 结果。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里的 result")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" result "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 跟这里面的result不是一个值,")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//只有里面的result才是跟外面的result是一个值")]),t._v("\n\t\tresult"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("year"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("下面我们再看一个案例:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("s "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" s "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ts "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// [0 0 0 10 10 10]")]),t._v("\n")])])]),a("p",[t._v("你猜结果是多少呢?是会一直 range 吗?因为数据在一直添加啊,nonono,只会 range 3 次而已,因为 range 后面的 s 是固定不变的,它本身只是原有 s 的复制品而已。")]),t._v(" "),a("p",[t._v("综上:")]),t._v(" "),a("ul",[a("li",[t._v("range 后面的数据是原有数据的复制品")]),t._v(" "),a("li",[t._v("range 前面的 k v 更是后面复制品输出数据的复制品")]),t._v(" "),a("li",[t._v("range 里面的数据才是跟外面的数据保持一致")])]),t._v(" "),a("p",[t._v("第三点很关键,range 后面的数据跟 range 里面的数据并不是一个:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("s "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里的s")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" s "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里的s 跟外面保持一致")]),t._v("\n\ts "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[a("strong",[t._v("不能")]),t._v("把 range 当做 function 来类比:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ts "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("k"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("v any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ts"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("如果是函数,函数体的变量 s 和函数内部的 s 就是同一个,显然,range 中,range 后面的 s 和 range 里面的 s 并不是同一个。")]),t._v(" "),a("h2",{attrs:{id:"切片转化为数组"}},[t._v("切片转化为数组")]),t._v(" "),a("p",[t._v("在 go 1.20 版本中,新添加了切片转为数组或者数组指针的操作,具体实现如下:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("s"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\na "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \na1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\na2 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\na3 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[a("code",[t._v("a[0] == s[0]")]),t._v(",也就是说,转化出来的就是底层的那个数组的复制,"),a("strong",[t._v("注意并不是底层数组本身")]),t._v(",不过这里相同切片转化的数组指针是指向这个切片的底层数组的,所以 a2 和 a3,s 是公用一个数组的,如果更改了 a2[0],那么 a3,s 也是会发生改变的,a1 a 已经是数据的复制了,他们有了分别的生活了。")]),t._v(" "),a("p",[t._v("下面我们看一个具体的代码演示:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ts "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ta "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ta1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ta2 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ta3 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" a1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" a2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" a3"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ta"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" a1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" a2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" a3"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ta2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" a1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" a2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" a3"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//[0 0] [0] [0 0] &[0] &[0 0]")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//[0 0] [1] [0 0] &[0] &[0 0]")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//[12 0] [1] [0 0] &[12] &[12 0]")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 可以证明确实是指向同样的底层数据")]),t._v("\nfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("a2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("a3"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 0x1400012a020 0x1400012a020 0x1400012a020")]),t._v("\n")])])]),a("p",[t._v("当一个切片转化为一个数组的时候,数组的长度不能大于切片的长度,而不是容量")]),t._v(" "),a("p",[t._v("那么下面这种代码就会出现 bug")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("s"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\na "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\na1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// panic: cannot convert slice with length 2 to array or pointer to array with length 3")]),t._v("\n")])])]),a("p",[t._v("下面我们看一下转化后的特殊值")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("s"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\na "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// []")]),t._v("\na1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// &[]")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//将非空切片转为长度为 0 的数组,得到的指针不是 nil,比如 b2")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" j "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\nb "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("j"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// panic")]),t._v("\nb1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("j"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// [] ")]),t._v("\nb2 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("j"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ")]),t._v("\nb3 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("j"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// panic")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//将 nil 切片转为长度为 0 的数组,得到的指针为 nil")]),t._v("\nc "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nu "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("c"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//&[] ")]),t._v("\nu1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("c"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// []")]),t._v("\n")])])]),a("h2",{attrs:{id:"issues"}},[t._v("issues")]),t._v(" "),a("p",[a("code",[t._v("问题一:")]),t._v(" "),a("em",[a("strong",[t._v("如果有多个切片指向了同一个底层数组,那么你认为应该注意些什么")])])]),t._v(" "),a("p",[t._v("一定要避免 a 切片的更改造成的底层数据的改变,对 b 切片的结果造成影响,因为它们指向同一个数据底层")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\nb "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n\na"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),t._v("\n\nfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[a("code",[t._v("[1 2 4] [1 2 4]")])]),t._v(" "),a("p",[a("code",[t._v("问题二:")]),t._v(" "),a("em",[a("strong",[t._v("怎样沿用扩容的思想对切片进行 “缩容")])])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("a"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\nb "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// b = [1,2]")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 如果确定a的数据多余的没有任何的用途了")]),t._v("\nnb "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("copy")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("nb"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("所谓扩容的思想,就是创造一个新的底层数据")]),t._v(" "),a("p",[a("code",[t._v("问题三:")]),t._v(" "),a("em",[a("strong",[t._v("nil 切片和空切片 (比如 []int {}) 的区别")])])]),t._v(" "),a("p",[t._v("最大的区别就是指向的底层数组的地址不一样")]),t._v(" "),a("ul",[a("li",[t._v("nil 压根就没有地址")]),t._v(" "),a("li",[t._v("空切片是有正儿八经的地址的,"),a("strong",[t._v("只不过这个地址指向的数组不占用空间")]),t._v(",这个数组叫做 zero 数组,并且所有的空切片指向同一个数组就是这个 zero 数组,也可以说在 go 里,zero 数组是唯一的存在,它存在的目的就是为了空切片")])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("unsafe"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sizeof")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 0")]),t._v("\n")])])]),a("p",[t._v("空的数据是不占内存空间的,还有类似的,比如空的 struct 也是一样的")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("unsafe"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sizeof")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 0")]),t._v("\n")])])]),a("p",[a("code",[t._v("问题四:")]),t._v(" "),a("em",[a("strong",[t._v("slice 和 array 的不同使用场景是什么")])])]),t._v(" "),a("p",[t._v("如果数据是固定的,可以用数组,否则还是切片更加灵活,实际上绝大多数情况下还是切片更好用")]),t._v(" "),a("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),a("ul",[a("li",[t._v("https://www.jianshu.com/p/9ea2fba64f06")]),t._v(" "),a("li",[t._v("https://chai2010.cn/advanced-go-programming-book")]),t._v(" "),a("li",[t._v("https://blog.csdn.net/kevin_tech/article/details/122138489")]),t._v(" "),a("li",[t._v("https://blog.csdn.net/weixin_39927993/article/details/112099007")])])])}),[],!1,null,null,null);s.default=p.exports}}]); \ No newline at end of file diff --git a/assets/js/26.529d156a.js b/assets/js/26.529d156a.js new file mode 100644 index 000000000..eab60b91c --- /dev/null +++ b/assets/js/26.529d156a.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[26],{454:function(t,s,n){"use strict";n.r(s);var a=n(36),r=Object(a.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"字符串"}},[t._v("字符串")]),t._v(" "),n("ul",[n("li",[t._v("字符串的基础操作")]),t._v(" "),n("li",[t._v("字符串的基础知识")]),t._v(" "),n("li",[t._v("字符串的底层操作")])]),t._v(" "),n("h2",{attrs:{id:"字符串的基础操作"}},[t._v("字符串的基础操作")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 声明")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"你好"')]),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v('// s := "你好"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//读取")]),t._v("\nfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Print")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 转化为unicode码点存储,单个字符, 例如'你',也是使用的rune来存储的,、")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 不过string底层本身是[]byte的方式存储的")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("rune")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 转化为字符存储")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("byte")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 字符的简单拼接")]),t._v("\ns1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"你"')]),t._v("\ns2 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"好"')]),t._v("\ns "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" s1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" s2\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//多行字符")]),t._v("\ns "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v("`\n你好\n\n世界\n`")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v('// 多行字符也通常用在屏蔽 ""作用的地方')]),t._v("\ns"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('`{"user": "shgopher", "links": ["https://github.com/shgopher"]}`')]),t._v("\n")])])]),n("h2",{attrs:{id:"字符串的基础知识"}},[t._v("字符串的基础知识")]),t._v(" "),n("ul",[n("li",[t._v("字符串的数据是只读数据不可更改")]),t._v(" "),n("li",[t._v("字符串的零值是可用的,"),n("code",[t._v("var s string")]),t._v(" 结果是 "),n("code",[t._v('""')])]),t._v(" "),n("li",[t._v("获取字符串长度的操作时间复杂度是 "),n("code",[t._v("O(1)")]),t._v(" 因为它是不可变的只读数据,所以长度被保存在了字段中,直接读这个字段即可")]),t._v(" "),n("li",[t._v("字符串可以通过 "),n("code",[t._v("+")]),t._v(","),n("code",[t._v("+=")]),t._v(" 进行拼接")]),t._v(" "),n("li",[t._v("字符串可以使用 "),n("code",[t._v("> < >= <= == =!")]),t._v(" 运算符,比较的顺序是:\n"),n("ul",[n("li",[t._v("先比较长度")]),t._v(" "),n("li",[t._v("再比较是否是指向一块内存地址")]),t._v(" "),n("li",[t._v("如果都满足再比较具体数据")])])]),t._v(" "),n("li",[t._v("字符串原生支持 unicode 字符集,并且 go 默认支持 utf-8 的编码算法\n"),n("ul",[n("li",[t._v("rune 存储 unicode 的一个码点")]),t._v(" "),n("li",[t._v("byte 存储真实的底层字符 (比如 utf-8,三个字符来保存一个中文字符,rune 就只显示一个字符,byte 会显示三个)")])]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"中"')]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Print")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("byte")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("rune")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// output: [228 184 173] [20013]")]),t._v("\n")])])])]),t._v(" "),n("li",[t._v("使用``原生支持多行字符")])]),t._v(" "),n("h2",{attrs:{id:"字符串的高效构造"}},[t._v("字符串的高效构造")]),t._v(" "),n("p",[t._v("字符串的构造有以下这么几种")]),t._v(" "),n("ul",[n("li",[t._v("最常规的使用 "),n("code",[t._v("+")]),t._v(" 和 "),n("code",[t._v("+=")])]),t._v(" "),n("li",[t._v("fmt.Sprintf")]),t._v(" "),n("li",[t._v("strings.Join")]),t._v(" "),n("li",[t._v("strings.Builder")]),t._v(" "),n("li",[t._v("bytes.Buffer")])]),t._v(" "),n("p",[t._v("让我们分别给出代码:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//最常规的使用`+`和 `+=`")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ts1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"中"')]),t._v("\n\ts2 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"国"')]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Print")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" s2"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//fmt.Sprintf")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ts1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"中"')]),t._v("\n\ts2 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"国"')]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Print")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sprintf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"%s%s"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s2"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//strings.Join")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"strings"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ts1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"中"')]),t._v("\n\ts2 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"国"')]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Print")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("strings"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Join")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("s1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s2"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//strings.Builder")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"strings"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" b strings"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Builder\n b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Grow")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 给出猜测最终的string长度")]),t._v("\n\ts1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"中"')]),t._v("\n\ts2 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"国"')]),t._v("\n\tb"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("WriteString")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tb"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("WriteString")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s2"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Print")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("String")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//bytes.Buffer")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"bytes"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" b bytes"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Buffer\n\ts1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"中"')]),t._v("\n\ts2 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"国"')]),t._v("\n\tb"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("WriteString")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tb"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("WriteString")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s2"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Print")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("String")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("根据 benchmark,得出以下结论:")]),t._v(" "),n("ul",[n("li",[t._v("带有预估 string 长度的 strings.Builder 最快")]),t._v(" "),n("li",[t._v("带有预估的 bytes.Buffer 和 strings.Join 性能第二档次")]),t._v(" "),n("li",[t._v("没有预估长度的 strings.Builder 和 bytes.Buffer 以及 + += 第三档次")]),t._v(" "),n("li",[t._v("fmt.Sprintf 最差劲")])]),t._v(" "),n("p",[t._v("那么:")]),t._v(" "),n("ul",[n("li",[t._v("当能给出预估的情况下,优选使用 strings.Builder")]),t._v(" "),n("li",[t._v("strings.Joins 性能最稳,没有预估的情况下,使用这个稳定啊 (实际上这个 join 就是调用了 string.Builder,并且给出了预估长度)")])]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Join")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("elems "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" sep "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("switch")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("elems"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" elems"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里是搞定string长度的")]),t._v("\nn "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("sep"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("elems"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("elems"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tn "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("elems"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 使用了builder")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" b Builder\nb"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Grow")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nb"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("WriteString")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("elems"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" elems"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tb"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("WriteString")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("sep"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tb"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("WriteString")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("String")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("ul",[n("li",[t._v("操作符 + += 最直观,并且在字符短,以及编译器知道连接的字符串个数时,这种方式还能得到编译器的优化")]),t._v(" "),n("li",[t._v("fmt.Sprintf 用在多类型组成字符串的时候是最好的,虽然它效率很差,但是人家能力强啊")])]),t._v(" "),n("p",[n("strong",[t._v("综上所诉")]),t._v(":优先选 "),n("code",[t._v("strings.Join")])]),t._v(" "),n("h2",{attrs:{id:"字符串的底层"}},[t._v("字符串的底层")]),t._v(" "),n("h3",{attrs:{id:"数据结构"}},[t._v("数据结构")]),t._v(" "),n("p",[t._v("一个 string 的底层数据类似一个 slice,只不过这个 slice 是只读数据,它的底层不同于一般的 slice,是一个特别的 struct")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" stringStruct "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tstr unsafe"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Pointer\n\t"),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("len")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" \n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 注意常规的slice这里是有一个cap的")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//但是string因为是只读的关系只有length的含义")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[n("code",[t._v("runtime/string.go")]),t._v(" 中出现了这么一段代码")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// rawstring allocates storage for a new string. The returned")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// string and byte slice both refer to the same storage.")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// The storage is not zeroed. Callers should use")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// b to set the string contents and then drop b.")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("rawstring")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" b "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tp "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("mallocgc")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("uintptr")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("size"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("stringStructOf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("str "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" p\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("stringStructOf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("len")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" size\n\n\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("slice"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("unsafe"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Pointer")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" slice"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("p"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" size"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" size"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("我们仔细看注释的这句话,当一个 string 导入数据的时候,运行时会给定一个辅助的 slice,用来辅助的导入数据,然后当数据导入完毕之后,这个 slice 的描述符,也就是这个代表了这个 slice 的 struct 就会被删除掉,所以说其实 string 不能跟 slice 划上等号,也不能简简单单的说它是一个只读的 slice,实际上它压根就不是 slice,slice 在生成 string 的过程中只是起到了辅助作用")]),t._v(" "),n("h3",{attrs:{id:"类型转换"}},[t._v("类型转换")]),t._v(" "),n("p",[t._v("字符串进行的转化只能是 string 和 "),n("code",[t._v("[]rune")]),t._v(" or "),n("code",[t._v("[]byte")]),t._v(" 互相转换")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ta "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"【你好】"')]),t._v("\n\tb "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("byte")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tc "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("rune")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// []byte 是可以直接使用fmt包直接输出为string的,但是[]rune需要显式进行转换。")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Printf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"现在我们打印出原始数据%s,打印出[]byte转化后的数据%v,打印出[]rune转化后的数据%v,打印出逆转的数据%s 和 %s,"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" c"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("c"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("上文我们提到的字符串的构造,例如删除一个字符,追加一个字符,都无一例外需要改变这个 string,那么很明显任何数据的处理都是"),n("strong",[t._v("拷贝")]),t._v("的数据,原数据是不会有任何变化的,所以这就告诫我们字符串的处理要小心非常有可能浪费大量的内存。")]),t._v(" "),n("p",[t._v("我们看一下底层的转换代码:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" tmpStringBufSize "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("32")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" tmpBuf "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("tmpStringBufSize"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("stringtoslicebyte")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("buf "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("tmpBuf"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" b "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" buf "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("buf"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("buf "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" tmpBuf"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\tb "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" buf"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tb "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("rawbyteslice")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里就发生了拷贝")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("copy")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" b\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("p",[t._v("于此同时我们也能发现 string 的底层的真实存储是 "),n("code",[t._v("[]byte")]),t._v(" 不是 "),n("code",[t._v("[]rune")])]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ta "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"【你好】"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" b "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("copy")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" c "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("rune")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// invalid argument: arguments to copy c (variable of type []rune) ")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// and a (variable of type string) have different element types rune and byte")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("copy")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("c"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" c"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("p",[t._v("go 为某几种特别的情况优化了 string 和 slice 转换必须拷贝的情况,意思就是不需要拷贝就让这个 string 直接使用这个 slice 的底层,但是有个规定,只要是 slice 发生了改变,那么这个 string 立即失效")]),t._v(" "),n("p",[n("code",[t._v("b = []int{1,2}")])]),t._v(" "),n("ul",[n("li",[t._v("string(b) 用在 map 的 key 中 "),n("code",[t._v("ma[string(b)]++")])]),t._v(" "),n("li",[t._v("string(b) 在字符串的拼接句子中 “a” + string(b)")]),t._v(" "),n("li",[t._v("for-range 中的 string 到[]byte 的转换")])]),t._v(" "),n("h2",{attrs:{id:"for-range-字符串"}},[t._v("for-range 字符串")]),t._v(" "),n("p",[t._v("因为对一个字符串使用 range 的时候,go 默认使用 utf8 的编码方式,但是 string 的底层是[]byte 的存储方式,所以直接 range 的时候,将这个时候的字符转化为字符就会发生乱码的情况")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[t._v("s "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"你好"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" k "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" s "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// äå ")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Printf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"%c"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("k"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("解决方法有两种:")]),t._v(" "),n("ol",[n("li",[t._v("直接获取 value 值")])]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ts "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"你好"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里的 v 就直接是 rune")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" s "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Printf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"%c"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("ol",{attrs:{start:"2"}},[n("li",[t._v("将 s 转化为 []rune 来获取真正的 unicode 编码:")])]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ts "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"你好"')]),t._v("\n\tsr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("rune")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里的 v 就直接是 rune")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" sr "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Printf")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"%c"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" sr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])])}),[],!1,null,null,null);s.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/27.1c78dbce.js b/assets/js/27.1c78dbce.js new file mode 100644 index 000000000..ad98cff05 --- /dev/null +++ b/assets/js/27.1c78dbce.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[27],{457:function(t,s,a){"use strict";a.r(s);var n=a(36),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"作用域"}},[t._v("作用域")]),t._v(" "),a("p",[t._v("先给出两个经典的案例:")]),t._v(" "),a("p",[t._v("if 级变量")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n \n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" b "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" c "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("c"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("a b c 这三个变量的作用域属于 if 这整个结构,只要前面初始化过,那么后面就可以使用,所以你会发现 print 可以输出三个变量。")]),t._v(" "),a("p",[t._v("loop 级变量")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n time"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("time"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("因为 "),a("strong",[t._v("i 属于 loop 级别")]),t._v(",通常来说 for 执行过程是快于新开辟一个 goroutine 的,所以导致这是个 goroutine 输出的都是最后一个 i,即都输出 10")]),t._v(" "),a("p",[t._v("根据 go 1.20 的表述,以后这个 loop 级别的变量"),a("strong",[t._v("非常有可能")]),t._v("会被修改成局部变量。如果没有被修改,我们可以开辟一个局部变量 (go 1.22 已经将这个 loop 级变量修改为局部变量!!!)")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" i\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n time"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("time"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"代码块的类型"}},[t._v("代码块的类型")]),t._v(" "),a("p",[t._v("代码块是代码执行的基本单位,代码执行流总是从一个代码块流转到另一个代码块。")]),t._v(" "),a("ul",[a("li",[t._v("显式代码块\n"),a("ul",[a("li",[t._v("函数体")]),t._v(" "),a("li",[t._v("for 循环体")]),t._v(" "),a("li",[t._v("if 语句")])])]),t._v(" "),a("li",[t._v("隐式代码块\n"),a("ul",[a("li",[t._v("宇宙代码块:所有的 go 源码都属于这个代码块")]),t._v(" "),a("li",[t._v("包代码块:每一个包都有一个代码块,这个代码块包括了整个包的源码")]),t._v(" "),a("li",[t._v("文件代码块:每一个文件都有一个代码块,这个代码块包括了整个文件的源码")])])])]),t._v(" "),a("p",[t._v("预定义的标识符,比如 make new cap len 作用域是宇宙代码块")]),t._v(" "),a("p",[t._v("包级变量,包级常量的作用域是包代码块")]),t._v(" "),a("p",[t._v("go 源代码中导入的包名称作用域是文件代码块")]),t._v(" "),a("p",[t._v("方法接收器,函数参数,返回值,对应的作用域是函数作用域")]),t._v(" "),a("p",[t._v("函数体内部声明的变量和常量作用域仅限于函数体内部的局部作用域")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" A "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// A的作用域就仅限于这个大括号里面")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"if"}},[t._v("if")]),t._v(" "),a("p",[t._v("if 包含了一个隐式的代码块:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("c "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" b"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("b "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" c "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("其实等于:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v(" c"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n a"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n b "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" b "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" c "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("所以,在 if 中的大括号里,是可以输出 a 的值的。")]),t._v(" "),a("h2",{attrs:{id:"for"}},[t._v("for")]),t._v(" "),a("p",[t._v("for 循环的作用域可以使用下面的两种方式展现。for 循环中的变量都属于 loop 级,不属于每次的那个小循环的局部变量,所以在整个的 loop 过程中,变量是唯一的,就是那一个。只不过不同的时间点,这个变量的值有可能是不同的。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 等价于")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这里需要注意一下,一旦 go 在后续的版本中修改了 loop 级变量这个设定,这个等价就不成立了。上文有讲。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" k"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" slice "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 等价于")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n k"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" k"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" slice "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"switch-select"}},[t._v("switch && select")]),t._v(" "),a("p",[t._v("这两种结构的等价是相同的,都是 case 级。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("switch")]),t._v(" expression"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" list1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" list2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("default")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\n等价于\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("switch")]),t._v(" expression"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" list1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" list2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("default")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("select 稍微有点不同,因为 select 中的 case 是可以新建一个变量的,除了我们说的每一个 case 是一个作用域之外,case 上新建的这个变量也只是属于下面这个小的作用域。")])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/28.8c065b05.js b/assets/js/28.8c065b05.js new file mode 100644 index 000000000..e661a40f8 --- /dev/null +++ b/assets/js/28.8c065b05.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[28],{455:function(t,s,n){"use strict";n.r(s);var a=n(36),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"其他内容"}},[t._v("其他内容")]),t._v(" "),n("h2",{attrs:{id:"指针"}},[t._v("指针")]),t._v(" "),n("p",[t._v("go 语言的指针类型是 unsafe.Pointer,"),n("code",[t._v("uintptr")]),t._v(" 是指针的计算类型,也就是说,地址必须通过 unsafe.Pointer 提取以后,再转为 uintptr 才能进行计算,uintptr 的实质是一个整数类型,并且完全可以容纳所有的指针的数据。")]),t._v(" "),n("p",[t._v("go 语言使用 "),n("code",[t._v("*")]),t._v(" 符号用来表示指针类型,以及取指针类型实际数据这个操作,使用 "),n("code",[t._v("&")]),t._v(" 取变量的指针 (地址),我们看一下操作")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 使用 内置函数 new() 去取结构体的地址")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" p "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("People "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("new")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("People"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("p "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" People"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"1"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 使用 取值符号去取得 变量 p 的地址")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("p"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" People "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tname "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("p",[t._v("我们看一下如何直接计算指针类型")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// a ,string 类型")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\t\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// b ,a变量的地址")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" b "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("a\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"打印初始a变量的地址"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// c,转为可计算的指针类型之后的变量")]),t._v("\n\tc "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("uintptr")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("unsafe"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Pointer")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// uintptr(unsafe.Pointer())")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"打印可计算指针类型c"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" c"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tc"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"打印可计算指针类型c++"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" c"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 将c再转化为 a 的地址")]),t._v("\n\tb "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("unsafe"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Pointer")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("c"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// (*string)(unsafe.Pointer())")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"打印转换后的a的指针"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[t._v("打印初始a的指针地址 "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x14000096230")]),t._v("\n打印可计算指针类型c "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1374390149680")]),t._v("\n打印可计算指针类型c"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1374390149681")]),t._v("\n打印转换后的a的指针 "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x14000096231")]),t._v("\n")])])]),n("h2",{attrs:{id:"go-的引用类型和非引用类型"}},[t._v("go 的引用类型和非引用类型")]),t._v(" "),n("table",[n("thead",[n("tr",[n("th",{staticStyle:{"text-align":"center"}},[t._v("引用类型")]),t._v(" "),n("th",{staticStyle:{"text-align":"center"}},[t._v("非引用类型")])])]),t._v(" "),n("tbody",[n("tr",[n("td",{staticStyle:{"text-align":"center"}},[t._v("slice, interface, chan, map")]),t._v(" "),n("td",{staticStyle:{"text-align":"center"}},[t._v("其余")])])])]),t._v(" "),n("p",[t._v("引用类型的实质其实就是 fat pointer 即:胖指针,整个类型使用 struct 作为底层数据,data 是一个指针地址,它指向的是要使用的数据。")]),t._v(" "),n("h2",{attrs:{id:"全局变量和局部变量"}},[t._v("全局变量和局部变量")]),t._v(" "),n("p",[t._v("全局变量,引用类型分配在堆上,值类型分配在栈上")]),t._v(" "),n("p",[t._v("局部变量,一般分配在栈上,当局部对象过大的时候分配在堆上,如果对局部变量做逃逸分析,发现它逃逸到了堆上,那么就将其分配到堆上")]),t._v(" "),n("h2",{attrs:{id:"go-init-函数的执行顺序"}},[t._v("go init 函数的执行顺序")]),t._v(" "),n("p",[t._v("init 函数在一个包内的执行顺序:对同一个 "),n("code",[t._v("go")]),t._v(" 文件的 "),n("code",[t._v("init()")]),t._v(" 调用顺序是从上到下的,对同一个 "),n("code",[t._v("package")]),t._v(" 中的不同文件,将文件名按字符串进行 “从小到大” 排序 (数字排在前面),之后顺序调用各文件中的 "),n("code",[t._v("init()")]),t._v(" 函数")]),t._v(" "),n("p",[t._v("对于不同的包,如果不相互依赖的话,按照 main 包中 import 的顺序调用其包中的 "),n("code",[t._v("init()")]),t._v(" 函数,如果包存在依赖,例如:导入顺序 main –> A –> B –> C,则执行顺序为 C –> B –> A –> main")]),t._v(" "),n("p",[n("strong",[t._v("go 会先执行全局变量再执行 init")]),t._v(",当然多包全局变量的初始化跟 init 的执行顺序是一致的")]),t._v(" "),n("h2",{attrs:{id:"go-可比较类型"}},[t._v("go 可比较类型")]),t._v(" "),n("blockquote",[n("p",[t._v("https://go.dev/ref/spec#Comparison_operators")])]),t._v(" "),n("p",[t._v("不可以的")]),t._v(" "),n("ul",[n("li",[t._v("切片,map func (这几种都是因为自己本身变来变去,同样的变量,不同时间,内部值就变了,所以他们不可以参与 == 的比较) 变量无法参与一般的比较,但是他们可以和 nil 作对比")])]),t._v(" "),n("p",[t._v("可以的:")]),t._v(" "),n("ul",[n("li",[t._v("数字类型,bool,string 这种常见类型可比较\n"),n("ul",[n("li",[n("code",[t._v("a := make(chan []int)")]),t._v(" 即使是这样的内部含有不可比较的通道变量本身也是可以比较的。")])])]),t._v(" "),n("li",[t._v("chan 当两个 chan 类型的变量进行比较时,只有它们都为 nil 或者指向同一个通道时才返回 true,否则返回 false。")]),t._v(" "),n("li",[t._v("内部字段都必须是可比较类型的数组和结构体可以比较")]),t._v(" "),n("li",[t._v("指针可以比较,指针的实质是一个整数类型")]),t._v(" "),n("li",[t._v("接口类型是可比较类型")])]),t._v(" "),n("p",[t._v("所以,接口变量是可以作为 map 的 key 值的,因为接口可以比较")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tb "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s Some\n\tb"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Some "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("methods")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h2",{attrs:{id:"go-可寻址类型"}},[t._v("go 可寻址类型")]),t._v(" "),n("p",[t._v("以下内容是"),n("strong",[t._v("不可寻址")]),t._v("的量")]),t._v(" "),n("blockquote",[n("p",[t._v("字面量的解释 var a int = 12,12 就是字面量,也就是所谓的那个值本身;结果值的解释:就是这个结果这个 value 的值")])]),t._v(" "),n("ul",[n("li",[n("p",[t._v("常量 "),n("code",[t._v("const a = 12")]),t._v(" &a 不可寻址")])]),t._v(" "),n("li",[n("p",[t._v("基本类型值的字面量 "),n("code",[t._v("a := 12")]),t._v(" &12 不可寻址")])]),t._v(" "),n("li",[n("p",[t._v("算术操作的结果值 "),n("code",[t._v("a := 12 b := 12 &(b+a)")])])]),t._v(" "),n("li",[n("p",[t._v("对各种字面量的索引表达式和切片表达式的结果值。不过有一个例外,对切片字面量的索引结果值却是可寻址的")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[t._v("a "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" k "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" a "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("k"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 无法寻址,这个数据属于临时的,可变的数据")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n\tb "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" k "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" b "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n\t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这个可以寻址,每个切片值都会持有一个底层数组,")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 而这个底层数组中的每个元素值都是有一个确切的内存地址")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("b"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("k"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])]),t._v(" "),n("li",[n("p",[t._v("切片字面量的切片结果值为什么却是不可寻址?"),n("code",[t._v("a := []int{1,2}, &(a[:1])")]),t._v(" 原因是切片表达式字面量的切片属于临时值跟字面量的结果值不一样,后者属于结果值有效值")])]),t._v(" "),n("li",[n("p",[t._v("对字符串变量的索引表达式和切片表达式的结果值。"),n("code",[t._v('a := "a" &("a") &a[0]')])])]),t._v(" "),n("li",[n("p",[t._v("对字典变量的索引表达式的结果值。"),n("code",[t._v("a := map[int]int{1:1} &(a[1])")])])]),t._v(" "),n("li",[n("p",[t._v("函数字面量和方法字面量,以及对它们的调用表达式的结果值。"),n("code",[t._v("&(func ())")])])]),t._v(" "),n("li",[n("p",[t._v("结构体字面量,见下面例子")])]),t._v(" "),n("li",[n("p",[t._v("类型转换表达式的结果值")])]),t._v(" "),n("li",[n("p",[t._v("类型断言表达式的结果值")])]),t._v(" "),n("li",[n("p",[t._v("接收表达式的结果值")])])]),t._v(" "),n("p",[t._v("我们看一道"),n("strong",[t._v("面试题")]),t._v(":")]),t._v(" "),n("p",[t._v("这道题就是因为中间变量无法获取地址造成的 bug")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 此处go会自动调用值的指针来运行 SetName 但是因为 return Dog{name} 是一个临时的值,所以无法获取到指针")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("New")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"nihao"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("SetName")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"monster"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("New")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("name "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" Dog "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" Dog"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Dog "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tname "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("d "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Dog"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("SetName")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("n "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\td"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("我们可以这么改")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\na "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" \t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("New")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"nihao"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\na"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("SetName")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"monster"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("New")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("name "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" Dog "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" Dog"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Dog "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tname "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("d "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Dog"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("SetName")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("n "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\td"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("p",[t._v("另外自增 ++ 自减 -- 左边的表达式都必须是可寻址的类型,否则也是无法操作的。")]),t._v(" "),n("blockquote",[n("p",[t._v("字典字面量和字典变量索引表达式的结果值是个例外例如 ma [“12”] ++")])]),t._v(" "),n("p",[t._v("在赋值语句中,赋值操作符左边的表达式的结果值必须可寻址的,但是对字典的索引结果值也是可以的")]),t._v(" "),n("p",[t._v("总结一下:")]),t._v(" "),n("ul",[n("li",[n("code",[t._v("常量")]),t._v(" + string 这种无法更改的数据无法寻址,函数通常来说也可以算作 “常量”,应该它就是一段代码,不可更改")]),t._v(" "),n("li",[n("code",[t._v("结果值/字面量")]),t._v(" 因为其无法更改所以寻址将没有意义")]),t._v(" "),n("li",[n("code",[t._v("中间值")]),t._v(" 或者 "),n("code",[t._v("临时对象")]),t._v(" 比如说 &(a + b) 这类临时的变量的内存地址没有意义")]),t._v(" "),n("li",[n("code",[t._v("不安全的操作")]),t._v(" 比如 map 中的 k-v 经常要从一个哈希桶迁移到另一个桶,所以你获取地址,它经常会改变,外界还不得而知,所以获取到这个 key-value 的地址是不安全的")])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/29.b8584add.js b/assets/js/29.b8584add.js new file mode 100644 index 000000000..1153be083 --- /dev/null +++ b/assets/js/29.b8584add.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[29],{456:function(t,s,n){"use strict";n.r(s);var a=n(36),p=Object(a.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"反转控制"}},[t._v("反转控制")]),t._v(" "),n("p",[t._v("概念:控制逻辑与业务逻辑分享,让业务逻辑依赖控制逻辑。")]),t._v(" "),n("p",[t._v("大概的思路就是***将控制代码剥离,然后在逻辑代码中去调用控制代码即可***,具体实现的时候,就是在逻辑代码中内嵌控制代码的类型,这样即可。")]),t._v(" "),n("p",[t._v("首先需要明确的是 “控制逻辑是什么” 这个问题,我们举一个例子:有一个数据库,数据库的增删改查就是控制逻辑,使用这些基础的动作去将用户的名字存入数据库,删除用户的名字,这叫业务逻辑,我们所说的反转控制,就是将基础的控制剥离出来,下面我们看一个例子:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这是控制代码,我们把控制代码先揪出来。")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Undo "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("undo "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Undo"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("function "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("undo "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("undo"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" function"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这段代码的作用:恢复")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("undo "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Undo"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Undo")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n functions "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("undo\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("functions"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" errors"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("New")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"No functions to undo"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n index "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("functions"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" function "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" functions"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("index"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" function "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("function")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// For garbage collection")]),t._v("\n functions"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("index"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" \n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里是为了规避在调用delelte的时候,又调用了add的情况")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 因为 delete的时候会调用add,add的时候会再次调用delete")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 为了防止这种情况,我们认为的将这个切片减去一个即可。")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("undo "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" functions"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("index"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("我们将控制逻辑嵌入到业务逻辑中")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" IntSet "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n data "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v("\n undo Undo\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n \n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewIntSet")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" IntSet "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" IntSet"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("data"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 直接继承")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("set "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("IntSet"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Undo")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" set"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("undo"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Undo")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n \n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("set "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("IntSet"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Contains")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" set"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("data"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("x"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("set "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("IntSet"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("set"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Contains")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 增加一个数据")]),t._v("\n set"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("data"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("x"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 如果是恢复的话,是需要删除一个数据的")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 所以这里的操作就是删除")]),t._v("\n set"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("undo"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" set"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Delete")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n set"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("undo"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n \n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("set "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("IntSet"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Delete")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" set"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Contains")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("delete")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("set"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("data"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" x"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n set"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("undo"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" set"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n set"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("undo"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])])])}),[],!1,null,null,null);s.default=p.exports}}]); \ No newline at end of file diff --git a/assets/js/3.159fe1e3.js b/assets/js/3.159fe1e3.js new file mode 100644 index 000000000..bfe98a362 --- /dev/null +++ b/assets/js/3.159fe1e3.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[3],{399:function(t,e,n){},432:function(t,e,n){"use strict";n(399)},548:function(t,e,n){"use strict";n.r(e);var i={functional:!0,props:{type:{type:String,default:"tip"},text:String,vertical:{type:String,default:"top"}},render:function(t,e){var n=e.props,i=e.slots;return t("span",{class:["badge",n.type],style:{verticalAlign:n.vertical}},n.text||i().default)}},r=(n(432),n(36)),p=Object(r.a)(i,void 0,void 0,!1,null,"15b7b770",null);e.default=p.exports}}]); \ No newline at end of file diff --git a/assets/js/30.40e92043.js b/assets/js/30.40e92043.js new file mode 100644 index 000000000..a537d9e93 --- /dev/null +++ b/assets/js/30.40e92043.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[30],{459:function(t,s,n){"use strict";n.r(s);var a=n(36),r=Object(a.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"map-filter-reduce"}},[t._v("map-filter-reduce")]),t._v(" "),n("p",[t._v("map filter reduce 这三个操作经常用在数据的处理上,比如数据的统计业务。")]),t._v(" "),n("p",[t._v("下面看一下这三者基本的做法是什么")]),t._v(" "),n("p",[t._v("我们用做饭来进行类比:")]),t._v(" "),n("ul",[n("li",[t._v("map 30 个进,30 个出,类似于洗菜的过程")]),t._v(" "),n("li",[t._v("filter 30 个进,15 个出,类似于摘菜")]),t._v(" "),n("li",[t._v("reduce 15 个进,1 个出,类似于炒菜的过程")])]),t._v(" "),n("p",[t._v("下面这个案例,我们要对于一组 string 进行数据处理。map 的过程,我们对这堆 string 进行全部的修饰,filer 过程我们挑选出合适的 string,reduce 过程我们最终输出要的成品")]),t._v(" "),n("p",[t._v("map 的举例")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("MapStr")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("f "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" newS "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n newS "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("newS"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" newS\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("filter 的举例")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("FilterStr")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("f "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" newS "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n newS "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("newS"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" newS\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("reduce 的举例")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("ReduceStr")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("f "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" newS "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n newS "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" newS\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ts "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"李明月"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"李月"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"百伯"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"张兰"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"武滴滴"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"叶赫那拉"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"迪丽热巴"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tns "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("MapStr")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"同学"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ns"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tns "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("FilterStr")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ns"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("rune")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ns"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tsum "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("ReduceStr")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ns"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"-"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("sum"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("这段代码的意思就是首先,我们有一堆学生的名字,我们在名字后面先加上同学,然后我们要把名字本身大于两个字的同学挑出来,并且把他们组成一句话。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("李明月同学 李月同学 百伯同学 张兰同学 武滴滴同学 叶赫那拉同学 迪丽热巴同学"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\nfilter "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("李明月同学 武滴滴同学 叶赫那拉同学 迪丽热巴同学"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\nreduce "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("李明月同学"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("武滴滴同学"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("叶赫那拉同学"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("迪丽热巴同学"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("\n")])])]),n("p",[t._v("通过这个案例我们发现,map-filter-reduce 本身只是控制逻辑,真正的业务逻辑其实是那个函数类型,所以说 map-filter reduce 也是标准的 "),n("code",[t._v("控制代码-业务代码")]),t._v(" 分离设计")])])}),[],!1,null,null,null);s.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/31.89f31e41.js b/assets/js/31.89f31e41.js new file mode 100644 index 000000000..13043145d --- /dev/null +++ b/assets/js/31.89f31e41.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[31],{458:function(t,e,n){"use strict";n.r(e);var s=n(36),o=Object(s.a)({},(function(){var t=this.$createElement,e=this._self._c||t;return e("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}},[e("h1",{attrs:{id:"go-generation"}},[this._v("go generation")]),this._v(" "),e("p",[this._v("// go 已经推出了泛型,因此没有写的必要了。")])])}),[],!1,null,null,null);e.default=o.exports}}]); \ No newline at end of file diff --git a/assets/js/32.a03da74e.js b/assets/js/32.a03da74e.js new file mode 100644 index 000000000..da4b2dcf9 --- /dev/null +++ b/assets/js/32.a03da74e.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[32],{461:function(t,s,n){"use strict";n.r(s);var a=n(36),p=Object(a.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"修饰器"}},[t._v("修饰器")]),t._v(" "),n("p",[t._v("简单的描述:一个修饰器函数,参数是函数,返回一个运行了这个参数函数的跟参数函数保持相同的函数。")]),t._v(" "),n("p",[t._v("如果是接口,那么就是一个修饰器函数,参数是一个接口,返回值还是这个接口。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("D")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"done"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("D")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h2",{attrs:{id:"具体案例"}},[t._v("具体案例")]),t._v(" "),n("p",[t._v("http 处理 cookie 和 header 的案例:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("HandleFunc")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithHeader")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("hello"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("HandleFunc")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/s"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithCookie")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("hello"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("ListenAndServe")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('":8080"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("hello")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("w http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ResponseWriter"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("r "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Request"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Fprintln")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("w"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithHeader")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("h http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("HandlerFunc"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("HandlerFunc"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("w http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ResponseWriter"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("r "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Request"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n w"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Header")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Set")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Server"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"HelloServer"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("h")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("w"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("r"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n \n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithCookie")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("h http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("HandlerFunc"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("HandlerFunc "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("w http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ResponseWriter"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" r "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Request"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tcookie "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Cookie"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("Name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"auth"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Value"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"123456"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Path"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\thttp"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("SetCookie")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("w"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" cookie"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("h")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("w"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" r"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h2",{attrs:{id:"多修饰器的-pipeline-串型调用"}},[t._v("多修饰器的 pipeline (串型调用)")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Hander "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("HandlerFunc"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("HandlerFunc\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("DealWith")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("h http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("HandlerFunc"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("handlers "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),t._v("Hander"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("HandlerFunc"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" handlers "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n d "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" handlers"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("handlers"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n h "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("d")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("h"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" h\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("HandleFunc")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("DealWith")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("hello"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("WithCookie"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("WithHeader"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n http"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("ListenAndServe")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('":8080"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])])}),[],!1,null,null,null);s.default=p.exports}}]); \ No newline at end of file diff --git a/assets/js/33.39bfe4d4.js b/assets/js/33.39bfe4d4.js new file mode 100644 index 000000000..b65508ac8 --- /dev/null +++ b/assets/js/33.39bfe4d4.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[33],{460:function(t,s,a){"use strict";a.r(s);var n=a(36),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"pipeline"}},[t._v("pipeline")]),t._v(" "),a("p",[t._v("pipeline 模式,它将一系列的小命令组合成一个大的命令,它可以将***一个大命令拆分为一个一个的高内聚的小命令***,我们可以使用这一系列的小命令组合更多更复杂的命令。")]),t._v(" "),a("p",[t._v("下面给出一个需求,我们将这个需求写成 pipeline 的模式。")]),t._v(" "),a("p",[t._v("需求是:对一个 int 的切片做 “平方”,“过滤奇函数”,“求和” 的操作,并且使用 pipeline 的方式去调用这一系列的高内聚的小函数。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 初始函数")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("echo")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("data "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tout "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" data "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\tout "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" v\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("out"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" out\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 平方")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("sq")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("in "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tout "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" in "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\tout "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" v\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("out"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" out\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 过滤奇函数")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("odd")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("in "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tout "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" in "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" v"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\tout "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" v\n\t\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("out"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" out\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 求和")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("sum")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("in "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tout "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tsum "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" in "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\tsum "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+=")]),t._v(" v\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\tout "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" sum\n\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("out"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" out\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" EchoFunction "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" PipeFunction "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// pipeline 处理")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Handle")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" e EchoFunction"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ps "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),t._v("PipeFunction"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tch "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("e")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" ps "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tch "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("v")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ch"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" ch\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// main函数")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ta "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("6")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("7")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("8")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("9")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tdata "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Handle")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" echo"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" sq"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" odd"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" sum"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("data"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/34.7610889f.js b/assets/js/34.7610889f.js new file mode 100644 index 000000000..1df990d6d --- /dev/null +++ b/assets/js/34.7610889f.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[34],{462:function(t,s,n){"use strict";n.r(s);var a=n(36),p=Object(a.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"k8s-visitor"}},[t._v("k8s visitor")]),t._v(" "),n("p",[t._v("这是一种将算法和数据结构分离的编程模式。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Visitor "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("shap Shap"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Shap "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Accept")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Visitor"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("我们创造一个函数类型,它接受一个接口作为参数。")]),t._v(" "),n("p",[t._v("我们定义刚才那个接口,并且在接口中我们定义一个函数,这个函数中的参数是刚才的函数类型。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" result1 "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n data "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("r result1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Accept")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v Visitor"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("v")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("r"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" result2 "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n data "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n name "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("r result2"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Accept")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v Visitor"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("v")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("r"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("我们实现了两个 struct。")]),t._v(" "),n("p",[t._v("接下来,我们定义一些函数,这些函数是 visitor 函数。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("n1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("shap Shap"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("shap"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("n2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("shap Shap"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("shap"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("接下来我们在 mian 函数中调用这个 visitor 函数。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n r1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" result1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n r2 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" result2"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"shgopher"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n result "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("Shap"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("r1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" r2"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" result "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Accept")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("n1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Accept")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("n2"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("数据是 r1,和 r2,算法是 n1 和 n2,这样,我们就可以将算法和数据给分开,互相不会有耦合。")])])}),[],!1,null,null,null);s.default=p.exports}}]); \ No newline at end of file diff --git a/assets/js/35.32a9ec30.js b/assets/js/35.32a9ec30.js new file mode 100644 index 000000000..c2f7873bf --- /dev/null +++ b/assets/js/35.32a9ec30.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[35],{463:function(t,s,e){"use strict";e.r(s);var n=e(36),r=Object(n.a)({},(function(){var t=this.$createElement,s=this._self._c||t;return s("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}},[s("h1",{attrs:{id:"k8s-builder"}},[this._v("k8s builder")])])}),[],!1,null,null,null);s.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/36.8d562ec5.js b/assets/js/36.8d562ec5.js new file mode 100644 index 000000000..133207e52 --- /dev/null +++ b/assets/js/36.8d562ec5.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[36],{464:function(t,s,n){"use strict";n.r(s);var a=n(36),r=Object(a.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"综合题"}},[t._v("综合题")]),t._v(" "),n("blockquote",[n("p",[t._v("本内容选自 "),n("a",{attrs:{href:"https://github.com/kubernetes/kubernetes/blob/cea1d4e20b4a7886d8ff65f34c6d4f95efcb4742/staging/src/k8s.io/cli-runtime/pkg/resource/visitor.go",target:"_blank",rel:"noopener noreferrer"}},[t._v("k8s"),n("OutboundLink")],1)])]),t._v(" "),n("h2",{attrs:{id:"k8s-visitor-修饰器-pipeline"}},[t._v("k8s visitor + 修饰器 + pipeline")]),t._v(" "),n("p",[t._v("这种写法,以 k8s 的 visitor 作为主要的实现方法,修饰器和 pipeline 作为辅助的实现手法")]),t._v(" "),n("p",[t._v("首先我们实现这段代码的目的是为了将数据结构和算法分离,并且,我们希望每段的算法处理数据结构的一部分。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 行为")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" visitorFunc "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 抽象层")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Visitor "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Visit")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("visitorFunc"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 数据")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" info "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tname "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\tyear "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\taddr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Visit")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v visitorFunc"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("v")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("我们基础部分已经写好了,这样就具备了基本的 visitor 的模式,接下来我们来写算法的实现")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 多态")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" OneDeal "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tvisitor Visitor\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("o OneDeal"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Visit")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v visitorFunc"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" o"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("visitor"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Visit")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\te "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("v")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"one deal"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" e\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" twoDeal "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tvisitor Visitor\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("o twoDeal"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Visit")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v visitorFunc"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" o"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("visitor"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Visit")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\te "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("v")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"two deal"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("addr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("year"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" e\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("当算法搞定以后,我们在 main 调用一下:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\to "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" v Visitor "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" o\n\tv "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" OneDeal"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tv "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" twoDeal"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tl "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\ti"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"liu"')]),t._v("\n\t\ti"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("year "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2020")]),t._v("\n\t\ti"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("addr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"beijing"')]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tv"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Visit")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("l"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("p",[t._v("可以看到,visitor,pipline,以及修饰器的模式都运用了")]),t._v(" "),n("ul",[n("li",[t._v("visitor:上面一个 type func,下面的 interface,连在一起就是 visitor 模式")]),t._v(" "),n("li",[t._v("pipline:可以看到 visitor 的变量在不停的传递,然后调用的时候肯定会一直的调用不同的 visit func 函数,这就是 pipline")]),t._v(" "),n("li",[t._v("修饰器:不同的 function,在不停的增加内容,并且输出的还是这个 function,这就是修饰器模式")])]),t._v(" "),n("p",[t._v("可以看到,这个模式下,visitor 是主要的模式,其他的模式都是因这个模式而附带的效果,所以也可以说,visitor 自带 pipline 和修饰器模式,所以这种写法就单单的只是 visitor 而已。")]),t._v(" "),n("p",[t._v("接下来,我们将修饰器模式作为主要的模式,另外加上 visitor 和 pipline")]),t._v(" "),n("h2",{attrs:{id:"修饰器-k8s-visitor-pipeline"}},[t._v("修饰器 + k8s visitor + pipeline")]),t._v(" "),n("p",[t._v("同样的我们需要先构造一个 visitor 系统")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" VisitorFunc "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Visitor "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Visit")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("VisitorFunc"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Info "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tname "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\tyear "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\taddr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Visit")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v VisitorFunc"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("v")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("接下来我们创建一个修饰器系统")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" VisitorBox "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tvisitor Visitor "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 行为")]),t._v("\n\tvisitorFucs "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("VisitorFunc "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 代表数据的桥梁")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewVisitorBox")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v Visitor"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fn "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),t._v("VisitorFunc"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" Visitor "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("fn"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" v\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" VisitorBox"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("visitorFucs"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" fn"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" visitor"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("vi VisitorBox"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Visit")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v VisitorFunc"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" vi"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("visitor"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Visit")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" e "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" e "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" e\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("v")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" k "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" vi"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("visitorFucs "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" vi"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("visitorFucs"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("k"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n\t\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("p",[t._v("接下来让我们调用一下")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n\tloadFile "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("info "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tinfo"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"li"')]),t._v("\n\t\tinfo"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("year "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v("\n\t\tinfo"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("addr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"bj"')]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tfunc1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("info "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tfunc2 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("info "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("year"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tfunc3 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("info "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("addr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n\tinfo "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Info"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" v Visitor "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" info\n\tv "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewVisitorBox")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" func1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" func2"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" func3"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tv"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Visit")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("loadFile"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("这种写法跟上面的写法改变之处在于创建了一个修饰器的 struct,并且装在了一个 pipline 的切片,然后我们就不用依次调用不同的算法结构体了,我们只需要在 new box 的时候将要执行的函数放入进去即可。")])])}),[],!1,null,null,null);s.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/37.1fac0e0a.js b/assets/js/37.1fac0e0a.js new file mode 100644 index 000000000..2ec0badd8 --- /dev/null +++ b/assets/js/37.1fac0e0a.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[37],{466:function(t,s,a){"use strict";a.r(s);var n=a(36),r=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"变量声明"}},[t._v("变量声明")]),t._v(" "),a("h2",{attrs:{id:"变量声明符号-var-和"}},[t._v("变量声明符号 "),a("code",[t._v("var")]),t._v(" 和 "),a("code",[t._v(":=")])]),t._v(" "),a("p",[t._v("在 go 语言中,我们使用 "),a("code",[t._v("var")]),t._v(" 来表示声明一个变量")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello world"')]),t._v("\n")])])]),a("p",[t._v("这就是一个标准的变量声明方式,var 符号在最前面,接着就是变量 a,变量后面紧跟着是变量的类型,这里是 string 类型,也就是字符串类型,"),a("code",[t._v("=")]),t._v(" 后面是要赋值的具体值")]),t._v(" "),a("p",[t._v("我们也可以直接声明不赋予初始值,go 语言默认,声明即赋予初始值,那么这里的字符串类型初始值就是一个空字符串 "),a("code",[t._v('""')])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n")])])]),a("p",[t._v("go 语言具有类型推断能力,所以我们可以省略类型,让 go 语言的编译器去推断类型")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello world"')]),t._v("\n")])])]),a("p",[t._v("编译器会自动推断 a 为 string 类型")]),t._v(" "),a("p",[t._v("在函数体内部 (这是一个先决条件) 我们"),a("strong",[t._v("也")]),t._v("可以使用省略的写法,就是使用一个符号 "),a("code",[t._v(":=")]),t._v(" 来充当 "),a("code",[t._v("var")]),t._v(" 的角色,也就是初始化的工作,比如说")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello world"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("可以看到,整个的用法是 "),a("code",[t._v("[变量] [:=] [初始值]")]),t._v(" 这三者缺一不可,而且还不能多,不能在 a 后面带有类型,不能省略初始值,且仅限于函数/方法内部使用")]),t._v(" "),a("p",[t._v("go 语言支持多变量同时赋值")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" b "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" c"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" d "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"1"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"2"')]),t._v("\n\na"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" b "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello world"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v("\n")])])]),a("p",[t._v("其中,使用 var 进行声明的时候,如果是多个变量同时声明,必须是相同类型;使用 "),a("code",[t._v(":=")]),t._v(" 进行多变量赋值时,多个变量可以不同类型,因为全靠编译器推断")]),t._v(" "),a("h2",{attrs:{id:"常见的变量声明方式"}},[t._v("常见的变量声明方式")]),t._v(" "),a("p",[t._v("从广义上来说,go 语言只有两种变量,包一级的变量和函数一级的变量")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" DefaultValue "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewMethod")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("n "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("其中 "),a("code",[t._v("DefaultValue")]),t._v(" 就是包级变量,"),a("code",[t._v("n")]),t._v(" 就是函数级变量 (也是形式变量)")]),t._v(" "),a("p",[t._v("下面我列举一些常见的声明方式")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n a "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n b "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int32")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" c "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//包级变量")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("hi")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n d "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//仅限函数内部使用,变量后面不能有类型")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" c "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 函数级变量")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" e "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello world"')]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//自动推断变量类型")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("可以说声明的方式很多,不过呢,在一个项目中应该尽量保证声明方式的一致性,因为可以加强代码的统一性,减少理解代码的难度。")]),t._v(" "),a("h2",{attrs:{id:"go-语言可导出变量"}},[t._v("go 语言可导出变量")]),t._v(" "),a("p",[t._v("go 语言跟一般的语言不同,它使用变量"),a("strong",[t._v("首")]),t._v("字母的大小写来区分变量的可导出性质,大写 (如果使用中文作为变量名称,默认是可导出的) 代表可导出,小写代表仅限包内部使用 (包这一级,多个文件只要是同一个包就可以使用)")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" Example\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" OutPutName "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0o77")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" inName "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x99")]),t._v("\n")])])]),a("p",[t._v("其中 "),a("code",[t._v("OutPutName")]),t._v(" 是一个可导出的变量,"),a("code",[t._v("inName")]),t._v(" 是不可导出变量")]),t._v(" "),a("p",[t._v("go 语言还拥有比如函数,方法,结构体,接口,等等各类型的组件,这里你可以先不懂到底是什么,你先有一个印象,但凡首字母是大写的都是可以导出的,小写就是包内使用,没错,go 语言就是这么简单")]),t._v(" "),a("h2",{attrs:{id:"包级变量"}},[t._v("包级变量")]),t._v(" "),a("p",[a("code",[t._v("第一种声明形式")]),t._v(":"),a("strong",[t._v("声明的同时,显式初始化")])]),t._v(" "),a("ul",[a("li",[a("code",[t._v('var a = method("t")')]),t._v(" go 编译器根据右侧的返回值自动确定左侧变量的类型")]),t._v(" "),a("li",[a("code",[t._v("var a = 3.14")]),t._v(" 在没有具体返回值,没有具体类型的情况下,go 赋予它默认类型,比如 float 的默认类型就是 float64,int 类型的默认类型就是 int")]),t._v(" "),a("li",[t._v("如果要显式的赋予类型,并且保证命名的一致性 "),a("code",[t._v("var a int = 12")]),t._v(" 的行为应该避免,应该写成 "),a("code",[t._v("var a = int(12)")]),t._v(" 用来保证一致性"),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3.14")]),t._v("\n b "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n e "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" errors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("New")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"EOF"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])])])]),t._v(" "),a("p",[a("code",[t._v("第二种声明形式")]),t._v(":"),a("strong",[t._v("声明但是延迟初始化")])]),t._v(" "),a("p",[t._v("只有 "),a("code",[t._v("var a int64")]),t._v(" 这一种方式,不过呢,go 语言的声明是直接赋予零值的,比如说这里的 a 默认就是 "),a("code",[t._v("0")])]),t._v(" "),a("p",[t._v("go 语言变量声明的聚集和就近原则:将同一类型的放在一个 var() 内部;或者另一种分类方法:将有初始值的放在一个 var 里,将延迟初始化的放在另一个 var 里。")]),t._v(" "),a("p",[t._v("分类一")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\na "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\nb "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\nc "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\nd "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\ne "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\nf "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("分类二")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v("\n b "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"string"')]),t._v("\n c "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"string"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n d "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n c "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n f "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("接下来谈一谈就近原则,变量的声明和变量的使用尽量的近,不要都声明在头部,如果一个变量被全局大量使用,那么可以放在头部,如果就是仅仅使用少量的次数,还是应该在使用的前面就进进行声明")]),t._v(" "),a("h2",{attrs:{id:"函数级变量"}},[t._v("函数级变量")]),t._v(" "),a("p",[a("code",[t._v("第一种声明形式")]),t._v(":"),a("strong",[t._v("延迟初始化")])]),t._v(" "),a("p",[t._v("在函数体内使用 var")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Method")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("如果变量特别多,也可以使用 "),a("code",[t._v("var()")]),t._v(" 的方法在函数内部使用")]),t._v(" "),a("p",[a("code",[t._v("第二种声明形式")]),t._v(":"),a("strong",[t._v("声明且显式初始化的局部变量")])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("method1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v("\n b "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("int32")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("20")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 改变默认类型")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"小心-shadow-的变量"}},[t._v("小心 shadow 的变量")]),t._v(" "),a("p",[t._v("我们知道,当有两个两个以上的变量在赋值时,如果其中有一个未被提前声明,那么就需要使用 "),a("code",[t._v(":=")]),t._v(",这个时候系统会自动判断有哪些未提前声明,然而有一种场景下系统会发生误判,准确的来说这是一种歧义,系统的判断会跟程序员的心理不一致,出现了变量 shadow 的行为,让我们看一下代码:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithName")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// tracing 为 bool 类型")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" tracing"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" example"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Method")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" example"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Method1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" \n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("在外层,a 已经提前声明,但是在 if 这个作用域中,由于 err 并未提前声明,所以使用了 "),a("code",[t._v(":=")]),t._v(",由于系统"),a("strong",[t._v("无法获知")]),t._v("这里的 a 是否需要再次声明,所以 go 语言默认 a 是一个新的变量,这样外层的 a 就无法得到新的值,外层 a 也就被内层的 a 给 shadow 了。")]),t._v(" "),a("p",[t._v("如果想改变这种 bug,我们可以将 err 也提前声明:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithName")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// tracing 为 bool 类型")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" tracing"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" example"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Method")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" example"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Method1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" \n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("或者也可以改变内部的变量名称,来改变这种 shadow:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithName")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// tracing 为 bool 类型")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" tracing"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n ai"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" example"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Method")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ai\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n ai"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" example"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Method1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" ai\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" \n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),a("ul",[a("li",[t._v("https://juejin.cn/post/7241452578125824061")])])])}),[],!1,null,null,null);s.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/38.53670642.js b/assets/js/38.53670642.js new file mode 100644 index 000000000..0f2d9c03d --- /dev/null +++ b/assets/js/38.53670642.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[38],{467:function(t,s,a){"use strict";a.r(s);var n=a(36),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"复合字面量作为构造器"}},[t._v("复合字面量作为构造器")]),t._v(" "),a("p",[t._v("go 的复杂类型经常采用复合字面量的方式进行初始化,例如 struct,数组,slice map,比如:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" Some"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hi there"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n year"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\narr "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\nsl "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\nm "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("当 struct 需要使用 "),a("code",[t._v("{key :value}")]),t._v(" 来构建时,即使可以按照顺序省略掉 key,也千万不要这么做,加上 key 就可以将结构体的"),a("strong",[t._v("使用和设计解耦")]),t._v(",可以乱序进行 struct 的赋值,即使某些字段没有赋值,那么系统也会自动的赋予它的初始值。")]),t._v(" "),a("p",[t._v("当获取 struct 的指针时,最好是在字面量上取 "),a("code",[t._v("&")]),t._v(",比取变量的指针地址更加符合具体的含义,即:获取结构体的指针类型并且复制给一个变量。比如 "),a("code",[t._v("a := &Some{}")]),t._v(" 就比 "),a("code",[t._v("a := Some{} , &a")]),t._v(" 更好,另外当我们要获取一个结构体的初始值的时候使用 "),a("code",[t._v("a := Some{}")]),t._v(" 比 "),a("code",[t._v("a = new(Some)")]),t._v(" 更加常用。")]),t._v(" "),a("p",[t._v("数组和切片的复合体跟 struct 不同,它使用下标来作为 key,它们几乎不会使用 key:value 的形式,不过下面两种情况还是会出现的:")]),t._v(" "),a("ul",[a("li",[t._v("为了省略中间元素")]),t._v(" "),a("li",[t._v("为了显著的体现下标")])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//为了省略中间元素")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ta "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n b "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token char"}},[t._v("'a'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token char"}},[t._v("'b'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里 '' 代表了 rune类型")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//为了显著的体现下标")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" data "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("当 slice 和 array 含有复合类型的时候,可以直接省略复合类型的类型名称,直接用 "),a("code",[t._v("{}")]),t._v(" 即可")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ta "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("Some"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\tname"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"12"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里尽量不要省略字段名称,方便解除跟结构体设计时的耦合")]),t._v("\n\t\t\tyear"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\tname"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"12"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t\tyear"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Some "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tname "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\tyear "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\tp "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("map 字面量作为初始值的时候,非常自然的就会使用 key-value 的方式,需要注意两点")]),t._v(" "),a("ul",[a("li",[t._v("当 value 或者 key 是复合类型的时候 (key 必须是"),a("RouterLink",{attrs:{to:"/基础/其他内容/#go可比较类型"}},[t._v("可比较的")]),t._v(") 可以省略复合类型的类型名称")],1),t._v(" "),a("li",[t._v("如果是指针类型,那么连指针也可以省略")])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/39.9c8315fb.js b/assets/js/39.9c8315fb.js new file mode 100644 index 000000000..5f2dbf6e5 --- /dev/null +++ b/assets/js/39.9c8315fb.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[39],{468:function(t,s,a){"use strict";a.r(s);var n=a(36),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"go-语言常量"}},[t._v("go 语言常量")]),t._v(" "),a("p",[t._v("go 使用 const 来声明常量,常量表达式的运算在编译期就可以完成了,并且 go 常量是类型安全的,编译器优化友好的元素。")]),t._v(" "),a("p",[t._v("常量中的数据类型只可以是布尔、数字 (整数、浮点和复数) 以及字符串型。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n startOne "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v("\n startTwo "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1.0")]),t._v("\n startThree "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3.14")]),t._v("\n isTrue "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n hi "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hi"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("go 推崇无类型常量,这主要是因为 go 不支持隐式的类型转化,只能显式转换,所以一旦常量给定类型,那么它跟变量之间或许就要将常量转化类型才能进行运算。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\tPI "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3.14")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\t\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("float32")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" PI\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" b "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("float64")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" PI\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("可以看到,如果是没有类型的常量,可以非常灵活的赋予 float32 或者 float64 都可以。")]),t._v(" "),a("h2",{attrs:{id:"局部常量"}},[t._v("局部常量")]),t._v(" "),a("p",[t._v("在 Go 中,局部常量在编译期常量折叠 (compile-time constant folding) 时会被编译器处理成字面值,因此局部常量在编译时会被编译。")]),t._v(" "),a("p",[t._v("例如,下面的代码定义了一个局部常量 x,并使用它来初始化一个变量 y:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" x "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("42")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" y "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" x "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v("\n fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("y"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("在编译时,x 将被处理成字面值 42,因此编译器将 y 初始化为 84,如下所示:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" y "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("84")]),t._v("\n fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("y"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("可见,局部常量在编译期已经被折叠成了字面值,因此在运行时不会再进行计算了。")]),t._v(" "),a("h2",{attrs:{id:"常量不可包含计算"}},[t._v("常量不可包含计算")]),t._v(" "),a("p",[t._v("在 Go 中,常量必须在编译时就可以确定其值。因此,常量的值必须是一个编译时的常量表达式,不能包含运行时的计算。")]),t._v(" "),a("p",[t._v("不过,常量表达式可以包含一些简单的运算,例如加、减、乘、除、取模等算术运算,以及位运算、逻辑运算等。例如:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n x "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 常量表达式,结果为 5")]),t._v("\n y "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("%")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 常量表达式,结果为 0")]),t._v("\n z "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" x "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("||")]),t._v(" y "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 常量表达式,结果为 true")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("常量表达式还可以使用一些内置函数,例如 "),a("code",[t._v("len")]),t._v("、"),a("code",[t._v("cap")]),t._v("、"),a("code",[t._v("make")]),t._v("、"),a("code",[t._v("new")]),t._v(" 等,以及可以在编译期计算的一些标准库函数,例如 "),a("code",[t._v("math.Sin")]),t._v("、"),a("code",[t._v("math.Pi")]),t._v(" 等。")]),t._v(" "),a("p",[t._v("总之,常量必须在编译期就可以确定其值,因此不能包含运行时的计算,但可以包含一些简单的算术、位运算、逻辑运算等。")]),t._v(" "),a("h2",{attrs:{id:"枚举常量-iota"}},[t._v("枚举常量 iota")]),t._v(" "),a("p",[t._v("go 语言提供隐式重复前一个非空表达式的机制")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\tPI "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3.14")]),t._v("\n\ta\n\tb\n\tc\n\td\n\te\n\tf\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("PI"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" c"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" d"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" e"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这段代码将会全部输出 3.14")]),t._v(" "),a("p",[t._v("iota 是 go 语言的一个预定义标识符,它表示 const 声明块中,每一个常量所处位置的偏移量,它本身也是一个无类型的常量,它的初始值是 0,意思是说此处的 iota 跟第一行比偏移了 0 个位置")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<<")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("iota")]),t._v("\n b\n c\n d\n e\n f\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("同一行的常量 iota 值是一样的,可以理解为偏移量相同")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("b "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("iota")]),t._v("\n c"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("d\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("如果要让 iota 的初始值是 1,那么可以这么做")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("iota")]),t._v("\n a\n b\n c\n d\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("iota 只需要在一个 const 群中出现一次即可,出现多次跟出现一次的效果一样")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("iota")]),t._v("\n b "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("iota")]),t._v("\n c "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("iota")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 一样")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("iota")]),t._v("\n b\n c\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("当 iota 群中出现异类该如何处理")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("iota")]),t._v("\n b "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v("\n c "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("iota")]),t._v("\n d\n e\n f\n g\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("答案就是异类输出自己的,其它的常量不受影响,比如这里的输出就是 "),a("code",[t._v("0 12 2 3 4 5 6")]),t._v(",只要记住 iota 是偏移位置就可以理解为什么是这么输出的了。")]),t._v(" "),a("p",[t._v("如果不考虑常量的灵活性,极致追求安全性,那么也可以给 iota 常量加上类型")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n a "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("iota")]),t._v("\n b\n c\n d\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("这种就要求变量类型必须是 int 才能被这些枚举类型接纳,但是一般常量还是用无类型的较为常见。")])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/4.a73c4d70.js b/assets/js/4.a73c4d70.js new file mode 100644 index 000000000..e3725fc52 --- /dev/null +++ b/assets/js/4.a73c4d70.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[4],{400:function(t,e,a){},433:function(t,e,a){"use strict";a(400)},437:function(t,e,a){"use strict";a.r(e);var n={name:"CodeBlock",props:{title:{type:String,required:!0},active:{type:Boolean,default:!1}},mounted:function(){this.$parent&&this.$parent.loadTabs&&this.$parent.loadTabs()}},i=(a(433),a(36)),s=Object(i.a)(n,(function(){var t=this.$createElement;return(this._self._c||t)("div",{staticClass:"theme-code-block",class:{"theme-code-block__active":this.active}},[this._t("default")],2)}),[],!1,null,"759a7d02",null);e.default=s.exports}}]); \ No newline at end of file diff --git a/assets/js/40.c47687be.js b/assets/js/40.c47687be.js new file mode 100644 index 000000000..0c7275221 --- /dev/null +++ b/assets/js/40.c47687be.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[40],{470:function(t,s,a){"use strict";a.r(s);var n=a(36),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"求值顺序"}},[t._v("求值顺序")]),t._v(" "),a("h2",{attrs:{id:"包级变量声明语句中的表达式求值顺序"}},[t._v("包级变量声明语句中的表达式求值顺序")]),t._v(" "),a("ul",[a("li",[t._v("按照变量的声明顺序,"),a("strong",[t._v("从上到下,从左到右")]),t._v(",进行求值")]),t._v(" "),a("li",[t._v("如果 a 求值时有依赖项 b,或者是间接的依赖项 b,那么先求值 b")]),t._v(" "),a("li",[t._v("在求值的过程中会一直对于变量是否拥有依赖项进行查找,直到查询到没有依赖项的变量将其求值,然后重复这个查找,最后全部求值。")]),t._v(" "),a("li",[t._v("同一个包不同文件的处理,原则上如果 a 文件在 b 文件前面,那么 a 文件中的所有变量求值比 b 文件中的更早,除非 a 文件变量依赖了 b 中的变量。")])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" c "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" b\n b "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n c "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n d "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n d "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" \n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" d\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("ol",[a("li",[t._v("查找没有依赖项的变量将其求值,这一轮中是 d,此时只有 [d=3]")]),t._v(" "),a("li",[t._v("查找没有依赖项的变量进行求值,这一轮中 a 还是不符合,但是 b 符合了,所以现在是 [b=4,d=4]")]),t._v(" "),a("li",[t._v("查找没有依赖项的变量进行求值,这一轮中是 _,此时 [b=4,d=5]")]),t._v(" "),a("li",[t._v("查找没有依赖项的变量进行求值,这一轮中是 c,此时 [b=4,c=6,d=6]")]),t._v(" "),a("li",[t._v("查找没有依赖项的变量进行求值,这一轮中是 a,此时 [a=10,b=4,c=6,d=6]")])]),t._v(" "),a("p",[t._v("输出 10 4 6 6")]),t._v(" "),a("h2",{attrs:{id:"普通求值顺序"}},[t._v("普通求值顺序")]),t._v(" "),a("p",[t._v("这包括了,函数,方法,channel 中的求值顺序。")]),t._v(" "),a("ul",[a("li",[t._v("规定是从左到右")])]),t._v(" "),a("p",[t._v("y[f()],ok = g(h(),i()+x[j()], <-c),k()")]),t._v(" "),a("p",[t._v("这个语句的求值顺序就是 f() h() i() j() x[] <-c g() k()")]),t._v(" "),a("p",[t._v("普通值求值顺序和包级变量求值依赖顺序一起使用的时候,包级变量优先级更高,并且它在导入包的时候就已经求值了,而普通的求值顺序只有调用的时候才会求值。")]),t._v(" "),a("h2",{attrs:{id:"赋值语句中的求值顺序"}},[t._v("赋值语句中的求值顺序")]),t._v(" "),a("p",[t._v("赋值语句求值有两个阶段 --- “先算后赋”")]),t._v(" "),a("ul",[a("li",[t._v("对等号左边的下标表达式,指针解引用表达式,以及等号右边的表达式,从左到右的依次求值")]),t._v(" "),a("li",[t._v("按照从左到右的顺序将变量进行赋值")])]),t._v(" "),a("p",[t._v("例如")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("n0 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\nn1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v("\nn0"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("n1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" n0"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v("n1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("n0\n")])])]),a("ol",[a("li",[t._v("左边没有要处理的表达式,右侧有,那么第一个位置就是 n0+n1,因为 n0 和 n1 已经存在了初始化的内容了,所以这里直接就是 3,no = 1,这时 n0 和 n1 还是 1 2,因为还没有赋值呢。")]),t._v(" "),a("li",[t._v("开始赋值:右侧一个是 3 一个是 1,那么最新的 no 和 n1 就是 3 1")])]),t._v(" "),a("h2",{attrs:{id:"switch-select-中的表达式的求值顺序"}},[t._v("switch select 中的表达式的求值顺序")]),t._v(" "),a("p",[t._v("这里主要想说一下惰性求值:就是只有需求的时候才会进行求值")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("n "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("switch")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"--"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("首先先求值的是 switch 后面的 f (2)")]),t._v(" "),a("p",[t._v("然后对 f(1) f(2) 求值;f(3) 不会求值,因为 f (2) 已经满足了要求。")]),t._v(" "),a("p",[t._v("在 select case 中,如果 case 中存在表达式,最开始会依次计算所有的表达式,只有一种除外,收 case 中,位于左侧的表达式,它会在接受数据之前才会计算,然后赋值。")]),t._v(" "),a("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),a("ul",[a("li",[t._v("https://go.dev/ref/spec#Order_of_evaluation")]),t._v(" "),a("li",[t._v("https://book.douban.com/subject/35720728/ 132 页 - 142 页")])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/41.0dc8c7ec.js b/assets/js/41.0dc8c7ec.js new file mode 100644 index 000000000..a02b87a98 --- /dev/null +++ b/assets/js/41.0dc8c7ec.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[41],{472:function(t,s,a){"use strict";a.r(s);var n=a(36),p=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"泛型"}},[t._v("泛型")]),t._v(" "),a("p",[a("strong",[t._v("导读:")])]),t._v(" "),a("ul",[a("li",[t._v("约束")]),t._v(" "),a("li",[t._v("使用方法")]),t._v(" "),a("li",[t._v("实现原理")]),t._v(" "),a("li",[t._v("跟其它语言的泛型进行对比")]),t._v(" "),a("li",[t._v("用例子学泛型")]),t._v(" "),a("li",[t._v("issues")])]),t._v(" "),a("blockquote",[a("p",[t._v("泛型需满足 "),a("code",[t._v("go1.18+")])])]),t._v(" "),a("h2",{attrs:{id:"约束"}},[t._v("约束")]),t._v(" "),a("p",[t._v("go 使用 interface 作为约束,约束的意思是约束了这个泛型都具有哪些实际类型。所以可以理解为,go 将 interface 的职责给扩展了,让接口不仅仅作为接口 --- 解耦的,抽象化的结构体,还具有了约束,对于类型的约束作用。")]),t._v(" "),a("p",[t._v("go 可以将所有的接口 (包括经典接口,和泛型以后的接口) 都用作约束,但是可以不代表应该,要有选择的去使用约束,但是约束并不是都可以作为传统的接口来使用的,例如传统的接口只能存在方法,并不能存在类型,"),a("strong",[t._v("只要存在类型就自动归纳为约束")])]),t._v(" "),a("p",[t._v("综上所述,第一,约束的概念大于接口,只有传统接口可以作为抽象类型去使用,约束只能存在于函数方法或者类型之中,不能单独使用。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a1 a\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("float64")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 不能在类型约束之外使用类型 a:接口包含类型约束")]),t._v("\ncannot use "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("type")]),t._v(" a outside a "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("type")]),t._v(" constraint: interface contains "),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("type")]),t._v(" constraints\n\n")])])]),a("p",[t._v("第二,不要把传统接口用在约束的地方,这种用法是不合适的,通常来说传统接口的模式就用作抽象类型的解耦即可 (用了也不错,只是不建议)。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 不建议")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Writer "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("hi")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" Get"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T Writer"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tv"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("hi")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h3",{attrs:{id:"泛型是类型-约束是类型的约束"}},[t._v("泛型是类型,约束是类型的约束")]),t._v(" "),a("p",[t._v("请认识到,泛型是一种类型,约束是一种类型的约束,请不要把约束当作泛型。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" st "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这里 st 约束拥有 int 和 string,请注意这里的 st 是约束,不是泛型类型")]),t._v(" "),a("p",[t._v("go 内置了很多约束,比如说 any 和 comparable,意思是任何类型和可以比较的类型。以后***应该***会有一个新的内置约束的包叫做 "),a("code",[t._v("package constraints")]),t._v(" 例如 any comparable,Ordered 等等约束都会内置到标准库中")]),t._v(" "),a("p",[t._v("约束不仅仅可以单独写出来,还可以内置于函数内部。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" Age"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" B "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("float64")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("j B"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这种形式下,T 和 B 的约束就是仅限此函数使用")]),t._v(" "),a("p",[t._v("下面我们看一种形式,这种情况下约束的不仅仅是 string 和 int,而是包含了底层是他们的所有数据,比如说 "),a("code",[t._v("type DD int")]),t._v(" 也符合这个约束,请记住只能使用在底层类型上,如果使用 "),a("code",[t._v("~DD")]),t._v(" 是不被允许的")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" st "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h3",{attrs:{id:"约束的嵌套"}},[t._v("约束的嵌套")]),t._v(" "),a("p",[t._v("约束跟接口是一样的也是可以嵌套的")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" ComparableHasher "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tcomparable\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Hash")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uintptr")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// or")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" ImpossibleConstraint "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tcomparable\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这里的意义就是 "),a("strong",[t._v("and")]),t._v(" 的意思就是说这个约束是可以比较的还是必须得支持 "),a("code",[t._v("hash()uintptr")])]),t._v(" "),a("p",[t._v("下面这种方式也是可以的")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" NumericAbs"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int8")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int16")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int32")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int64")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v("\n\t\t~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint8")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint16")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint32")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint64")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uintptr")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v("\n\t\t~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("float32")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("float64")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v("\n\t\t~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("complex64")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("complex128")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Abs")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" T\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("上面的类型意思是满足数字类型,下面的意思是满足这个方法,所以最终实现这个约束的对象就是一个数字类型,并且实现了这个接口的 "),a("code",[t._v("Abs()T")]),t._v(" 方法。")]),t._v(" "),a("h2",{attrs:{id:"泛型-2"}},[t._v("泛型")]),t._v(" "),a("p",[t._v("当结构体中使用泛型的时候,泛型不能直接作为嵌入使用")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Lockable"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tT "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 正确的方法应该是 t T ; 将 T 作为类型参数,不可直接嵌入")]),t._v("\n\tmu sync"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Mutex\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("错误提示:"),a("code",[t._v("embedded field type cannot be a (pointer to a) type parameter")]),t._v(";"),a("code",[t._v("嵌入式字段类型不能是(指向)类型参数")])]),t._v(" "),a("p",[t._v("我们再看一下当泛型结构体嵌入到其它结构体中如何使用")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tT T\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" B"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里使用 A[T] 输入了实际类型,")]),t._v("\n\tA"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n\tT T\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("可以看出关键点,泛型结构体被嵌入其它结构体的时候,泛型要给实际的类型才可以")]),t._v(" "),a("p",[t._v("结构体泛型和方法中的泛型做对比:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a A"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("方法里的泛型是可以直接从 struct 定义的地方继承这个泛型 T 的,当这个结构体使用时,指定实际的类型即可。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" d1 d\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" A"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("d"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\td1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\ta"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 只要没有类型就不是约束,是接口,在go语言中,接口可以充当一个一般类型")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" d "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tT T\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a A"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[a("em",[a("strong",[t._v("约束里的泛型同样不能直接嵌入使用")])])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ❌")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" B"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tT\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("错误提示:"),a("code",[t._v("cannot embed a type parameter")])]),t._v(" "),a("p",[t._v("泛型只能充当类型:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" EmbeddedParameter"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint")]),t._v(" \n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("me")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" T \n")])])]),a("p",[t._v("使用约束中的泛型还是需要注意一下的,稍微有些复杂:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" Abs"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T EmbeddedParameter"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("解释一下,中括号里面泛型的两个 T 表达的意思是不一样的,后面的 T 表达的是"),a("strong",[t._v("约束里的泛型")]),t._v(",表示 any,前面的 T 表示的是满足后面的这个约束的类型 T,但是这里注意,后面 T 虽然之前定义的时候是 any 但是这里被赋予为 T 之后,改变了,改变为了必须满足约束 "),a("code",[t._v("EmbeddedParameter")]),t._v(" 的类型,如果说的通俗点,从 any 变成了,满足 "),a("code",[t._v("int | uint and 实现 me()T方法")]),t._v(" 后文会有代码进行解释。")]),t._v(" "),a("p",[t._v("当然了,后面的 T 没有也行,如果没有后面的 T 就是相当于不改变后面的 T 的约束类型了")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Differ"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1 any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Diff")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("T1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" IsClose"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T2 Differ"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" b T2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Diff")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("当使用了泛型之后,是无法使用断言的,这是非法的,那么如果一定要在运行时的时候去判断类型怎么办呢?答案就是转变成 "),a("code",[t._v("any")]),t._v(" (type any = interface {}) 即可,因为我们知道任何对象都已经实现了空接口,那么就可以被空接口去转化")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" GeneralAbsDifference"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T Numeric"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" b T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" T "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("switch")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("any")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int8")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int16")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int32")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int64")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint8")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint16")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint32")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint64")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uintptr")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("float32")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("float64")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("OrderedAbsDifference")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("complex64")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("complex128")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("ComplexAbsDifference")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("下面看一下别名的真实类型是泛型的情况")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("T\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" AliasA "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" A "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 错误 ❌")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" AliasA "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" A"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 正确")]),t._v("\n")])])]),a("p",[t._v("其中错误的问题是别名不能直接使用泛型类型 "),a("code",[t._v("cannot use generic type A[T any] without instantiation")]),t._v(",它需要泛型实际赋值")]),t._v(" "),a("h2",{attrs:{id:"使用方法"}},[t._v("使用方法")]),t._v(" "),a("p",[t._v("下面展示一下 go 泛型的基本使用方法")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Printf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"%v"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("Age"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" Age"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" t\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这是函数使用泛型的写法,当函数使用泛型的时候,需要在变量前使用中括号标注泛型的具体约束,然后后面才能使用这个泛型类型,使用泛型函数的时候,中括号是可以省略的 "),a("code",[t._v("Age(12)")]),t._v(" 系统会自动推算泛型的具体实现。顺便说一下,泛型类型使用 "),a("code",[t._v("%v")]),t._v(" 作为占位符,也就是默认的类型,泛型类型无法进行断言。")]),t._v(" "),a("p",[t._v("当然了,我么也可以不用 any,自定义一个约束")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tAge"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" st "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" Age"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T st"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("看完了在函数内的泛型,我们在看一下在方法中如何使用泛型")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("new")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Age"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Post")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" dd DD"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n\tdd"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("TT")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Age"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tI T\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Age"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Post")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("I"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" DD"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("T\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("dd "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("DD"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("TT")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("dd"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("在 age 结构体声明的时候,声明了一个泛型 T,在 struct 体内就可以使用这个 T,方法内部仅可以使用定义在这个结构体对象上的泛型")]),t._v(" "),a("p",[t._v("下面是一个"),a("strong",[t._v("错误案例")])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ❌")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Age"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("Post"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("B any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("b B"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("I"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" \n")])])]),a("p",[a("code",[t._v("syntax error: method must have no type parameters")])]),t._v(" "),a("p",[t._v("接下来我们看一下,如何使用有类型也有方法的泛型")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" d DDD\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" i DDD\n\td "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n\ti "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v("\n\tio "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" AbsDifference"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("DDD"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("d"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("io"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" DDD "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ddd DDD"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Abs")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" DDD "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" ddd "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" ddd\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" NumericAbs"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int8")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int16")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int32")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int64")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v("\n\t\t~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint8")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint16")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint32")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint64")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uintptr")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v("\n\t\t~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("float32")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("float64")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v("\n\t\t~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("complex64")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("complex128")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Abs")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" T\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// AbsDifference computes the absolute value of the difference of")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// a and b, where the absolute value is determined by the Abs method.")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" AbsDifference"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T NumericAbs"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" b T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" T "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\td "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v(" b\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" d"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Abs")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"实现原理"}},[t._v("实现原理")]),t._v(" "),a("p",[t._v("泛型的第一种方法是在编译这个泛型时,使用一个字典,里面包含了这个泛型函数的全部类型信息,然后当运行时,使用函数实例化的时候从这个字典中取出信息进行实例化即可,这种方法会导致执行性能下降,一个实例化类型 "),a("code",[t._v("int, x=y")]),t._v(" 可能通过寄存器复制就可以了,但是泛型必须通过内存了 (因为需要字典进行运行时赋值),不过好处就是不浪费空间")]),t._v(" "),a("p",[t._v("还有一种方法就是把这个泛型的所有类型全部提前生成,这种方法也有一个巨大的缺点就是代码量直线上升,如果是一个包的情况下还能根据具体的函数调用去实现该实现的类型,如果是包输出的的情况下,那么就得不得不生成所有的类型。")]),t._v(" "),a("p",[t._v("所以将两者结合在一起或许是最好的选择。")]),t._v(" "),a("p",[t._v("这种方法是这样的,如果类型的内存分配器/垃圾回收器呈现的方式一致的情况下,只给它生成一份代码,然后给它一个字典来区分不同的具体行为,可以最大限度的平衡速度和体积")]),t._v(" "),a("h2",{attrs:{id:"跟其它语言的泛型进行对比"}},[t._v("跟其它语言的泛型进行对比")]),t._v(" "),a("ul",[a("li",[t._v("c 语言:本身不具有泛型,需要程序员去实现一个泛型,实现复杂,但是不增加语言的复杂度 (换言之只增加了程序员的)")]),t._v(" "),a("li",[t._v("c++和 rust:跟 go 基本保持一种方式,就是增加编译器的工作量")]),t._v(" "),a("li",[t._v("Java:将泛型装箱为 object,在装箱和拆箱擦除类型的过程中,程序执行效率会变低")])]),t._v(" "),a("h3",{attrs:{id:"为什么-java-编译器不在编译期就完成泛型的装箱操作呢"}},[t._v("为什么 Java 编译器不在编译期就完成泛型的装箱操作呢?")]),t._v(" "),a("p",[t._v("主要有以下几个原因:")]),t._v(" "),a("ol",[a("li",[a("p",[t._v("编译期装箱需要编译器对代码细节有完整信息。但是泛型类型信息可能来自其他模块或第三方库,编译器难以完整获取。")])]),t._v(" "),a("li",[a("p",[t._v("即使编译器可以提前装箱,也需要代码里有大量反射样板代码来还原原始泛型类型,违背泛型设计初衷。")])]),t._v(" "),a("li",[a("p",[t._v("编译期装箱也无法很好处理运行期检查和转换的各种边界情况。")])])]),t._v(" "),a("h2",{attrs:{id:"用例子学泛型"}},[t._v("用例子学泛型")]),t._v(" "),a("p",[t._v("理论学习完了,不使用例子进行复习的话会忘的很快的。跟着我看几个例子吧")]),t._v(" "),a("h3",{attrs:{id:"函数泛型-map-filter-reduce"}},[t._v("函数泛型 "),a("code",[t._v("map-filter-reduce")])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tvM "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" Map"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" i\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Printf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"map的结果是:%v"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" vM"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tvF "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" Filter"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" t "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Printf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"filter的结果是:%v"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" vF"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tvR "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" Reduce"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("Value"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Result"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("Value"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"tt"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" year"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"bb"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" year"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"7i"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" year"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"8i"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" year"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"u4i"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" year"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"uei"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" year"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("6")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"uwi"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" year"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("7")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"uti"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" year"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("8")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Result"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("r "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Result"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v Value"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Result "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("value "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" r"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("value "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("year\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" r\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"reduce的结果是:"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" vR"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("value"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Map:类似于洗菜,进去的菜和出来的菜不一样了所以需要两种种类")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" Map"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" T2 any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("arr "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("T1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" f "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("T1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" T2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("T2 "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tresult "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("T2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("arr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" k"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" arr "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tresult"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("k"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" result\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Filter:类似于摘菜,进去的菜和出来的菜是一种,不过量减少了")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" Filter"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("arr "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" f "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("T "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" result "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("T\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" arr "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\tresult "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" result\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Reduce:类似于做菜,将菜做成一道料理,所以需要两种类型")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" Reduce"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" T2 any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("arr "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("T1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" zero T2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" f "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("T2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" T1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" T2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" T2 "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tresult "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" zero\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" arr "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tresult "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" result\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Value "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tname "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\tyear "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Result "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tvalue "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[a("code",[t._v("map的结果是:[2 4 6 8 10] filter的结果是:[3 4 5] reduce的结果是: 36")])]),t._v(" "),a("h3",{attrs:{id:"例子二-方法上的泛型-sets"}},[t._v("例子二:方法上的泛型 "),a("code",[t._v("sets")])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里 Sets的具体类型和Make的具体类型都是int,所以可以正常赋值")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s Sets"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" Make"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//")]),t._v("\n\ts"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ts"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Contains")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ts"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Iterate")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ts"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Delete")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Sets 一个key 存储对象")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Sets"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T comparable"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Make 实例化一个map")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" Make"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("D comparable"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" Sets"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("D"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 泛型就像一个管道一样,只要实例化的时候管子里的东西一致,那么就是一根管子")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Sets"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("D"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Add 向这个sets添加内容")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s Sets"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ts"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// delete ,从这个sets中删除内容")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s Sets"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Delete")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("delete")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Contains 播报t是否属于这个sets")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s Sets"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Contains")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ok "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" ok\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//Len sets拥有的长度")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s Sets"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Iterate 迭代器,并且给予每个元素功能")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s Sets"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Iterate")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" k "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" s "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("k"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[a("code",[t._v("map[1:{} 2:{}] false 2 1 2 map[1:{} 2:{}] map[1:{}]")])]),t._v(" "),a("h3",{attrs:{id:"例子三-外部定义的约束-实现一个sort接口类型"}},[t._v("例子三:外部定义的约束 "),a("code",[t._v("实现一个sort接口类型")])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Hello, 世界"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ~ 代表只要底层满足这些类型也可以算满足约束")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Ordered "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int8")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int16")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int32")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int64")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uintptr")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v("\n\t\t~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint8")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint16")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint32")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint64")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v("\n\t\t~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("float32")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("float64")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" orderedSlice"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T Ordered"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("T\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s orderedSlice"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s orderedSlice"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Less")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" j "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("j"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s orderedSlice"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Swap")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" j "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("j"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("j"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" OrderedSlice"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T Ordered"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tsort"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sort")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("orderedSlice"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"issues"}},[t._v("issues")]),t._v(" "),a("h3",{attrs:{id:"关于泛型中的零值"}},[t._v("关于泛型中的零值")]),t._v(" "),a("p",[t._v("在 go 里面对泛型的零值并没有一个所谓的泛型零值可以使用,需要根据不同的实践去实现,比如")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Aget"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tt "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("T\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 根据实际判断,如果a的t不等于nil再返回,如果是nil就返回一个T类型的nil(意思就是只声明)")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Aget"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Approach")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" T "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("t "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" \n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("t\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" r T\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" r\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("实际上目前,还没一个确切的泛型的零值,那么我们要做的只能是按照实际来具体分析,按照提案,以后有可能使用 "),a("code",[t._v("return ...")]),t._v(" "),a("code",[t._v("return _")]),t._v(" "),a("code",[t._v("return")]),t._v(" "),a("code",[t._v("return nil")]),t._v(" "),a("code",[t._v("return T{}")]),t._v(" 这些都是可能的结果,我个人比较喜欢 "),a("code",[t._v("return T{}")]),t._v(" 来表示泛型的零值,拭目以待吧。")]),t._v(" "),a("h3",{attrs:{id:"无法识别使用了底层数据的其它类型"}},[t._v("无法识别使用了底层数据的其它类型")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Float "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("float32")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v(" ~"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("float64")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" NewtonSqrt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T Float"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v T"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" T "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" iterations "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("switch")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("float32")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\titerations "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("float64")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\titerations "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("default")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("panic")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sprintf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"unexpected type %T"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Code omitted.")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" MyFloat "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("float32")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" G "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewtonSqrt")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("MyFloat")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("64")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("这里约束 Float 拥有的约束类型是 "),a("code",[t._v("~float32")]),t._v(" 和 "),a("code",[t._v("float64")]),t._v(" 当在 switch 中定义了 float32 和 flaot64 时,无法识别下面的新类型 MyFloat 即使它的底层时 float32,go 的提议是以后在 switch 中使用 "),a("code",[t._v("case ~float32:")]),t._v(" 来解决这个问题,目前尚未解决这个问题")]),t._v(" "),a("h3",{attrs:{id:"即便约束一致-类型也是不同的"}},[t._v("即便约束一致,类型也是不同的")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" Copy"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("T1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" T2 any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("dst "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("T1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" src "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("T2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" x "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" src "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("dst"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" i\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\tdst"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("T1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// x 是 T2类型 不能直接转化为 T1类型")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("src"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("T1,和 T2 虽然都是 any 的约束,但是啊,它不是一个类型啊!")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("Copy"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这种情况下,你能说可以直接转化吗???")]),t._v("\n")])])]),a("p",[t._v("这种代码可以更改一下")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("dst"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("T1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("确认是一种类型以后才能转化")]),t._v(" "),a("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),a("ul",[a("li",[t._v("https://coolshell.cn/articles/21615.html")]),t._v(" "),a("li",[t._v("https://go.dev/doc/tutorial/generics")]),t._v(" "),a("li",[t._v("https://colobu.com/2021/08/30/how-is-go-generic-implemented/")]),t._v(" "),a("li",[t._v("https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md")]),t._v(" "),a("li",[t._v("https://quii.gitbook.io/learn-go-with-tests/go-fundamentals/generics")]),t._v(" "),a("li",[t._v("https://mp.weixin.qq.com/s/zKnh_iPm8skxWv3rxaOscw")]),t._v(" "),a("li",[t._v("https://mp.weixin.qq.com/s/BFsoQPvrog_sMKMTEofZyQ")])])])}),[],!1,null,null,null);s.default=p.exports}}]); \ No newline at end of file diff --git a/assets/js/42.d4dc564f.js b/assets/js/42.d4dc564f.js new file mode 100644 index 000000000..d08707d88 --- /dev/null +++ b/assets/js/42.d4dc564f.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[42],{471:function(t,s,n){"use strict";n.r(s);var a=n(36),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"结构体"}},[t._v("结构体")]),t._v(" "),n("h2",{attrs:{id:"简单介绍-struct"}},[t._v("简单介绍 struct")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" People "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Addr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n name "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n year "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("ul",[n("li",[t._v("Peopel 首字母大写,根据我们所学的首字母大写可导出的知识,它是包级可导出结构")]),t._v(" "),n("li",[t._v("Addr 首字母大写,所以它是可导出字段,name 和 year 都是小写,所以他们俩不可导出")])]),t._v(" "),n("p",[t._v("下面看一下结构体的使用")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 初始化")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" p People\n p"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Addr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"北京"')]),t._v("\n p"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"小明"')]),t._v("\n p"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("year "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2000")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" p1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" People"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Addr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"北京"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"小明"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n year"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" p2 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("People"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Addr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"北京"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"小明"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n year"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" People "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Addr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n name "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n year "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 结构体的调用")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" p "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("People"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Addr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"北京"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"小明"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n year"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2000")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里是语法糖,p虽然是people的指针,")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 但是它却可以直接调用 Addr")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 实际上就是 (*p).Addr 的省略")]),t._v("\n p"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Addr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"上海"')]),t._v("\n p"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"小红"')]),t._v("\n p"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("year "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2001")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("p"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Addr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" p"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" p"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("year"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 我们还可以使用new来代替&")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这种写法跟上文中的 var p = &People{} ")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 一个意思")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" p1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("new")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("People"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("p1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" People "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Addr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n name "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n year "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h2",{attrs:{id:"解耦结构体声明和调用"}},[t._v("解耦结构体声明和调用")]),t._v(" "),n("p",[t._v("当我们实现结构体的时候,如果显示写出结构体的字段变量名称,就可以不按照顺序,以及可以不完全实现全部字段,这样的话,结构体的声明和实现就可以完全解耦,当然可以隐藏实现的结构体变量,那么你不得不要按照顺序,以及实现全部字段,满足这两者才可以。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 显式实现")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" People "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Addr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n name "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n year "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" p "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" People"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Addr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"北京"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n year"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2000")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" a\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n \n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("上述代码就是显示的写出了字段的变量名称,你看,name 并没有被写上,这种情况下,name 就会被命名为一个初始值,即 “”")]),t._v(" "),n("p",[t._v("这样,即使结构体本身有什么增加字段的行为,实现结构体的逻辑代码也不用改变了。")]),t._v(" "),n("p",[t._v("如果是隐式的话,那么必须按照顺序,以及数量进行实现,建议在字段不变以及字段数量非常少的时候使用。")]),t._v(" "),n("h2",{attrs:{id:"匿名-struct"}},[t._v("匿名 struct")]),t._v(" "),n("p",[t._v("我们使用匿名 struct,可以完全将另一个结构体嵌入到这个结构体中。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Student "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n People\n score "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" People "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n year "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n addr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("使用匿名函数,等于将这个匿名函数的字段完全给予了这个新的结构体,这跟使用这个结构体当作一个字段的类型是不同的,我们看一个例子")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Student "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n People People\n score "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" People "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n year "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n addr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("这段代码跟上文看起来很像,但是一个是将 people 直接嵌入,一个是当成了它的一个字段类型。让我们看一下这两者使用起来的区别")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Student1 "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n People People\n score "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Student "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n People\n score "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" People "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n year "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n addr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("new")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Student"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s1 "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("new")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Student1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"小明"')]),t._v("\n s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("year "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"2000"')]),t._v("\n s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("addr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"北京"')]),t._v("\n s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("score "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("100")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("year"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("addr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("score"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n s1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("people"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"小红"')]),t._v("\n s1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("people"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("year "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"2001"')]),t._v("\n s1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("people"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("addr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"上海"')]),t._v("\n s1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("score "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("200")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("people"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("people"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("year"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("people"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("addr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("score"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("我们发现匿名函数的字段直接赋予了新的结构体,它不再需要显式的调用 "),n("code",[t._v("s1.People.name")])]),t._v(" "),n("p",[t._v("实际上,不止一个 struct 可以当作匿名函数,内置类型也是可以的")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Student "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n People\n "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n score "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" People "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n year "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n addr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("new")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Student"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("People "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" People"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"小明"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n year"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"2000"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n addr"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"北京"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v("\n s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"12"')]),t._v("\n s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("score "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("100")]),t._v("\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("函数也可以将指针类型当作类型以及直接嵌入,与非指针相比,我们不能直接调用嵌入的结构体的字段,因为它目前还是 nil,这个时候我们必须先将结构体初始化,然后再进行操作,下文的代码有演示。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Student "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Name "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Name\n "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("People\n "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n score "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Name "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fist "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\n last "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" People "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n year "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n addr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("new")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Student"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Name "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("new")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("People "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("new")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("People"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("first "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"小明"')]),t._v("\n s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("last "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"小红"')]),t._v("\n s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"小明"')]),t._v("\n s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("year "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"2000"')]),t._v("\n s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("addr "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"北京"')]),t._v("\n s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v("\n s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"12"')]),t._v("\n s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("score "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("100")]),t._v("\n\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("p",[t._v("通过这个演示我们发现,其实嵌入更像是一种语法糖,它就跟将结构体当作类型,前面的结构体字段变量跟这个类型保持一致这种操作是同样的作用,只不过直接嵌入可以将嵌入的字段当作自己的字段,省略了中间的变量罢了,它有下面的等于关系")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Student "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//People *People")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 等于 ")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// *People")]),t._v("\n\n\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// People People")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 等于 ")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// People")]),t._v("\n \n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" People "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n year "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n addr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("当一个结构体拥有了其它嵌入的时,也一起拥有了在他们身上的方法,不过,非嵌入的那种,还是需要带上中间的字段变量名称比如:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Student "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tName Name\n\t"),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("People\n\t"),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\tscore "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Name "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tfirst "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\tlast "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" People "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tname "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\tyear "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\taddr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("S")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"stuend-s"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 如果这里不是定义在指针上的方法,这段代码将报错")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 原因是 people嵌入的时候还是nil,如果这里不是指针上的方法,是值上的方法")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 底层是需要 *People (取值) 操作的,但是这时候是nil,所以就会报错。")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("People"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("SetS")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"people-setS"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("new")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Student"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里注意,因为不是嵌入的操作,所以它必须带上中间的 Name")]),t._v("\n\ts"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Name"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("S")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ts"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("SetS")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("h2",{attrs:{id:"空结构体"}},[t._v("空结构体")]),t._v(" "),n("p",[t._v("通常,当我们需要一个临时的变量时,我们可以会想到设置一个 bool 类型,因为我们潜意识中感觉一个 bool 类型是比较小的,但是一个空的变量才是最小的,下面让我们看一个例子,这个例子发生在使用 channel 传递信息这个场景。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n sig "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n sig "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" sig\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"任务已经完成"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("没错,这段代码中,使用一个 bool 类型的 channel 是没什么问题的,你甚至也可以使用 int string 都可以,因为只是传递一个信息,信息的内容不重要,但是当我们站在优化的角度来考虑,这里的 bool 就不完美了,我们改成空的结构体即可:")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n sig "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("time"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n sig "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" sig\n fmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"任务已经完成"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("需要注意的是,一个空的结构体,表示它类型的方式是 "),n("code",[t._v("struct{}")]),t._v(",而使用这个空结构体的方式就是 "),n("code",[t._v("struct{}{}")]),t._v(",前面的大括号是跟 struct 一起的整体表示空结构体,后面的大括号表示一个空结构体类型的结构体调用")]),t._v(" "),n("h2",{attrs:{id:"直接嵌套还是作为字段"}},[t._v("直接嵌套还是作为字段")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Pool "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n wg sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("WaitGroup\n JobQueue "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" Job\n dispatcher "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("dispatcher\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// or")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Pool "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n sync"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("WaitGroup\n \n JobQueue "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" Job\n dispatcher "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("dispatcher\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("p",[t._v("结构体嵌套可能带来的问题:")]),t._v(" "),n("ul",[n("li",[n("p",[t._v("名称冲突\n如果 Pool 结构体中还定义了 Add/Done/Wait 方法,和嵌套的 WaitGroup 中的方法就会产生冲突。")])]),t._v(" "),n("li",[n("p",[t._v("不必要的方法\n嵌套整个 WaitGroup 会让 Pool 结构体拥有 Add/Done/Wait 等方法,但 Pool 可能只需要 Wait 就够了。")])]),t._v(" "),n("li",[n("p",[t._v("使结构体臃肿\n嵌套整个 WaitGroup 会让 Pool 结构体看起来很臃肿,包含许多其实用不到的方法。")])]),t._v(" "),n("li",[n("p",[t._v("继承关系不清晰\nPool 并不是 WaitGroup 的一种,嵌套整个 WaitGroup 让人可能误以为它是在继承 WaitGroup。")])])]),t._v(" "),n("p",[t._v("所以,作为一个字段,可以避免上述问题,又可以保持必要的功能。")]),t._v(" "),n("p",[t._v("总结一下,嵌套要慎用,只有当:")]),t._v(" "),n("ul",[n("li",[t._v("被嵌套的类型方法不会与现有方法冲突")]),t._v(" "),n("li",[t._v("被嵌套的所有方法都会被用到")]),t._v(" "),n("li",[t._v("两者之间有逻辑上的继承关系")])]),t._v(" "),n("p",[t._v("时,才更适合使用嵌套 (组合) 的方式。")]),t._v(" "),n("p",[t._v("否则,使用字段的方式可以获得必要的功能,而不引入嵌套的潜在问题。")])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/43.ceafd4d8.js b/assets/js/43.ceafd4d8.js new file mode 100644 index 000000000..4a0c34103 --- /dev/null +++ b/assets/js/43.ceafd4d8.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[43],{473:function(t,s,a){"use strict";a.r(s);var n=a(36),r=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"逻辑和判断语句"}},[t._v("逻辑和判断语句")]),t._v(" "),a("p",[t._v("go 语言基本上继承了 c 语言的逻辑判断语句,但是仍有些不同,比如:")]),t._v(" "),a("ul",[a("li",[t._v("在循环语句中,仅保留了 "),a("code",[t._v("for")]),t._v(" 语句,do-while,while 均不支持,这也是满足了 go 小而美的原则 -- 单一功能仅保留一种方式。")]),t._v(" "),a("li",[t._v("break continue 后面添加 label 功能")]),t._v(" "),a("li",[t._v("switch 中 case 语句不会自动执行下一个 case,需要显示的使用 "),a("code",[t._v("fallthrough")]),t._v(",当然了在 select 中的 case,并没有 fallthrough 功能")]),t._v(" "),a("li",[t._v("switch 中 case 支持表达式列表")]),t._v(" "),a("li",[t._v("switch 增加 type 模式,让类型也可以作为选择的条件")]),t._v(" "),a("li",[t._v("跟 c 语言最大的不同,增加了 select - case 功能。")])]),t._v(" "),a("h2",{attrs:{id:"if"}},[t._v("if")]),t._v(" "),a("p",[t._v("go 语言中的控制语句基础语法:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" conditon "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" condition"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n \n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 比如:")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"a > 0"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"a > 0 并且 < 12"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"a > 12 或者是非正数"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("go 语言推崇的控制语句是 “快乐路径”:")]),t._v(" "),a("ul",[a("li",[t._v("当出现错误时,直接返回")]),t._v(" "),a("li",[t._v("成功的逻辑不要放在 if 语句中")]),t._v(" "),a("li",[t._v("返回值通常在此函数的最后一行")])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" value "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("handle")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" value\n")])])]),a("h2",{attrs:{id:"for"}},[t._v("for")]),t._v(" "),a("p",[t._v("for 循环的基础语言和 c 类似,for range 语句中,range 后面接数组,指向数组的指针,切片,字符串,和拥有读权限的 channel")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" k"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" sliceValue"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("其中普通循环语句中,i 值属于整个 loop 所有,这一点需要好好注意,我们在作用域那一章节中也提到过。")]),t._v(" "),a("p",[t._v("for-range 语句中,有两点需要注意,首先跟普通循环一样,变量属于整个 loop 所有。")]),t._v(" "),a("p",[t._v("其次,k,v 变量是切片或者 map 的 index 和 value 复制体,当遇到比如 slice 更改数据的时候,切勿直接更改 k v,可使用 "),a("code",[t._v("arr[k] = v+1")]),t._v(" 这种方式直接改变切片本身,另外,当使用 for-range 语句时,如果第二个变量没有使用的价值,可以不写,并且无需使用占位符 "),a("code",[t._v("_")]),t._v(":")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" k "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" sliceValue"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 如果是省略第一个,那么还是需要占位符的")]),t._v("\n\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" sliceValue"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("切片在运行时,len 是会变化的,因为确定切片的 len 是 runtime 的责任,而数组的 len 是在编译期确定的,for - range 后面的数组或者切片,真正处理的其实是这个数据的复制,下面看一个 bug")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("\n\ta "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" number "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tnumber"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\ta "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("6")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("7")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("number"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 5")]),t._v("\n")])])]),a("p",[t._v("这里 number 输出的是 5,因为我们 append 添加的长度是原切片的长度,但是循环体中存储的 len,还是最初的长度 5,所以只能循环 5 次。")]),t._v(" "),a("p",[t._v("for-range 中还有一个容易出 bug 的事情,比如 range 后面跟一个数组,因为 range 的时候实际上是数组的复制品,看下面这段代码:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" r "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\ta"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v("\n\t\t\ta"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("13")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\tr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" v\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("r"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("本来期望的 r 值是 1 12 13 4 5,因为在 i == 0 时就更改了数组 a 的值,但是最终输出的却是 1 2 3 4 5,原因很简单,因为 v 读取的是 a 这个数组的复制品,也就是说,实际上这个代码的隐藏含义是 "),a("code",[t._v("range a'")]),t._v(" 这里的 "),a("code",[t._v("a'")]),t._v(" 就是 a 的复制品,所以更改了 a,a 的复制品也不会被改变,解决方法就是取这个数组的切片就可以了,这样即便是复制了,也只是复制了一份儿指针而已。")]),t._v(" "),a("h3",{attrs:{id:"string"}},[t._v("string")]),t._v(" "),a("p",[t._v("如果 for-range 中后面接的是 string,每次迭代不是按照 byte 来进行的,而是按照 rune 来进行,比如 "),a("code",[t._v('"你好"')]),t._v(" 每次的迭代输出的就是你和好,而不是 byte")]),t._v(" "),a("h3",{attrs:{id:"map"}},[t._v("map")]),t._v(" "),a("p",[t._v("for range 后面是 map 时,无法保证 map 的输出顺序,但是 map 和 slice 一样都是胖指针,所以时可以对 map 进行直接操作的。如果在循环体中新创建一个 map 项,那么这个项目在 range 时有可能会被输出,不能保证一定。")]),t._v(" "),a("h3",{attrs:{id:"channel"}},[t._v("channel")]),t._v(" "),a("p",[t._v("channel 的本质也是一个胖指针,所以它也可以直接被操作本体,channel 在 range 时是阻塞式的读取,如果不关闭 channel,这个 range 会一直阻塞。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" c "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" c "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这段代码就会造成一直阻塞,进而触发系统的 Panic")]),t._v(" "),a("h2",{attrs:{id:"switch-select"}},[t._v("switch && select")]),t._v(" "),a("p",[t._v("在 go 语言的 switch 和 select 的 case 中,我们经常会使用 break 来跳出循环,默认跳出的就是最小的那个循环单位,比如下面这个例子")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("a")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" time"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("After")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("time"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这段代码是有 bug 的,因为它无法跳出 for 这层循环,只能跳出 select 这里。那么我们该怎么做呢?这个时候应该使用 label 了,所谓 label 就是标签的意思,意思就是指定好了 break 的位置,它的作用就是这个。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("a")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Now"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v(" time"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("After")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("time"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),t._v(" Now\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这个时候就可以 break 到 Now 指定的层级了。")]),t._v(" "),a("p",[t._v("我们应避免使用 fallthrough 来执行多条件表达式,比如这样的代码")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("switch")]),t._v(" v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("fallthrough")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int8")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("fallthrough")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"yes"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这种代码也很烂,我们可以直接使用多个 case 并列的方式:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int16")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("a")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("a")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("switch")]),t._v(" v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int8")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"yes"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("default")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"no"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这段代码使用的就是判断 type 的断言模式,固定用法就是 "),a("code",[t._v("value.(type)")]),t._v(" 前面是要判断的 value,后面是固定用法 type,必须是这个单词才行。")]),t._v(" "),a("p",[t._v("我再带你回忆一下普通的断言,"),a("code",[t._v("a.(int)")]),t._v(" 除了在 switch 中的断言方式,普通的断言就跟这段代码是一致的,一个 any 类型 (interface {}) 后面跟具体的类型。")]),t._v(" "),a("p",[t._v("经过上面的初步介绍,接下来,我们深入看一下 for range 的一些底层原理")]),t._v(" "),a("h2",{attrs:{id:"汇总一下-for-和-for-range-中最容易迷惑的几段代码"}},[t._v("汇总一下 for 和 for range 中最容易迷惑的几段代码")]),t._v(" "),a("p",[t._v("第一段代码:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n arr "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" arr "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n arr "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("arr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("arr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这段代码 for 循环不会一直循环,原因是,arr 会在 range 一个复制一份儿,这个复制体的 len 在最初的 range 中的开头已经确定是 3,后面继续追加的 arr,并不会改变这个最初读取的 "),a("code",[t._v("len == 3")]),t._v(" 这个结果。")]),t._v(" "),a("p",[t._v("不过,如果你使用的是传统的循环,那么这种写法就会出现 bug:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tn "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tn"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n\t\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"i"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("使用这种传统的 for 循环,因为 n 在循环体和循环内部都是同一个,所以循环不会结束")]),t._v(" "),a("p",[t._v("因此你应该将这种代码改写为 for - range 模式:")]),t._v(" "),a("blockquote",[a("p",[t._v("go 1.22 增加了对于整数的 for range,之前只有 chan slice map,不过整数的 for range 前面只有一个变量,并且跟其他 for range 一致,n 为复制值,并且 for i := range n,i 也是复制值。这跟其他的 for range 保持一致")])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tn "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tn"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n\t\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"i"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("第二段代码:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("arr "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\nresult "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" arr "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\nresult "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这段代码是存在 bug 的,&v,首先,根据作用域可知道,这个 v 是 loop 级作用域,那么这个 result 中存在的&v 就是同一个值,所以这个代码是错误的。")]),t._v(" "),a("p",[t._v("改正的方式就是放入正确的切片中数据的指针即可:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("arr "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\nresult "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" k "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" arr "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\nresult "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("result"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("arr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("k"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("第三段代码:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" result "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n result"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这是一段对 int 切片进行归零的方法,很多人会觉得这要循环一次会非常浪费时间,其实不会,因为在编译器中,它不会真的循环,编译器会优化这个操作,直接给它内存清零。")])])}),[],!1,null,null,null);s.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/44.5dc33e34.js b/assets/js/44.5dc33e34.js new file mode 100644 index 000000000..a1feeb92f --- /dev/null +++ b/assets/js/44.5dc33e34.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[44],{474:function(t,s,a){"use strict";a.r(s);var n=a(36),r=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"错误处理"}},[t._v("错误处理")]),t._v(" "),a("h2",{attrs:{id:"错误处理的基本认识"}},[t._v("错误处理的基本认识")]),t._v(" "),a("p",[t._v("在 go 语言中,没有传统编程语言的 "),a("code",[t._v("try - catch")]),t._v(" 操作,go 语言中一切错误都需要显式处理:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("readFile")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"./x"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("通常,我们规定函数返回的最后一个数据是错误接口:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"错误x"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("我们直接返回一个 err 是一种简单的做法,如果错误比较初级也可以这么做,但是如果想要带有更精确的提示信息,可以在返回的时候 wrap 一层信息:")]),t._v(" "),a("p",[t._v("就以上文的读取数据为例")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("readFile")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"./"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"在读取数据的时候发生了错误,错误信息是:%w"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("wrap 一层信息,对于错误的定位更加高效")]),t._v(" "),a("h2",{attrs:{id:"error-的本质是什么"}},[t._v("Error 的本质是什么?")]),t._v(" "),a("p",[t._v("错误处理的核心就是下面这一个 error 的接口")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("所以只要我们的自建类型实现了这个接口,就可以使用很多的错误处理的方法。")]),t._v(" "),a("h3",{attrs:{id:"自定义-error"}},[t._v("自定义 error")]),t._v(" "),a("p",[t._v("我们使用 errors.New() 的时候其实就是返回了一个 go 自建的,叫做 errorString 的实现了 error 接口的结构体。\n这就是自建 error 的方法")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\te "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" errors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("New")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"a"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// (0x47fd48,0xc00003e730)")]),t._v("\n")])])]),a("p",[t._v("为了防止在比较错误的时候发生错误一致的情况,所以自建 error,返回的实际上是一个指针。")]),t._v(" "),a("blockquote",[a("p",[t._v("下文会提用什么方法进行比较 err,实际上就是 “两个接口类型是否相等 --- 类型一致,值一致”,如果返回的值是指针,那么值肯定就不可能一样了。")])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// go 源代码")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("New")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("errorString"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("当我们使用 fmt.Errorf() 的时候,其实也是使用的上述方法。")]),t._v(" "),a("p",[t._v("不过,如果我们使用了占位符 "),a("code",[t._v("%w")]),t._v(" 时,将不会使用上述方法,而是使用了 wrapError:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" wrapError "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tmsg "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\terr "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("wrapError"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" e"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("msg\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("wrapError"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unwrap")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" e"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("err\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("使用这种方式主要是为了错误链,那就让我们看一下如何使用错误链的相关操作。")]),t._v(" "),a("h3",{attrs:{id:"errors-is"}},[t._v("errors.Is()")]),t._v(" "),a("p",[t._v("上文我们说到,错误可以使用 wrap 的方式进行封装,那么如果我们想判断封装的这么多层的错误中,有没有哪一层错误等于我们要的值,可以使用这个函数进行判断:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\terr "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" errors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("New")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"err is now"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\terr1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"err1:%w"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\terr2 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"err1:%w"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" errors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Is")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"是一个错误"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h3",{attrs:{id:"errors-as"}},[t._v("errors.As()")]),t._v(" "),a("p",[t._v("这个方法跟上文的 Is() 类似,只不过它判断的是类型是否一致。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" errS "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ta "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("errS"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("a\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\nerr "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("errS"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"typical error"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\nerr1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"wrap err: %w"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nerr2 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"wrap err1: %w"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" e "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("errS\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里的 target 必须使用指针")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("errors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("As")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("e"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("panic")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"TypicalErr is not on the chain of err2"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"TypicalErr is on the chain of err2"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" e"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("h3",{attrs:{id:"errors-join"}},[t._v("errors.Join()")]),t._v(" "),a("p",[t._v("这个方法是将几个错误结合在一起的方法:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("\terr1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"err1:%w"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\terr2 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"err1:%w"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\terr "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" errors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Join")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("h2",{attrs:{id:"当错误处理遇到了-defer"}},[t._v("当错误处理遇到了 defer")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" xx "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("close")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这段伪代码的意思是说,当有条件之后,返回一个错误,但是 defer 的内容发生在这个 err 被固定之后,所以 defer 中如果再有错误将不会被处理。")]),t._v(" "),a("p",[t._v("那么我们该怎么更改呢?")]),t._v(" "),a("p",[t._v("我想你一定想到了前文我们说过,使用带有变量的返回值是可以将 defer 的值进行返回的:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" e "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" xx "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\te "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" xx\n\t\t\ti "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" xx\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" \n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("close")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("那么这种写法,defer 中如果发生了错误就会覆盖掉了程序执行中的 err,所以这种方法也是不行的,即使它能照顾到了 defer 中的错误处理。")]),t._v(" "),a("p",[t._v("我们可以将错误处理都放在 defer 中处理就可以了")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" e "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" xx "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\ti "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" xx\n\t\te "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" xx\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" \n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\te1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" e "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" e1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n\t\t\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" \n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\terr "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" e1\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这样,两种错误都能处理到了")]),t._v(" "),a("h2",{attrs:{id:"错误处理实战的五种方式"}},[t._v("错误处理实战的五种方式")]),t._v(" "),a("h3",{attrs:{id:"经典的错误处理方式"}},[t._v("经典的错误处理方式")]),t._v(" "),a("p",[t._v("每一个步骤分别直接处理错误")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" age "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getAge")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("putAge")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("allAge")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("D")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ag age"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" ag"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getAge")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"%w"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" ag"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("putAge")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"%w"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" ag"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("allAge")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"%w"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h3",{attrs:{id:"屏蔽过程中的错误处理"}},[t._v("屏蔽过程中的错误处理")]),t._v(" "),a("p",[t._v("将错误放置在对象的内部进行处理:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" FileCopier "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tw "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("File\n\tr "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("File\n\terr "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("FileCopier"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("open")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("File"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("err\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n\th"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Open")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tf"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" err\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" h"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("FileCopier"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("OpenSrc")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tf"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("r"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Open")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" \n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("FileCopier"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("CopyFile")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("src"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" dst "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("err\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("r "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\tf"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("r"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Close")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("w"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\tf"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("w"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Close")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("w "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\tos"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Remove")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("dst"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\n\tf"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("opensrc")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("src"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tf"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("createDst")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("dst"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("err\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这段代码并不是特别完整,但是从中我们还是可以理解这种将错误放在对象中的写法的技巧。")]),t._v(" "),a("p",[t._v("首先,错误直接放置在对象自身,在方法中首先去调用这个字段来看是否拥有错误,如果有,直接退出即可")]),t._v(" "),a("p",[t._v("如果没有错误继续往下走,如果本次方法发生错误就继续将这个错误赋值给这个字段,")]),t._v(" "),a("p",[t._v("当最后处理的方法时,这里也就是 copyfile 方法,我们在 defer 中要对于各个子方法进行判断,到底是哪个方法有错误,然后逐一进行判定。相当于处理错误的逻辑集中放置到了最后一个函数进行执行了。")]),t._v(" "),a("p",[t._v("也就是说,将错误放置在对象本身的时候,通常应该为顺序调用的方法,一旦前者出现错误,后者即可退出")]),t._v(" "),a("p",[t._v("如果不是顺序的执行过程,那么有些的错误就可能被湮没,导致错误无法被感知。")]),t._v(" "),a("h3",{attrs:{id:"分层架构中的错误处理方法"}},[t._v("分层架构中的错误处理方法")]),t._v(" "),a("p",[t._v("常见的分层架构")]),t._v(" "),a("ul",[a("li",[t._v("controller 控制层")]),t._v(" "),a("li",[t._v("service 服务层")]),t._v(" "),a("li",[t._v("dao 数据访问层")])]),t._v(" "),a("p",[t._v("dao 层生产错误")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"%w"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("service 追加错误")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Dao"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getName")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"getname err: %w"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("controller 打印错误")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h3",{attrs:{id:"pkg-errors"}},[t._v("pkg/errors")]),t._v(" "),a("p",[t._v("如果感觉标准库提供的错误处理不够丰富,也可以使用 github.com/pkg/errors 来处理错误")]),t._v(" "),a("p",[t._v("此包常用的方法有")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 生成新的错误,同样会附加堆栈信息")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("New")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 只附加新的消息")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithMessage")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("message "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 只附加堆栈信息")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithStack")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 附加信息 + 堆栈信息(就是一大堆的各种文件的堆栈调用过程的详细信息)")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Wrapf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("format "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("args"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 获取最根本的错误(错误链的最底层)")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Cause")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n")])])]),a("p",[t._v("例如:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\terr "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// %+v 是 pkg/errors 包提供的格式化输出格式,输出错误堆栈")]),t._v("\n\t\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Printf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"%+v"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" errors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Wrap")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"open error"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("在使用这个 pkg/errors 包的时候要注意一件事,因为 wrap 可以包装堆栈向上输出,如果你调用的第三方库使用了 wrap,你再次使用 wrap,那么就会出现两堆相同的堆栈信息,这造成了极大的冗余。")]),t._v(" "),a("p",[t._v("所以,")]),t._v(" "),a("ul",[a("li",[t._v("在提供多人使用的三方库的时候不要使用 wrap,只有逻辑代码的时候使用 wrap 的功能")]),t._v(" "),a("li",[t._v("遇到一个错误不打算处理,那么要带上足够多的信息再向上抛出")]),t._v(" "),a("li",[t._v("一旦错误处理完成之后就没有错误了,不再需要把错误继续网上抛,返回 nil 即可")])]),t._v(" "),a("p",[t._v("所以我们使用 pkg/errors 包将上面的分层写法做一个更完善的改进:")]),t._v(" "),a("p",[t._v("dao 层生产错误")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// a")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getName")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 返回此处的错误堆栈")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" errors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Wrap")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"error:"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("service 追加错误")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("getName")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 不返回堆栈了,仅仅添加错误")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" errors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithMessage")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"getName error"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("controller 打印错误")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 添加日志")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("log")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h3",{attrs:{id:"errgroup-的使用技巧"}},[t._v("errgroup 的使用技巧")]),t._v(" "),a("p",[t._v("errgroup 的使用方法是 golang.org/x/sync/errgroup")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"context"')]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"golang.org/x/sync/errgroup"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tg"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ctx "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" errgroup"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithContext")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Background")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 启动一个 goroutine去处理错误")]),t._v("\n\tg"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Go")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"error1"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tg"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Go")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"error2"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 类似 waitgroup 的 wait 方法")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" g"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Wait")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"错误处理相关技巧"}},[t._v("错误处理相关技巧")]),t._v(" "),a("p",[t._v("这里会介绍在实战过程中用到的诸多技巧")]),t._v(" "),a("h3",{attrs:{id:"使用-errors-new-时要写清楚包名"}},[t._v("使用 errors.New() 时要写清楚包名")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" age\n\nErrMyAge "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" errors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("New")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"age: ErrMyAge is error"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nErrMyAddress "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" errors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("New")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"age: ErrMyAddress is error"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("h3",{attrs:{id:"使用-error-处理一般错误-使用-panic-处理严重错误-异常"}},[t._v("使用 error 处理一般错误,使用 panic 处理严重错误 (异常)")]),t._v(" "),a("p",[t._v("使用这种模型就避免了类似 Java 那种所有错误都一样的行为,Java 的使用 try-catch 的方式导致任何错误都是一个方式去处理,非常有可能让程序员忽略错误的处理")]),t._v(" "),a("p",[t._v("然而 go 不同,"),a("strong",[t._v("错误")]),t._v("使用 error,"),a("strong",[t._v("异常")]),t._v("使用 panic 的方式去处理。")]),t._v(" "),a("ul",[a("li",[t._v("错误:error")]),t._v(" "),a("li",[t._v("异常:panic")])]),t._v(" "),a("p",[t._v("假设我们在代码中使用了 panic,通常来说,为了代码的健壮性还是会使用 defer 函数去运行一个 recover() 的,程序的存活比啥都重要。")]),t._v(" "),a("h3",{attrs:{id:"基础库-应该避免使用-error-types"}},[t._v("基础库,应该避免使用 error types")]),t._v(" "),a("p",[t._v("因为这种写法容易造成代码的耦合,尤其是在我们写的基础库中,非常容易造成改动代码来引入的不健壮性。")]),t._v(" "),a("p",[t._v("使用自定义的 error type")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 为了额外增加更多的错误信息,字段需大写")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" ErrMyAge "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tEV "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\tMErr "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("e "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("ErrMyAge"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sprintf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"age: %s"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" e"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("EV"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\terr "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("ErrMyAge"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"err age is hi"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("使用 errors.New() 哨兵错误模式:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 我方代码")]),t._v("\nErrAge "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" errors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("New")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"age: ErrAge is error"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nErrAddress "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" errors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("New")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"age: ErrAddress is error"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("--")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("--")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("--")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("--")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 使用者")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"github.com/shgopher/age"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 带来了耦合")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" errors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Is")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" age"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ErrAge"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 处理")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("实际上他们都是 error types,只不过前者比后者增加了很多额外的信息,但是相同点是,他们都造成了耦合")]),t._v(" "),a("p",[t._v("如果别人使用了这个基础库,那么势必这些错误就会跟使用者的代码耦合,我们改动了代码,第三方的代码就会因此受到影响。")]),t._v(" "),a("p",[t._v("因此,在对外暴露的基础包中,我们应尽量减少定义哨兵错误 (上述定义方法被称之为哨兵模式的错误)")]),t._v(" "),a("p",[t._v("上述提供的哨兵模式是透明的错误导出机制,所以容易造成耦合")]),t._v(" "),a("p",[t._v("我们可以提供不透明的机制,不导出透明的错误类型,仅仅让用户判断是否等于 nil,就可以防止耦合的存在")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"github.com/shgopher/age"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" age"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Bar")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这也是大多数程序应该提供的模式,不对外暴露,避免了耦合")]),t._v(" "),a("p",[t._v("那么这种方法的缺陷也很明显了,就是无法获取更多的错误信息,理论上来说,我们也没必要获取那么多错误信息,但是如果真的要获取错误信息了,该如何去做呢?")]),t._v(" "),a("p",[t._v("我们可以向外暴露一些动作,只暴露行为:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" mage "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 通过一个对外暴露的函数可以对外输出行为")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 并且还不用暴露出具体的对象")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("IsMage")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ok "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("mage"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" ok "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h3",{attrs:{id:"优化不必要的代码让程序变得更简洁"}},[t._v("优化不必要的代码让程序变得更简洁")]),t._v(" "),a("p",[t._v("方法一将大函数变小函数,通过封装函数的方法从视觉上降低 if err 的影响。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 改造之前")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("OpenFile")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("src "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("dst "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" r"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Open")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("src"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" w"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("dst"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Cloase")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" io"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Copy")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("w"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("r"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 将前面两个操作封装成一个函数")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("OpenD")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("src dst "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("File"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("File"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" r "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("w "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("File \n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" r"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Open")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("src"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" w"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Create")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("dst"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Close")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" r"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("w"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 主函数就只有一个 if err 了")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("OpenFile")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("src"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("dst "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" r"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" w "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("File\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" r"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("w"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("OpenD")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("src"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("dst"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Close")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\tw"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Close")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" io"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Copy")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("w"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("r"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("方法二将代码中不必要的成分删除,来保证代码的简洁")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 改造前")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("AuthticateRequest")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("r "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Request"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\terr "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("authticate")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("r"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("User"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 实际上 authticate 只返回一个 error 类型的接口,根本不需要判断")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("AuthticateRequest")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("r "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Request"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("authticate")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("r"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("User"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("方法三使用更加合适的方法")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 我们要逐行去读取数据")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 改造前")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Countlines")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("r io"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Reader"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t\tbr "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" bufio"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewReader")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("r"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\tline "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\t\terr "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" br"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("readline")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token char"}},[t._v("'\\n'")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\tline "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v(" \n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("break")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" io"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("EOF "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" lines"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n\t\t\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 代码看起来也是很合理的样子,也很简洁,但是,我们其实用的函数不是特别的合适")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//其实这里使用 scan 更加合适,代码量更加精简,并且结构异常舒服")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Countlines")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("r io"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Reader"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\t\n\tsc "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" bufio"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewScanner")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("r"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tlines "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" sc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Scan")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tlines"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" lines"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" sc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Err")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("h3",{attrs:{id:"错误应该只处理一次"}},[t._v("错误应该只处理一次")]),t._v(" "),a("p",[t._v("我们有日志系统,有些时候我们发现一个错误,然后打了一个日志,然后又把错误给 return 了,实际上这与 go 语言哲学中说的错误只处理一次相违背")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ❌")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Open")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"./a"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tlog"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Prinln")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ✅")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("age")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Open")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"./a"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" errors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Wrap")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"open error"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("正确的处理方法是错误只处理一次,那么在什么时候处理呢?")]),t._v(" "),a("p",[t._v("上文提到了多层的架构设计,我们在底层 (带有堆栈的错误向上抛出) 和中层 (仅仅附加信息再次向上抛出) 仅仅是向上抛出,并不需要将错误记录在日志中,在应用层才需要去使用日志记录错误,日志记录完错误以后,也不需要再向上抛出错误了 (最顶端了) 完全满足 “只处理一次错误” 的要求。")]),t._v(" "),a("h2",{attrs:{id:"业务-code-码的设置"}},[t._v("业务 code 码的设置")]),t._v(" "),a("p",[t._v("常见的 http 错误码数量较少,比如常见的只有例如 404 301 302 200 503 等,绝对数量还是较少,无法去表达业务上的错误,因此我们需要设置一套能表达具体生产业务的 code 码。")]),t._v(" "),a("p",[t._v("为了保证服务端的安全,我们设置的 code 码应该设置两套数据,一套显示给客户端,一套自用,以此来保证服务端的绝对安全。")]),t._v(" "),a("p",[t._v("有三种设计业务 code 码的方式:")]),t._v(" "),a("h3",{attrs:{id:"一律返回-http-status-200-具体-code-码单独设置"}},[t._v("一律返回 http status 200,具体 code 码单独设置")]),t._v(" "),a("p",[t._v("例如")]),t._v(" "),a("div",{staticClass:"language-json extra-class"},[a("pre",{pre:!0,attrs:{class:"language-json"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"error"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"message"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Syntax error \\"Field picture specified more than once. This is only possible before version 2.1\\" at character 23: id,name,picture,picture"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"type"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"OAuthException"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"code"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2500")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"fbtrace_id"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"xxxxxxxxxxx"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("ul",[a("li",[t._v("http status code 通通 200")]),t._v(" "),a("li",[t._v("code 2500,才是真实的面向客户端的 code 码")])]),t._v(" "),a("p",[t._v("使用这种方法的一大缺陷就是必须解析 body 内容才能发现具体的错误业务码,很多场景我们仅仅需要知道返回的是成功或者错误,并不需要知晓具体的业务码,这是这种方式的一大弊端。")]),t._v(" "),a("h3",{attrs:{id:"使用合适的-http-status-code-简单的信息以及业务错误代码"}},[t._v("使用合适的 http status code + 简单的信息以及业务错误代码")]),t._v(" "),a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[t._v("HTTP/1.1 "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("400")]),t._v(" Bad Request\nx-connection-hash: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nset-cookie: "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("guest_id")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\nDate: Thu, 01 Jun "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2017")]),t._v(" 03:04:23 GMT\nContent-Length: "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("62")]),t._v("\nx-response-time: "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),t._v("\nstrict-transport-security: max-age"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("631138519")]),t._v("\nConnection: keep-alive\nContent-Type: application/json"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("charset")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("utf-8\nServer: tsa_b\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 注意这里:仅仅返回简单的错误信息")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"errors"')]),t._v(":"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"code"')]),t._v(":215,"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"message"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Bad Authentication data."')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这种方案也是大多数公司采纳的方案,使用具体的 http status code 可以知晓大概的业务类型,是错误还是正常运行,然后使用简单的错误信息和业务错误代码去定位具体的错误")]),t._v(" "),a("p",[t._v("如果业务不是特别复杂,使用这种方式即可")]),t._v(" "),a("h3",{attrs:{id:"使用合适的-http-status-code-非常详细的业务错误代码以及信息"}},[t._v("使用合适的 http status code + 非常详细的业务错误代码以及信息")]),t._v(" "),a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[t._v("HTTP/1.1 "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("400")]),t._v("\nDate: Thu, 01 Jun "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2017")]),t._v(" 03:40:55 GMT\nContent-Length: "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("276")]),t._v("\nConnection: keep-alive\nContent-Type: application/json"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("charset")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("utf-8\nServer: Microsoft-IIS/10.0\nX-Content-Type-Options: nosniff\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"SearchResponse"')]),t._v(":"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Version"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"2.2"')]),t._v(","),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Query"')]),t._v(":"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"SearchTerms"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"api error codes"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(","),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Errors"')]),t._v(":"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Code"')]),t._v(":1001,"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Message"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Required parameter is missing."')]),t._v(","),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Parameter"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"SearchRequest.AppId"')]),t._v(","),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"HelpUrl"')]),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"http'),a("span",{pre:!0,attrs:{class:"token entity",title:"\\u003a"}},[t._v("\\u003a")]),a("span",{pre:!0,attrs:{class:"token entity",title:"\\u002f"}},[t._v("\\u002f")]),a("span",{pre:!0,attrs:{class:"token entity",title:"\\u002f"}},[t._v("\\u002f")]),t._v("msdn.microsoft.com"),a("span",{pre:!0,attrs:{class:"token entity",title:"\\u002f"}},[t._v("\\u002f")]),t._v("en-us"),a("span",{pre:!0,attrs:{class:"token entity",title:"\\u002f"}},[t._v("\\u002f")]),t._v("library"),a("span",{pre:!0,attrs:{class:"token entity",title:"\\u002f"}},[t._v("\\u002f")]),t._v('dd251042.aspx"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("当业务逻辑稍微复杂一些,并且需要极其精准和快速的定位错误时,就需要在返回的 body 中去设置非常详细的错误信息")]),t._v(" "),a("p",[a("strong",[t._v("综上所述:")])]),t._v(" "),a("ul",[a("li",[t._v("使用正确的 http status code 让业务的第一步变得更加直观")]),t._v(" "),a("li",[t._v("区别于 http status code,具体业务的 code 码会更加丰富")]),t._v(" "),a("li",[t._v("返回尽可能详细的错误,有助于复杂逻辑的快速错误定位")]),t._v(" "),a("li",[t._v("直接返回给客户的错误代码不应该包括敏感信息,敏感信息的 code 码仅供内部使用")]),t._v(" "),a("li",[t._v("错误信息要求规范,简洁以及有用")])]),t._v(" "),a("h3",{attrs:{id:"业务-code-码的具体设计"}},[t._v("业务 code 码的具体设计")]),t._v(" "),a("p",[t._v("引入业务 code 码的核心原因就是 http status code 太少,以及他们并不能跟具体业务挂钩。")]),t._v(" "),a("p",[t._v("当我们设置好良好又详细的 code 码时,我们就可以快速定位业务代码,以及可以快速知晓发生错误的等级模块,具体信息等")]),t._v(" "),a("p",[t._v("下面给出具体的设计思路:"),a("strong",[t._v("纯数字表达,不同的数字段表达不同的模块不同的业务")])]),t._v(" "),a("p",[t._v("例如 100101")]),t._v(" "),a("ul",[a("li",[t._v("10:表示某个服务")]),t._v(" "),a("li",[t._v("01:表示某个服务下的模块")]),t._v(" "),a("li",[t._v("01:模块下的错误码")])]),t._v(" "),a("p",[t._v("10 服务 01 模块 01 错误,--- 服务 10 数据库模块未找到记录错误")]),t._v(" "),a("p",[t._v("一共最多有 100 个服务,每个服务最多有 100 个模块,每个模块最多有 100 个错误,如果某些模块 100 个都不够用,那怎么这个模块有必要去拆分一下了。")]),t._v(" "),a("h3",{attrs:{id:"如何设置-http-status-code"}},[t._v("如何设置 http status code")]),t._v(" "),a("ul",[a("li",[a("code",[t._v("1xx")]),t._v(":请求已接收,继续处理")]),t._v(" "),a("li",[a("code",[t._v("2xx")]),t._v(":成功处理了请求")]),t._v(" "),a("li",[a("code",[t._v("3xx")]),t._v(":请求被重定向")]),t._v(" "),a("li",[a("code",[t._v("4xx")]),t._v(":请求错误")]),t._v(" "),a("li",[a("code",[t._v("5xx")]),t._v(":服务器错误")])]),t._v(" "),a("p",[t._v("由于 http status code 相对数量也不算太少,如果每一个都利用上,难免会增加复杂度,建议仅使用基本的几个即可")]),t._v(" "),a("ul",[a("li",[t._v("200 - 表示请求成功执行。")]),t._v(" "),a("li",[t._v("400 - 表示客户端出问题。")]),t._v(" "),a("li",[t._v("500 - 表示服务端出问题。")])]),t._v(" "),a("p",[t._v("如果上述的感觉太少,再增加下面几个也可以")]),t._v(" "),a("ul",[a("li",[t._v("401 - 表示认证失败。")]),t._v(" "),a("li",[t._v("403 - 表示授权失败。")]),t._v(" "),a("li",[t._v("404 - 表示资源找不到,这里的资源可以是 URL 或者 RESTful 资源。")])]),t._v(" "),a("p",[t._v("将 http status code 控制在"),a("strong",[t._v("个位数")]),t._v(",有利于后端的逻辑代码简洁性,比如 301 302 确实是代表不同的含义,"),a("strong",[t._v("前端或许可以设置丰富的 http status code,因为浏览器会进行相关的具体操作,但是后端返回给前端的 http status code 并没有任何的操作,使用过多只会增加复杂度。")])]),t._v(" "),a("h2",{attrs:{id:"设计一个生产级的错误包"}},[t._v("设计一个生产级的错误包")]),t._v(" "),a("h3",{attrs:{id:"生产级的错误包需要的功能"}},[t._v("生产级的错误包需要的功能")]),t._v(" "),a("ol",[a("li",[t._v("支持错误堆栈")])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2021")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("07")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("02")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("14")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("17")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("03")]),t._v(" call "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" got failed"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" called "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n\nmain"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("funcB "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("home"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("colin"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("workspace"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("golang"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("src"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("github"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("com"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("marmotedu"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("gopractise"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("demo"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("errors"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("good"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("27")]),t._v("\nmain"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("funcA "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("home"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("colin"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("workspace"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("golang"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("src"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("github"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("com"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("marmotedu"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("gopractise"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("demo"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("errors"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("good"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("19")]),t._v("\nmain"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("main "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("home"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("colin"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("workspace"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("golang"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("src"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("github"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("com"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("marmotedu"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("gopractise"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("demo"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("errors"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("good"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v("\nruntime"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("main "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("home"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("colin"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("go1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("16.2")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("src"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("runtime"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("proc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("225runtime"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("goexit "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("home"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("colin"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("go1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("16.2")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("src"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("runtime"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("asm_amd64"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1371")]),t._v("\nexit status "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n")])])]),a("p",[t._v("拥有错误的堆栈,我们才能定位错误的根源。")]),t._v(" "),a("ol",{attrs:{start:"2"}},[a("li",[a("p",[t._v("支持打印不同的格式比如 %s %w %d")])]),t._v(" "),a("li",[a("p",[t._v("支持 Wrap() Unwrap() 的功能,就是错误的嵌套和反嵌套")])]),t._v(" "),a("li",[a("p",[t._v("错误包要支持 Is() 和 As() 方法,这主要是因为有错误的嵌套,所以无法再使用接口相比较的方式进行判断接口类型是否相等 (类型相同,值相同)")])]),t._v(" "),a("li",[a("p",[t._v("要支持格式化和非格式化的创建方式")])])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("errors"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("New")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"err"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"%w"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("h3",{attrs:{id:"具体实现"}},[t._v("具体实现")]),t._v(" "),a("p",[t._v("从 github.com/pkg/errors 包中改造即可。")]),t._v(" "),a("p",[t._v("增加以下字段的结构体就可以满足上面的需求")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" withCode "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\terr "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n\tcode "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\tcause "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("stack\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"issues"}},[t._v("issues")]),t._v(" "),a("p",[a("code",[t._v("问题一:")]),t._v(" "),a("strong",[t._v("请说出下列代码的执行输出")]),t._v("*")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"3"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"2"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"1"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hi1"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("panic")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"oops"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里的defer将不会进栈,所以也就不会执行了。")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"x"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hi2"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("答案是")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("hi1\n"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v("\noops\n"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("panic")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" oops\n\ngoroutine "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("running"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\nmain"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("tmp"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("sandbox1932632082"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("prog"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("18")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0xa7")]),t._v("\n\nProgram exited"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\n")])])]),a("p",[t._v("解释:Panic,意味着恐慌,意思等于 return,所以 panic 下面的数据是无法执行的,defer 不同,他们是顺序的将这些 defer 函数装入函数内置的 defer 栈的,所以在 return 之后,defer 栈会执行,所以这里的 defer 1 2 3 可以执行,Panic 前面的 hi1 可以执行,但是 Panic 之后,相当于 return 后面的 hi2 就无法执行了。")]),t._v(" "),a("p",[a("code",[t._v("问题二:")]),t._v(" "),a("strong",[t._v("看一段代码,分析答案")])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" r "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("recover")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" r "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Recovered in f"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" r"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"starting f"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("g")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"behind g"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//会终止执行")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("g")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Defer in g"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Panicking!"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("panic")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sprintf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"%v"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Printing in g"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//终止执行")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("答案是")]),t._v(" "),a("div",{staticClass:"language-js extra-class"},[a("pre",{pre:!0,attrs:{class:"language-js"}},[a("code",[t._v("starting f\npanicking\nDefer "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" g "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v("\nrecoverd "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" f\n")])])]),a("p",[t._v("解释一下,首先执行的是 f 函数的代码,然后开始执行 g,在 g 中遇到了 Panic,所以 panic 后面的 parinting in g 就无法执行了,所以执行了 defer in g\n这个时候 f 中的 g(2) 后面的数据也无法执行了,因为整个 f 也陷入了恐慌,所以它只能 return 进入 defer 了,defer 中刚好有 recover,所以执行了 recover 信息后,就退出了函数。")]),t._v(" "),a("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),a("ul",[a("li",[t._v("https://mp.weixin.qq.com/s/EvkMQCPwg-B0ffZonpwXodg")]),t._v(" "),a("li",[t._v("https://mp.weixin.qq.com/s/D_CVrPzjP3O81EpFqLoynQ")]),t._v(" "),a("li",[t._v("https://time.geekbang.org/column/article/391895")]),t._v(" "),a("li",[t._v("极客时间《go 进阶训练营》")])])])}),[],!1,null,null,null);s.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/45.59f733ac.js b/assets/js/45.59f733ac.js new file mode 100644 index 000000000..cd50f3724 --- /dev/null +++ b/assets/js/45.59f733ac.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[45],{475:function(t,s,a){"use strict";a.r(s);var n=a(36),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"go-语言零值"}},[t._v("go 语言零值")]),t._v(" "),a("p",[t._v("go 在声明时期就会为变量提供默认值。")]),t._v(" "),a("p",[t._v("下面是所有的原生 go 类型的零值:")]),t._v(" "),a("ul",[a("li",[t._v("整数类型:0")]),t._v(" "),a("li",[t._v("浮点数:0.0")]),t._v(" "),a("li",[t._v("布尔类型:false")]),t._v(" "),a("li",[t._v("字符串类型:“”")]),t._v(" "),a("li",[t._v("指针,接口,切片,channel,map,function:nil")])]),t._v(" "),a("p",[t._v("至于复合类型,比如数组,和结构体的声明过程,就是 "),a("code",[t._v("递归的,针对于它所有的子元素的初始化")])]),t._v(" "),a("h2",{attrs:{id:"零值可用"}},[t._v("零值可用")]),t._v(" "),a("p",[t._v("go 奉行零值可用,最经典的案例是切片的 append 过程,即是你最初的切片变量只是声明,并未 make 赋予底层数组,那么 go 系统仍然处理的结果是可用,并非 Panic")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\ns "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("下吗再看一个例子")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" b "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("bytes"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Buffer\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[a("code",[t._v("fmt.Println(b)")]),t._v(" 是调用的 "),a("code",[t._v("b.String()")]),t._v(",那么为什么这里输出的是 "),a("code",[t._v("")]),t._v(" 不是 Panic 呢?")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Buffer"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("String")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" b "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Special case, useful in debugging.")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("buf"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("off"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这就是答案,它在实现的 String 中实现了零值可用,"),a("code",[t._v("原因一:")]),t._v(" "),a("strong",[t._v("nil 的对象是完全可以调用属于它身上的方法的")]),t._v(","),a("code",[t._v("原因二:")]),t._v(" 这个方法内部实现的时候又是直接 return 一个 "),a("code",[t._v("")]),t._v(" 避免了取 nil 索引值的情况。所以说基于这两点,它零值可用。")]),t._v(" "),a("p",[t._v("再看一个经典的 buffer 例子:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"bytes"')]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a bytes"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Buffer\n\ta"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Write")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("byte")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"12"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("String")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("为什么只是声明了 buffer 就可以直接往里面写值呢?")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 结构体")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Buffer "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tbuf "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// contents are the bytes buf[off : len(buf)]")]),t._v("\n\toff "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// read at &buf[off], write at &buf[len(buf)]")]),t._v("\n\tlastRead readOp "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// last read operation, so that Unread* can work correctly.")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 取自 Write() 相关的代码 这段代码保证了即便是初始化的buf是nil也可以零值可用。")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("buf "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&&")]),t._v(" n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<=")]),t._v(" smallBufferSize "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("buf "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" smallBufferSize"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("另外,当切片是 nil 的时候,也是完全可以取它的 "),a("code",[t._v("[:]")]),t._v(" 切片的,只要不超过 index 都没问题,这也是满足零值可用的一个小 tips")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\ns "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 或者 s = s[:0] or s = s[0:] or s= s[0:0]")]),t._v("\n")])])]),a("h2",{attrs:{id:"零值不可用"}},[t._v("零值不可用")]),t._v(" "),a("ul",[a("li",[t._v("slice 零值赋值")]),t._v(" "),a("li",[t._v("map 零值赋值")]),t._v(" "),a("li",[t._v("互斥锁的值复制")])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s slice\ns"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 错误 ❌")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" m "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\nm"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 错误 ❌")]),t._v("\n")])])]),a("p",[t._v("再看互斥锁的案例")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" mu sync"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Mutex\nmu"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\nmu"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("这段代码是可以正常使用的")]),t._v(" "),a("p",[t._v("但是,如果队 mu 进行值的复制就不能使用了")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"sync"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" mu sync"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Mutex\n\tmu"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("foo")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("mu"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tmu"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("foo")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("mu sync"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Mutex"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tmu"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Lock")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tmu"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Unlock")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("这个问题的解释是这样的:互斥锁是带有状态的,就是说,当你复制的时候本来是 a 的状态,然后复制过去还是 a 的状态,但是这是一个新的对象了按道理应该是初始状态,所以就会出现错误,这也是传说中的重入锁 (go 不支持),因为 go 的互斥锁是带有状态的,所以这种复制的方法就会出现错误。")]),t._v(" "),a("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),a("ul",[a("li",[t._v("https://github.com/golang/go/blob/037b209ae3e0453004a4d57e152aa522c56f79e4/src/bytes/buffer.go#L117")])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/46.ed939de4.js b/assets/js/46.ed939de4.js new file mode 100644 index 000000000..9e83e4b32 --- /dev/null +++ b/assets/js/46.ed939de4.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[46],{476:function(t,E,r){"use strict";r.r(E);var a=r(36),v=Object(a.a)({},(function(){var t=this,E=t.$createElement,r=t._self._c||E;return r("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[r("h1",{attrs:{id:"工程"}},[t._v("工程")]),t._v(" "),r("ul",[r("li",[r("a",{attrs:{href:"./log"}},[t._v("log 包")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./%E6%B5%8B%E8%AF%95"}},[t._v("测试")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./%E6%80%A7%E8%83%BD%E5%89%96%E6%9E%90"}},[t._v("性能剖析")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./%E5%8C%85%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7"}},[t._v("包管理工具")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./%E5%8A%A8%E6%80%81%E8%B0%83%E8%AF%95"}},[t._v("动态调试")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86"}},[t._v("错误处理")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./cgo"}},[t._v("cgo")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./golint"}},[t._v("go lint")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./%E5%8F%8D%E5%B0%84"}},[t._v("反射")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./web"}},[t._v("web")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./wasm"}},[t._v("wasm")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./%E5%91%BD%E4%BB%A4"}},[t._v("命令")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./%E4%BC%98%E7%A7%80%E7%AC%AC%E4%B8%89%E6%96%B9%E5%8C%85"}},[t._v("优秀第三方包")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./go%E6%A0%87%E5%87%86%E5%BA%93"}},[t._v("go 标准库")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./%E9%A1%B9%E7%9B%AE%E7%BB%84%E7%BB%87%E5%BD%A2%E5%BC%8F"}},[t._v("go 项目组织形式")])]),t._v(" "),r("li",[r("a",{attrs:{href:"./go%E5%91%BD%E5%90%8D%E6%83%AF%E4%BE%8B"}},[t._v("go 命名惯例")])])])])}),[],!1,null,null,null);E.default=v.exports}}]); \ No newline at end of file diff --git a/assets/js/47.6dd9b6a9.js b/assets/js/47.6dd9b6a9.js new file mode 100644 index 000000000..f15157596 --- /dev/null +++ b/assets/js/47.6dd9b6a9.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[47],{477:function(t,s,i){"use strict";i.r(s);var e=i(36),n=Object(e.a)({},(function(){var t=this.$createElement,s=this._self._c||t;return s("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}},[s("h1",{attrs:{id:"cgo"}},[this._v("cgo")]),this._v(" "),s("h2",{attrs:{id:"参考资料"}},[this._v("参考资料")]),this._v(" "),s("ul",[s("li",[this._v("https://mp.weixin.qq.com/s/Xs5Ngi9444z6SyVVu0byUg")])])])}),[],!1,null,null,null);s.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/48.e525f69a.js b/assets/js/48.e525f69a.js new file mode 100644 index 000000000..9f5ef4fdb --- /dev/null +++ b/assets/js/48.e525f69a.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[48],{478:function(t,s,i){"use strict";i.r(s);var e=i(36),n=Object(e.a)({},(function(){var t=this.$createElement,s=this._self._c||t;return s("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}},[s("h1",{attrs:{id:"github-copilot"}},[this._v("Github Copilot")]),this._v(" "),s("p",[this._v("自动补全代码")])])}),[],!1,null,null,null);s.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/49.75d23cdb.js b/assets/js/49.75d23cdb.js new file mode 100644 index 000000000..f6bfe2bb7 --- /dev/null +++ b/assets/js/49.75d23cdb.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[49],{480:function(t,e,n){"use strict";n.r(e);var s=n(36),o=Object(s.a)({},(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"web-开发"}},[t._v("web 开发")]),t._v(" "),n("p",[t._v("这里主要讲述 go 的 web 开发的用法。基本涉及到的包有 "),n("code",[t._v("net")]),t._v(","),n("code",[t._v("net/http")])]),t._v(" "),n("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),n("ul",[n("li",[t._v("https://mp.weixin.qq.com/s/NQSgECerdR2rM06ugp4qjQ")]),t._v(" "),n("li",[t._v("https://mp.weixin.qq.com/s/jGrPM0UgqjZ10cXafoC0wQ")])])])}),[],!1,null,null,null);e.default=o.exports}}]); \ No newline at end of file diff --git a/assets/js/5.864fc44c.js b/assets/js/5.864fc44c.js new file mode 100644 index 000000000..e3d72b559 --- /dev/null +++ b/assets/js/5.864fc44c.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[5],{401:function(e,t,a){},434:function(e,t,a){"use strict";a(401)},438:function(e,t,a){"use strict";a.r(t);a(96),a(45),a(10),a(99),a(100);var o={name:"CodeGroup",data:function(){return{codeTabs:[],activeCodeTabIndex:-1}},watch:{activeCodeTabIndex:function(e){this.activateCodeTab(e)}},mounted:function(){this.loadTabs()},methods:{changeCodeTab:function(e){this.activeCodeTabIndex=e},loadTabs:function(){var e=this;this.codeTabs=(this.$slots.default||[]).filter((function(e){return Boolean(e.componentOptions)})).map((function(t,a){return""===t.componentOptions.propsData.active&&(e.activeCodeTabIndex=a),{title:t.componentOptions.propsData.title,elm:t.elm}})),-1===this.activeCodeTabIndex&&this.codeTabs.length>0&&(this.activeCodeTabIndex=0),this.activateCodeTab(0)},activateCodeTab:function(e){this.codeTabs.forEach((function(e){e.elm&&e.elm.classList.remove("theme-code-block__active")})),this.codeTabs[e].elm&&this.codeTabs[e].elm.classList.add("theme-code-block__active")}}},n=(a(434),a(36)),c=Object(n.a)(o,(function(){var e=this,t=e.$createElement,a=e._self._c||t;return a("ClientOnly",[a("div",{staticClass:"theme-code-group"},[a("div",{staticClass:"theme-code-group__nav"},[a("ul",{staticClass:"theme-code-group__ul"},e._l(e.codeTabs,(function(t,o){return a("li",{key:t.title,staticClass:"theme-code-group__li"},[a("button",{staticClass:"theme-code-group__nav-tab",class:{"theme-code-group__nav-tab-active":o===e.activeCodeTabIndex},on:{click:function(t){return e.changeCodeTab(o)}}},[e._v("\n "+e._s(t.title)+"\n ")])])})),0)]),e._v(" "),e._t("default"),e._v(" "),e.codeTabs.length<1?a("pre",{staticClass:"pre-blank"},[e._v("// Make sure to add code blocks to your code group")]):e._e()],2)])}),[],!1,null,"deefee04",null);t.default=c.exports}}]); \ No newline at end of file diff --git a/assets/js/50.ac1acdef.js b/assets/js/50.ac1acdef.js new file mode 100644 index 000000000..6d24001e2 --- /dev/null +++ b/assets/js/50.ac1acdef.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[50],{479:function(t,s,a){"use strict";a.r(s);var n=a(36),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"go-命名惯例"}},[t._v("go 命名惯例")]),t._v(" "),a("p",[t._v("go 语言的命名哲学就是"),a("strong",[t._v("尽可能的简单化")]),t._v("。")]),t._v(" "),a("p",[t._v("go 要求的简单,不仅仅是简单化,还要关注上下文的连贯性,go 喜欢一个简洁明了的命名+对字段的注释")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// userTypeInfo stores the information associated with a type the user has handed")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// to the package. It's computed once and stored in a map keyed by reflection")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// type.")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" userTypeInfo "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tuser reflect"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Type "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// the type the user handed us")]),t._v("\n\tbase reflect"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Type "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// the base type after all indirections")]),t._v("\n\tindir "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// number of indirections to reach the base type")]),t._v("\n\texternalEnc "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// xGob, xBinary, or xText")]),t._v("\n\texternalDec "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// xGob, xBinary or xText")]),t._v("\n\tencIndir "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int8")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// number of indirections to reach the receiver type; may be negative")]),t._v("\n\tdecIndir "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int8")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// number of indirections to reach the receiver type; may be negative")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("比如这段代码,对于字段和结构体都进行了注释,但是结构体内部的字段都相对简洁")]),t._v(" "),a("h2",{attrs:{id:"包"}},[t._v("包")]),t._v(" "),a("p",[t._v("包名称一般使用小写单词进行命名,例如说:"),a("code",[t._v("context")]),t._v(","),a("code",[t._v("http")]),t._v(","),a("code",[t._v("net")]),t._v(",无需考虑包名称的重复化,因为在 go 引入包的过程中,实际上是引入的路径,并且 go 的包在引入过程中还可以进行包的重命名")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"example.com/hey/log"')]),t._v("\n\n blog "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"example.net/bob/log"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("包的路径名称最后一位尽量和包的名称保持一致性,不过不一致也是有不少情况的,比如说,现在有一个适应不同语言的 log,那么命名的时候就变成了 "),a("code",[t._v("github.com/shgopher/golog")]),t._v(","),a("code",[t._v("github.com/shgopher/javalog")]),t._v(",但是他们实际上的包都叫 log,并不是 golog 和 javalog,那么使用的时候就需要对其中一个包进行重命名了。")]),t._v(" "),a("h2",{attrs:{id:"变量-类型-函数-方法"}},[t._v("变量,类型,函数,方法")]),t._v(" "),a("p",[t._v("因为 go 语言使用导出的变量时需要带上包的名称 (除了。省略包名称的方式,一般不提倡),所以命名的时候变量不需要再加上包的称呼,比如说 string 包的 Reader 就不需要写成 "),a("code",[t._v("string.StringReader")]),t._v(",写成 "),a("code",[t._v("string.Reader")]),t._v(" 即可")]),t._v(" "),a("p",[t._v("go 变量的命名使用驼峰的命名方式,go 的文件名称使用下划线连接的方式,比如说 "),a("code",[t._v("UpperName")]),t._v(","),a("code",[t._v("lowName")]),t._v(" 区别就是前者是大驼峰,导出的变量,后者是小驼峰,不可导出变量,缩略词如果首字母大写那么全部大写,比如 "),a("code",[t._v("HTTP")]),t._v(",不能写成 "),a("code",[t._v("Http")])]),t._v(" "),a("p",[t._v("go 代码里使用了大量单字母或者短字母的命名方式。")]),t._v(" "),a("p",[t._v("for 循环中以及 if 条件语句中使用单字母")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" k"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" users"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("method1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("函数和方法的参数或者返回值,常见单字母或单个单词的方式出现。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("method1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("name "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("方法调用的时候会绑定类型信息,可以尽量使用单个单词的命名方法")]),t._v(" "),a("p",[t._v("函数和类型多以多单词驼峰的方法组合,比如 "),a("code",[t._v("func MakeFile(){}")]),t._v(","),a("code",[t._v("type gobEncoderType struct {}")])]),t._v(" "),a("p",[t._v("在命名变量的时候,不要带上类型信息,比如说 "),a("code",[t._v("names")]),t._v(" 比 "),a("code",[t._v("nameSlice")]),t._v(" 要好,因为 go 强调命名和使用越近越好,没必要在字段上加上类型的名称。")]),t._v(" "),a("p",[t._v("go 使用一致性来强调单字母或者单个单词的意义,意思就是说使用的单个字母在任何地方表示的意思都是一样的。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" k"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" names"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" users"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("从 k v i 这三个字母就可以观察到,绝大多数情况下,在 go 语言中 kv 就是表示的 key 和 value,i 表示的就是 index,那么即便是单个字母也不会造成识别障碍")]),t._v(" "),a("p",[t._v("另外 "),a("code",[t._v("t")]),t._v(" "),a("code",[t._v("b")]),t._v(" "),a("code",[t._v("n")]),t._v(" 也很常见,他们通常表示 time 和 byte 以及数量")]),t._v(" "),a("h2",{attrs:{id:"常量"}},[t._v("常量")]),t._v(" "),a("ul",[a("li",[t._v("go 语言的常量并不要求全大写,通常只有本身是全大写的常量才是全大写,比如 "),a("code",[t._v("PI")])]),t._v(" "),a("li",[t._v("常量不需要赋予类型,系统会根据根据使用时期左变量的类型,以及运算操作进行自动推断")])]),t._v(" "),a("p",[t._v("一般常量")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n keepHostHeader "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("全大写常量")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" PI "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3.14")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("const")]),t._v(" SIGABRT "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Signal")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0x6")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("h2",{attrs:{id:"接口"}},[t._v("接口")]),t._v(" "),a("p",[t._v("拥有唯一方法的接口,或者内嵌多个拥有唯一方法的接口一律使用 "),a("code",[t._v("单词+er")]),t._v(" 结尾,比如 "),a("code",[t._v("Reader")]),t._v(" 或者 "),a("code",[t._v("WriteReadCloser")])]),t._v(" "),a("p",[t._v("go 语言推崇简单接口,和内嵌多个简单接口的接口,所以你会看到 go 语言标准库很多带有 er 结尾的接口")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Reader "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Reade")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Writer "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Write")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" WriteReadeCloser "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Writer\n Reader\n Closer\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"两种命名的对比"}},[t._v("两种命名的对比")]),t._v(" "),a("p",[t._v("简洁的并且考虑上下文一致性的代码:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("RuneCount")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n count "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" RuneSelf "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("DecodeRune")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+=")]),t._v("n\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n count"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" count\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("见名知意的 java 式的代码:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("RuneCount")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("buffer "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n runeCount "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" index "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v("index "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("buffer"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" buffer"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("index"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<")]),t._v(" runeSelf "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n index"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" size "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("DecodeRune")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("buffer"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("index"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n index "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+=")]),t._v(" size\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n runeCount"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" runeCount\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),a("ul",[a("li",[t._v("图书:go 语言精进之路")])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/51.df597de1.js b/assets/js/51.df597de1.js new file mode 100644 index 000000000..a9cabfb28 --- /dev/null +++ b/assets/js/51.df597de1.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[51],{481:function(t,s,a){"use strict";a.r(s);var e=a(36),r=Object(e.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"go-标准库的简单介绍"}},[t._v("go 标准库的简单介绍")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/std")]),t._v(" "),a("h2",{attrs:{id:"archive"}},[t._v("archive")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/archive")]),t._v(" "),a("p",[t._v("目前不存在 archive 包本身,存在其子包,这个包,包含的内容是对于档案文件的处理,比如 tar,zip 这种文档文件。")]),t._v(" "),a("h3",{attrs:{id:"archive-tar"}},[t._v("archive/tar")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/archive/tar")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"archive/tar"')]),t._v("\n")])])]),a("p",[a("code",[t._v("archive/tar")]),t._v(" 包,实现了对 tar 文件的处理")]),t._v(" "),a("ul",[a("li",[t._v("tar 文件头的设置")]),t._v(" "),a("li",[t._v("tar 文件的读写")])]),t._v(" "),a("h3",{attrs:{id:"archive-zip"}},[t._v("archive/zip")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/archive/zip")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"archive/zip"')]),t._v("\n")])])]),a("p",[a("code",[t._v("archive/zip")]),t._v(" 包,实现了对 zip 文件的处理")]),t._v(" "),a("ul",[a("li",[t._v("zip 文件头的设置")]),t._v(" "),a("li",[t._v("zip 文件的读写")])]),t._v(" "),a("h2",{attrs:{id:"bufio"}},[t._v("bufio")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/bufio")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"bufio"')]),t._v("\n")])])]),a("p",[a("code",[t._v("bufio")]),t._v(" 包,提供了有缓冲的 i/o,比 io 包封装程度更高。使用缓冲区来一次读取多个字节,从而减少系统调用的次数。")]),t._v(" "),a("ul",[a("li",[t._v("提供了基本的读写功能")]),t._v(" "),a("li",[t._v("提供了逐行读取的功能")])]),t._v(" "),a("h2",{attrs:{id:"builtin"}},[t._v("builtin")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/builtin")]),t._v(" "),a("p",[t._v("注意,此包无法使用 import 的方式引入,因为它只是内部包和内部类型的一个存放位置而已")]),t._v(" "),a("p",[t._v("比如有 append() clear() max() min() int float64 等等")]),t._v(" "),a("h2",{attrs:{id:"bytes"}},[t._v("bytes")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/bytes")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"bytes"')]),t._v("\n")])])]),a("p",[t._v("bytes 包,包含了很多处理 bytes 类型的操作。")]),t._v(" "),a("p",[t._v("跟字符串的基本操作类型")]),t._v(" "),a("h2",{attrs:{id:"cmp"}},[t._v("cmp")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/cmp")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"cmp"')]),t._v("\n")])])]),a("p",[t._v("cmp 包提供了有关比较的相关内容。")]),t._v(" "),a("ul",[a("li",[t._v("cmp.Ordered 表示可比较的类型约束")]),t._v(" "),a("li",[t._v("提供了比较函数")])]),t._v(" "),a("h2",{attrs:{id:"compress"}},[t._v("compress")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/compress")]),t._v(" "),a("p",[t._v("不直接提供 compress 包,提供了众多子包,所有的内容都是关于压缩算法")]),t._v(" "),a("h3",{attrs:{id:"compress-bzip2"}},[t._v("compress/bzip2")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/compress/bzip2")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"compress/bzip2"')]),t._v("\n")])])]),a("p",[t._v("提供了 bzip2 的解压功能,然而并没有提供压缩的功能")]),t._v(" "),a("h3",{attrs:{id:"compress-flate"}},[t._v("compress/flate")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/compress/flate")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"compress/flate"')]),t._v("\n")])])]),a("p",[t._v("flate 实现了 RFC1951 中描述的 DEFLATE 压缩数据格式。")]),t._v(" "),a("h3",{attrs:{id:"compress-gzip"}},[t._v("compress/gzip")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/compress/gzip")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"compress/gzip"')]),t._v("\n")])])]),a("p",[t._v("gzip 实现了对 gzip 格式压缩文件的解压 (读取) 和压缩 (写入)")]),t._v(" "),a("h3",{attrs:{id:"compress-lzw"}},[t._v("compress/lzw")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/compress/lzw")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"compress/lzw"')]),t._v("\n")])])]),a("p",[t._v("lzw 实现了 lzw 文件 (Lempel-Ziv-Welch) 的解压缩操作。")]),t._v(" "),a("h3",{attrs:{id:"compress-zlib"}},[t._v("compress/zlib")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/compress/zlib")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"compress/zlib"')]),t._v("\n")])])]),a("p",[t._v("zlib 实现了对 zlib 格式文件的解压和压缩操作。")]),t._v(" "),a("h2",{attrs:{id:"container"}},[t._v("container")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/container")]),t._v(" "),a("p",[t._v("container 没有提供本包,提供了众多子包,这个目录下的内容都是关于容器的,这也是 go 提供的内置容器")]),t._v(" "),a("h3",{attrs:{id:"container-heap"}},[t._v("container/heap")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/container/heap")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"container/heap"')]),t._v("\n")])])]),a("p",[t._v("go 内置的堆,值得注意的是,go 语言仅提供了接口以及接口的相关函数,并没有具体的实现,使用时还需要自行实现接口。")]),t._v(" "),a("h3",{attrs:{id:"container-list"}},[t._v("container/list")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/container/list")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(' "container'),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("list\n")])])]),a("p",[t._v("go 内置的双向链表,这里不是接口了,是已经实现好了的双向链表")]),t._v(" "),a("h3",{attrs:{id:"container-ring"}},[t._v("container/ring")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/container/ring")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"container/ring"')]),t._v("\n")])])]),a("p",[t._v("go 内置的循环链表,非接口,已经实现好了")]),t._v(" "),a("h2",{attrs:{id:"context"}},[a("a",{attrs:{href:"../%E5%B9%B6%E5%8F%91/context"}},[t._v("context")])]),t._v(" "),a("p",[t._v("https://pkg.go.dev/context")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"context"')]),t._v("\n")])])]),a("p",[t._v("context 提供了在 “多线程” 的场景下的线程控制功能,简单的说就是 context 这个上下文可以统一取消所有的上下文环境中的 goroutine")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// cal的调用,以及计时器的到达均可调用 ctx.Done() 的发生。")]),t._v("\n\tctx"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" cal "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithTimeout")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("context"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Background")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" time"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("cal")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("select")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("time"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("After")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("time"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Print")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),t._v("ctx"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Done")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Print")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ttime"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("time"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"crypto"}},[t._v("crypto")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/crypto")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"crypto"')]),t._v("\n")])])]),a("p",[t._v("crypto 包,包含了很多加密算法")]),t._v(" "),a("h3",{attrs:{id:"crypto-aes"}},[t._v("crypto/aes")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/crypto/aes")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"crypto/aes"')]),t._v("\n")])])]),a("p",[t._v("提供了 aes 加密算法的加密过程。此算法为对称加密算法")]),t._v(" "),a("ul",[a("li",[t._v("创建一个密钥")])]),t._v(" "),a("h3",{attrs:{id:"crypto-cipher"}},[t._v("crypto/cipher")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/crypto/cipher")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"crypto/cipher"')]),t._v("\n")])])]),a("h3",{attrs:{id:"crypto-des"}},[t._v("crypto/des")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/crypto/des")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"crypto/des"')]),t._v("\n")])])]),a("h3",{attrs:{id:"crypto-dsa"}},[t._v("crypto/dsa")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/crypto/dsa")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"crypto/dsa"')]),t._v("\n")])])]),a("h3",{attrs:{id:"crypto-ecdh"}},[t._v("crypto/ecdh")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/crypto/ecdh")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"crypto/ecdh"')]),t._v("\n")])])]),a("h3",{attrs:{id:"crypto-ecdsa"}},[t._v("crypto/ecdsa")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/crypto/ecdsa")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"crypto/ecdsa"')]),t._v("\n")])])]),a("h3",{attrs:{id:"crypto-ed25519"}},[t._v("crypto/ed25519")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/crypto/ed25519")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"crypto/ed25519"')]),t._v("\n")])])]),a("h3",{attrs:{id:"crypto-elliptic"}},[t._v("crypto/elliptic")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/crypto/elliptic")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"crypto/elliptic"')]),t._v("\n")])])]),a("h3",{attrs:{id:"crypto-hmac"}},[t._v("crypto/hmac")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/crypto/hmac")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"crypto/hmac"')]),t._v("\n")])])]),a("h3",{attrs:{id:"crypto-md5"}},[t._v("crypto/md5")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/crypto/md5")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"crypto/md5"')]),t._v("\n")])])]),a("h3",{attrs:{id:"crypto-rand"}},[t._v("crypto/rand")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/crypto/rand")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"crypto/rand"')]),t._v("\n")])])]),a("h3",{attrs:{id:"crypto-rc4"}},[t._v("crypto/rc4")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/crypto/rc4")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"crypto/rc4"')]),t._v("\n")])])]),a("h3",{attrs:{id:"crypto-rsa"}},[t._v("crypto/rsa")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/crypto/rsa")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"crypto/rsa"')]),t._v("\n")])])]),a("h3",{attrs:{id:"crypto-sha1"}},[t._v("crypto/sha1")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/crypto/sha1")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"crypto/sha1"')]),t._v("\n")])])]),a("h3",{attrs:{id:"crypto-sha256"}},[t._v("crypto/sha256")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/crypto/sha256")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"crypto/sha256"')]),t._v("\n")])])]),a("h3",{attrs:{id:"crypto-sha512"}},[t._v("crypto/sha512")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/crypto/sha512")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"crypto/sha512"')]),t._v("\n")])])]),a("h3",{attrs:{id:"crypto-subtle"}},[t._v("crypto/subtle")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/crypto/subtle")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"crypto/subtle"')]),t._v("\n")])])]),a("h3",{attrs:{id:"crypto-tls"}},[t._v("crypto/tls")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/crypto/tls")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"crypto/tls"')]),t._v("\n")])])]),a("h3",{attrs:{id:"crypto-x509"}},[t._v("crypto/x509")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/crypto/x509")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"crypto/x509"')]),t._v("\n")])])]),a("h4",{attrs:{id:"crypto-x509-pkix"}},[t._v("crypto/x509/pkix")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/crypto/x509/pkix")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"crypto/x509/pkix"')]),t._v("\n")])])]),a("h2",{attrs:{id:"database"}},[t._v("database")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/database\n此包不可直接使用,它包含了子包,这个子包是处理 sql 的统一接口,并不提供实际的实现")]),t._v(" "),a("h3",{attrs:{id:"database-sql"}},[t._v("database/sql")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/database/sql")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"database/sql"')]),t._v("\n")])])]),a("p",[t._v("当我们使用 MySQL,Redis 等数据库的时候,通常要使用上述的方式引入这个包,此包提供了 SQL 操作的接口。")]),t._v(" "),a("p",[t._v("使用此包必须跟第三方的数据库驱动结合,比如下面这种操作:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"database/sql"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"xx/mysql"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n")])])]),a("h3",{attrs:{id:"database-sql-driver"}},[t._v("database/sql/driver")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/database/sql/driver")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"database/sql/driver"')]),t._v("\n")])])]),a("p",[t._v("driver 包,包含了数据库 driver 的接口,要想实现某个数据库的驱动 (driver) 就必须引入此包,实现此包定义接口的具体内容。")]),t._v(" "),a("p",[t._v("在被用户使用的时候,引入第三方数据库驱动,加上引入 database/sql 这个 SQL 操作包,就可以实现正常的 SQL 操作。")]),t._v(" "),a("h2",{attrs:{id:"debug"}},[t._v("debug")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/debug")]),t._v(" "),a("p",[t._v("不提供 debug 包本身,debug 包含了众多子包,都是跟调试相关。")]),t._v(" "),a("h3",{attrs:{id:"debug-buildinfo"}},[t._v("debug/buildinfo")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/debug/buildinfo\n不直接使用此包,此包提供二进制的功能,由 runtime/debug 来调用。")]),t._v(" "),a("h3",{attrs:{id:"debug-dwarf"}},[t._v("debug/dwarf")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/debug/dwarf")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"debug/dwarf"')]),t._v("\n")])])]),a("p",[t._v("用于解析 DWARF 调试信息,DWARF 调试信息包含了程序的源代码、变量、函数、类型等相关信息,可以帮助调试器进行源代码级别的调试。")]),t._v(" "),a("h3",{attrs:{id:"debug-elf"}},[t._v("debug/elf")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/debug/elf")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"debug/elf"')]),t._v("\n")])])]),a("p",[t._v("用于解析 ELF 可执行文件格式,")]),t._v(" "),a("h3",{attrs:{id:"debug-gosym"}},[t._v("debug/gosym")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/debug/gosym")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"debug/gosym"')]),t._v("\n")])])]),a("p",[t._v("debug/gosym 用于解析 Go 语言程序的符号表信息。")]),t._v(" "),a("h3",{attrs:{id:"debug-macho"}},[t._v("debug/macho")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/debug/macho")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"debug/macho"')]),t._v("\n")])])]),a("p",[t._v("debug/macho 用于解析 Mach-O 格式的可执行文件")]),t._v(" "),a("h3",{attrs:{id:"debug-pe"}},[t._v("debug/pe")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/debug/pe")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"debug/pe"')]),t._v("\n")])])]),a("p",[t._v("debug/pe 用于解析 PE 格式的可执行文件")]),t._v(" "),a("h3",{attrs:{id:"debug-plan9obj"}},[t._v("debug/plan9obj")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/debug/plan9obj")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"debug/plan9obj"')]),t._v("\n")])])]),a("p",[t._v("debug/plan9obj 用于解析 Plan 9 object 文件格式。")]),t._v(" "),a("h2",{attrs:{id:"embed"}},[t._v("embed")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/embed")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"embed"')]),t._v("\n")])])]),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//go:embed hello.text")]),t._v("\n")])])]),a("p",[t._v("mbed 是 Go 语言自 1.16 版本引入的一个标准库,用于将静态文件嵌入到 Go 代码中。")]),t._v(" "),a("p",[t._v("通过 embed 包,我们可以将静态文件 (如文本文件、JSON 文件、HTML 文件、图像文件等) 直接嵌入到 Go 代码中,而无需将文件作为独立的资源文件放在磁盘上。这样做的好处是,可以将所有的资源文件打包到可执行文件中,方便分发和部署。")]),t._v(" "),a("p",[t._v("将一个文件嵌入到一个 string 中")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"embed"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里的注释开头没有空格,这是 go 的自有命令注释;//go:xx ")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//go:embed hello.txt")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("print")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("将一个文件嵌入到文件系统中")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"embed"')]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//go:embed hello.txt")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" helloFile embed"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("FS\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tcontent"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" helloFile"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("ReadFile")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello.txt"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"无法读取文件:"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("content"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"encoding"}},[t._v("encoding")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/encoding\n使用时,不直接使用 encoding,除非你想亲自实现某个编码的加解码")]),t._v(" "),a("p",[t._v("encoding 是 Go 语言标准库中的一个包,用于处理数据的编码和解码。")]),t._v(" "),a("p",[t._v("encoding 包提供了许多子包,每个子包都专门用于处理特定的数据编码格式")]),t._v(" "),a("ul",[a("li",[t._v("encoding/json:用于处理 JSON 数据的编码和解码。")]),t._v(" "),a("li",[t._v("encoding/xml:用于处理 XML 数据的编码和解码。")]),t._v(" "),a("li",[t._v("encoding/csv:用于处理 CSV (逗号分隔值) 数据的编码和解码。")]),t._v(" "),a("li",[t._v("encoding/base64:用于进行 base64 编码和解码。")]),t._v(" "),a("li",[t._v("encoding/hex:用于进行十六进制编码和解码。")]),t._v(" "),a("li",[t._v("encoding/gob:用于进行 Go 对象的编码和解码。")])]),t._v(" "),a("h3",{attrs:{id:"encoding-ascii85"}},[t._v("encoding/ascii85")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/encoding/ascii85")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"encoding/ascii85"')]),t._v("\n")])])]),a("p",[t._v("encoding/ascii85 用于进行 ASCII85 编码和解码")]),t._v(" "),a("h3",{attrs:{id:"encoding-asn1"}},[t._v("encoding/asn1")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/encoding/asn1")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"encoding/asn1"')]),t._v("\n")])])]),a("p",[t._v("encoding/asn1 用于进行 ASN.1 (Abstract Syntax Notation One) 编码和解码。")]),t._v(" "),a("h3",{attrs:{id:"encoding-base32"}},[t._v("encoding/base32")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/encoding/base32")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"encoding/base32"')]),t._v("\n")])])]),a("h3",{attrs:{id:"encoding-base64"}},[t._v("encoding/base64")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/encoding/base64")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"encoding/base64"')]),t._v("\n")])])]),a("p",[t._v("encoding/base32 用于进行 Base32 编码和解码")]),t._v(" "),a("h3",{attrs:{id:"encoding-binary"}},[t._v("encoding/binary")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/encoding/binary")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"encoding/binary"')]),t._v("\n")])])]),a("p",[t._v("encoding/base64 用于进行 Base64 编码和解码。")]),t._v(" "),a("h3",{attrs:{id:"encoding-csv"}},[t._v("encoding/csv")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/encoding/csv")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"encoding/csv"')]),t._v("\n")])])]),a("p",[t._v("encoding/csv 用于处理 CSV (逗号分隔值) 格式的数据。")]),t._v(" "),a("h3",{attrs:{id:"encoding-gob"}},[t._v("encoding/gob")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/encoding/gob")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"encoding/gob"')]),t._v("\n")])])]),a("p",[t._v("encoding/gob 用于将 Go 的值编码为二进制格式,并进行序列化和反序列化。")]),t._v(" "),a("h3",{attrs:{id:"encoding-hex"}},[t._v("encoding/hex")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/encoding/hex")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"encoding/hex"')]),t._v("\n")])])]),a("p",[t._v("十六进制编码将二进制数据编码为十六进制字符串,每个字节对应两个十六进制字符。十六进制编码通常用于将二进制数据转换为可打印的 ASCII 字符串,例如在 URL 参数中传递二进制数据或在文本文件中嵌入二进制数据。")]),t._v(" "),a("p",[t._v("encoding/hex 包提供了一组函数和类型,用于进行十六进制编码和解码。主要有两个函数:EncodeToString 和 DecodeString。")]),t._v(" "),a("h3",{attrs:{id:"encoding-json"}},[t._v("encoding/json")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/encoding/json")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"encoding/json"')]),t._v("\n")])])]),a("p",[t._v("encoding/json 用于对 JSON (JavaScript Object Notation) 格式的数据进行编码和解码操作。")]),t._v(" "),a("p",[t._v("JSON 是一种常用的数据交换格式,用于在不同平台和编程语言之间传输和存储数据。JSON 数据由键值对组成,可以表示复杂的数据结构和层次关系。")]),t._v(" "),a("h3",{attrs:{id:"encoding-pem"}},[t._v("encoding/pem")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/encoding/pem")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"encoding/pem"')]),t._v("\n")])])]),a("p",[t._v("encoding/pem 用于对 PEM (Privacy-Enhanced Mail) 格式的数据进行编码和解码操作。")]),t._v(" "),a("p",[t._v("PEM 是一种常用的文本格式,用于在非文本环境中传输和存储密钥、证书等数据。PEM 格式使用 ASCII 字符表示二进制数据,通常以 “-----BEGIN…” 和 “-----END…” 标记来标识不同类型的数据。")]),t._v(" "),a("h3",{attrs:{id:"encoding-xml"}},[t._v("encoding/xml")]),t._v(" "),a("p",[t._v("https://pkg.go.dev/encoding/xml")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"encoding/xml"')]),t._v("\n")])])]),a("p",[t._v("encoding/xml 用于对 XML (eXtensible Markup Language) 格式的数据进行编码和解码操作。")]),t._v(" "),a("p",[t._v("XML 是一种常用的文本格式,用于存储和传输结构化的数据。XML 数据由标签、属性和文本内容组成,可以表示复杂的数据结构和层次关系。")]),t._v(" "),a("h2",{attrs:{id:"errors"}},[t._v("errors")]),t._v(" "),a("h2",{attrs:{id:"expvar"}},[t._v("expvar")]),t._v(" "),a("h2",{attrs:{id:"flag"}},[t._v("flag")]),t._v(" "),a("h2",{attrs:{id:"fmt"}},[t._v("fmt")]),t._v(" "),a("h2",{attrs:{id:"go"}},[t._v("go")]),t._v(" "),a("h3",{attrs:{id:"go-ast"}},[t._v("go/ast")]),t._v(" "),a("h3",{attrs:{id:"go-build"}},[t._v("go/build")]),t._v(" "),a("h4",{attrs:{id:"go-build-constraint"}},[t._v("go/build/constraint")]),t._v(" "),a("h3",{attrs:{id:"go-constant"}},[t._v("go/constant")]),t._v(" "),a("h3",{attrs:{id:"go-doc"}},[t._v("go/doc")]),t._v(" "),a("h4",{attrs:{id:"go-doc-comment"}},[t._v("go/doc/comment")]),t._v(" "),a("h3",{attrs:{id:"go-format"}},[t._v("go/format")]),t._v(" "),a("h3",{attrs:{id:"go-importer"}},[t._v("go/importer")]),t._v(" "),a("h3",{attrs:{id:"go-parser"}},[t._v("go/parser")]),t._v(" "),a("h3",{attrs:{id:"go-printer"}},[t._v("go/printer")]),t._v(" "),a("h3",{attrs:{id:"go-scanner"}},[t._v("go/scanner")]),t._v(" "),a("h3",{attrs:{id:"go-token"}},[t._v("go/token")]),t._v(" "),a("h3",{attrs:{id:"go-types"}},[t._v("go/types")]),t._v(" "),a("h2",{attrs:{id:"hash"}},[t._v("hash")]),t._v(" "),a("h3",{attrs:{id:"hash-adler32"}},[t._v("hash/adler32")]),t._v(" "),a("h3",{attrs:{id:"hash-crc32"}},[t._v("hash/crc32")]),t._v(" "),a("h3",{attrs:{id:"hash-crc64"}},[t._v("hash/crc64")]),t._v(" "),a("h3",{attrs:{id:"hash-fnv"}},[t._v("hash/fnv")]),t._v(" "),a("h3",{attrs:{id:"hash-maphash"}},[t._v("hash/maphash")]),t._v(" "),a("h2",{attrs:{id:"html"}},[t._v("html")]),t._v(" "),a("h3",{attrs:{id:"html-template"}},[t._v("html/template")]),t._v(" "),a("h2",{attrs:{id:"image"}},[t._v("image")]),t._v(" "),a("h3",{attrs:{id:"image-color"}},[t._v("image/color")]),t._v(" "),a("h4",{attrs:{id:"image-color-palette"}},[t._v("image/color/palette")]),t._v(" "),a("h3",{attrs:{id:"image-draw"}},[t._v("image/draw")]),t._v(" "),a("h3",{attrs:{id:"image-gif"}},[t._v("image/gif")]),t._v(" "),a("h3",{attrs:{id:"image-jpeg"}},[t._v("image/jpeg")]),t._v(" "),a("h3",{attrs:{id:"png"}},[t._v("png")]),t._v(" "),a("h2",{attrs:{id:"index"}},[t._v("index")]),t._v(" "),a("h3",{attrs:{id:"index-suffixarray"}},[t._v("index/suffixarray")]),t._v(" "),a("h2",{attrs:{id:"io"}},[t._v("io")]),t._v(" "),a("h3",{attrs:{id:"io-fs"}},[t._v("io/fs")]),t._v(" "),a("h3",{attrs:{id:"io-ioutil"}},[t._v("io/ioutil")]),t._v(" "),a("h2",{attrs:{id:"log"}},[t._v("log")]),t._v(" "),a("h3",{attrs:{id:"log-slog"}},[t._v("log/slog")]),t._v(" "),a("h3",{attrs:{id:"log-syslog"}},[t._v("log/syslog")]),t._v(" "),a("h2",{attrs:{id:"maps"}},[t._v("maps")]),t._v(" "),a("h2",{attrs:{id:"math"}},[t._v("math")]),t._v(" "),a("h3",{attrs:{id:"math-big"}},[t._v("math/big")]),t._v(" "),a("h3",{attrs:{id:"math-bits"}},[t._v("math/bits")]),t._v(" "),a("h3",{attrs:{id:"math-cmplx"}},[t._v("math/cmplx")]),t._v(" "),a("h3",{attrs:{id:"math-rand"}},[t._v("math/rand")]),t._v(" "),a("h2",{attrs:{id:"mime"}},[t._v("mime")]),t._v(" "),a("h3",{attrs:{id:"mime-multipart"}},[t._v("mime/multipart")]),t._v(" "),a("h3",{attrs:{id:"mime-quotedprintable"}},[t._v("mime/quotedprintable")]),t._v(" "),a("h2",{attrs:{id:"net"}},[t._v("net")]),t._v(" "),a("h3",{attrs:{id:"net-http"}},[t._v("net/http")]),t._v(" "),a("h4",{attrs:{id:"net-http-cgi"}},[t._v("net/http/cgi")]),t._v(" "),a("h4",{attrs:{id:"net-http-cookiejar"}},[t._v("net/http/cookiejar")]),t._v(" "),a("h4",{attrs:{id:"net-http-fcgi"}},[t._v("net/http/fcgi")]),t._v(" "),a("h4",{attrs:{id:"net-http-httptest"}},[t._v("net/http/httptest")]),t._v(" "),a("h4",{attrs:{id:"net-http-httptrace"}},[t._v("net/http/httptrace")]),t._v(" "),a("h4",{attrs:{id:"net-http-httputil"}},[t._v("net/http/httputil")]),t._v(" "),a("h4",{attrs:{id:"net-http-pprof"}},[t._v("net/http/pprof")]),t._v(" "),a("h3",{attrs:{id:"net-mail"}},[t._v("net/mail")]),t._v(" "),a("h3",{attrs:{id:"net-netip"}},[t._v("net/netip")]),t._v(" "),a("h3",{attrs:{id:"net-rpc"}},[t._v("net/rpc")]),t._v(" "),a("h4",{attrs:{id:"net-rpc-jsonrpc"}},[t._v("net/rpc/jsonrpc")]),t._v(" "),a("h3",{attrs:{id:"net-smtp"}},[t._v("net/smtp")]),t._v(" "),a("h3",{attrs:{id:"net-textproto"}},[t._v("net/textproto")]),t._v(" "),a("h3",{attrs:{id:"net-url"}},[t._v("net/url")]),t._v(" "),a("h2",{attrs:{id:"os"}},[t._v("os")]),t._v(" "),a("h3",{attrs:{id:"os-exec"}},[t._v("os/exec")]),t._v(" "),a("h3",{attrs:{id:"os-signal"}},[t._v("os/signal")]),t._v(" "),a("h3",{attrs:{id:"os-user"}},[t._v("os/user")]),t._v(" "),a("h2",{attrs:{id:"path"}},[t._v("path")]),t._v(" "),a("h3",{attrs:{id:"path-filepath"}},[t._v("path/filepath")]),t._v(" "),a("h2",{attrs:{id:"plugin"}},[t._v("plugin")]),t._v(" "),a("h2",{attrs:{id:"reflect"}},[t._v("reflect")]),t._v(" "),a("h2",{attrs:{id:"regexp"}},[t._v("regexp")]),t._v(" "),a("h3",{attrs:{id:"regexp-syntax"}},[t._v("regexp/syntax")]),t._v(" "),a("h2",{attrs:{id:"runtime"}},[t._v("runtime")]),t._v(" "),a("h3",{attrs:{id:"runtime-cgo"}},[t._v("runtime/cgo")]),t._v(" "),a("h3",{attrs:{id:"runtime-coverage"}},[t._v("runtime/coverage")]),t._v(" "),a("h3",{attrs:{id:"runtime-debug"}},[t._v("runtime/debug")]),t._v(" "),a("h3",{attrs:{id:"runtime-metrics"}},[t._v("runtime/metrics")]),t._v(" "),a("h3",{attrs:{id:"runtime-pprof"}},[t._v("runtime/pprof")]),t._v(" "),a("h3",{attrs:{id:"runtime-race"}},[t._v("runtime/race")]),t._v(" "),a("h3",{attrs:{id:"runtime-trace"}},[t._v("runtime/trace")]),t._v(" "),a("h2",{attrs:{id:"slices"}},[t._v("slices")]),t._v(" "),a("h2",{attrs:{id:"sort"}},[t._v("sort")]),t._v(" "),a("h2",{attrs:{id:"strconv"}},[t._v("strconv")]),t._v(" "),a("h2",{attrs:{id:"strings"}},[t._v("strings")]),t._v(" "),a("h2",{attrs:{id:"sync"}},[t._v("sync")]),t._v(" "),a("h3",{attrs:{id:"sync-atomic"}},[t._v("sync/atomic")]),t._v(" "),a("h2",{attrs:{id:"syscall"}},[t._v("syscall")]),t._v(" "),a("h3",{attrs:{id:"sync-js"}},[t._v("sync/js")]),t._v(" "),a("h2",{attrs:{id:"testing"}},[t._v("testing")]),t._v(" "),a("h3",{attrs:{id:"testing-fstest"}},[t._v("testing/fstest")]),t._v(" "),a("h3",{attrs:{id:"testing-iotest"}},[t._v("testing/iotest")]),t._v(" "),a("h3",{attrs:{id:"testing-quick"}},[t._v("testing/quick")]),t._v(" "),a("h3",{attrs:{id:"testing-slogtest"}},[t._v("testing/slogtest")]),t._v(" "),a("h2",{attrs:{id:"text"}},[t._v("text")]),t._v(" "),a("h3",{attrs:{id:"text-scanner"}},[t._v("text/scanner")]),t._v(" "),a("h3",{attrs:{id:"text-tabwriter"}},[t._v("text/tabwriter")]),t._v(" "),a("h3",{attrs:{id:"text-template"}},[t._v("text/template")]),t._v(" "),a("h4",{attrs:{id:"template-parse"}},[t._v("template/parse")]),t._v(" "),a("h2",{attrs:{id:"time"}},[t._v("time")]),t._v(" "),a("h3",{attrs:{id:"time-tzdata"}},[t._v("time/tzdata")]),t._v(" "),a("h2",{attrs:{id:"unicode"}},[t._v("unicode")]),t._v(" "),a("h3",{attrs:{id:"unicode-utf16"}},[t._v("unicode/utf16")]),t._v(" "),a("h3",{attrs:{id:"unicode-utf8"}},[t._v("unicode/utf8")]),t._v(" "),a("h2",{attrs:{id:"unsafe"}},[t._v("unsafe")])])}),[],!1,null,null,null);s.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/52.9dfffd13.js b/assets/js/52.9dfffd13.js new file mode 100644 index 000000000..820472b8b --- /dev/null +++ b/assets/js/52.9dfffd13.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[52],{483:function(t,e,n){"use strict";n.r(e);var s=n(36),i=Object(s.a)({},(function(){var t=this.$createElement,e=this._self._c||t;return e("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}},[e("h1",{attrs:{id:"encoding-hex"}},[this._v("encoding/hex")]),this._v(" "),e("h2",{attrs:{id:"参考资料"}},[this._v("参考资料")]),this._v(" "),e("ul",[e("li",[this._v("https://pkg.go.dev/encoding/hex@go1.17.8#DecodeString")])])])}),[],!1,null,null,null);e.default=i.exports}}]); \ No newline at end of file diff --git a/assets/js/53.c98436b4.js b/assets/js/53.c98436b4.js new file mode 100644 index 000000000..4d3846803 --- /dev/null +++ b/assets/js/53.c98436b4.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[53],{482:function(t,s,i){"use strict";i.r(s);var n=i(36),e=Object(n.a)({},(function(){var t=this.$createElement,s=this._self._c||t;return s("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}},[s("h1",{attrs:{id:"fmt"}},[this._v("fmt")]),this._v(" "),s("h2",{attrs:{id:"参考资料"}},[this._v("参考资料")]),this._v(" "),s("ul",[s("li",[this._v("https://studygolang.com/topics/2594/comment/7252")])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/54.3fb40b8b.js b/assets/js/54.3fb40b8b.js new file mode 100644 index 000000000..e32ad142b --- /dev/null +++ b/assets/js/54.3fb40b8b.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[54],{486:function(t,a,s){"use strict";s.r(a);var n=s(36),r=Object(n.a)({},(function(){var t=this,a=t.$createElement,s=t._self._c||a;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h1",{attrs:{id:"sync"}},[t._v("sync")]),t._v(" "),s("h2",{attrs:{id:"sync-map"}},[t._v("sync.Map")]),t._v(" "),s("h3",{attrs:{id:"sync-map-的底层数据"}},[t._v("sync.Map 的底层数据")]),t._v(" "),s("p",[t._v("sync.Map 适合在读多写少的场景下使用,sync.Map 的核心思想是读写分离,以及用空间换时间。")]),t._v(" "),s("p",[t._v("有两个数据结构:")]),t._v(" "),s("p",[s("code",[t._v("dirty")]),t._v(" 数据结构")]),t._v(" "),s("div",{staticClass:"language-go extra-class"},[s("pre",{pre:!0,attrs:{class:"language-go"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Map "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 互斥锁")]),t._v("\n mu Mutex\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 原子操作")]),t._v("\n read atomic"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Value\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// dirty数据 ")]),t._v("\n dirty "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("entry\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 标记tag 表示数据每次从diry中往read中迁移一个就+1")]),t._v("\n misses "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[s("code",[t._v("read")]),t._v(" 数据结构")]),t._v(" "),s("div",{staticClass:"language-go extra-class"},[s("pre",{pre:!0,attrs:{class:"language-go"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" readOnly "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n m "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("entry\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 标记:false 证明数据不只是read中有,dirty也有")]),t._v("\n amended "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" \n")])])]),s("p",[t._v("读数据是优先从 read 中读 (注意,read 是只读的数据结构,所以不加锁),如果 read 不包含这个数据,会从 dirty 中读取 (注意,从 dirty 查找的时候会加锁),并 misses+1,直到 misses 大于 dirty,开始迁移数据,数据一次性从 dirty 传到 read 中。")]),t._v(" "),s("p",[t._v("因为 read 没有锁,所以才说,sync.Map 读的时候效率高。")]),t._v(" "),s("p",[t._v("当新增的时候,从 read 和 dirty 中查找是否用数据,在 read 有数据且没有被标记为清除就无锁覆盖原来的值。这个时候开始加锁了,如果在 read 中虽然有数据,但是被标记为清除了,并且 dirty 没有数据,就加入到 diry 中,如果这个时候 read 有数据,虽然被被标记为删除了,dirty 也有数据,那么直接原子更新数据即可。如果数据不在 read,在 dirty 中,直接在 dirty 中更新即可,如果数据不在 read 中也不再 dirty 中,如果这个时候 read.amended 是 false (意思就是 dirty 空了),"),s("strong",[t._v("那么就必须将数据一点一点从 read 中复制到 dirty 中")]),t._v(",如果是 true 就不用复制了,在完成这个工序后,再把数据放置在 dirty 中。")]),t._v(" "),s("p",[t._v("删除的时候,就先从 read 中查找,有了就原子性质的将其标记为 nil,并不是真的删,没有就加锁从 dirty 中找,有了就删。这个时候是真删,如果都没有就返回。")]),t._v(" "),s("p",[t._v("在写多读少的时候,涉及到 read 中无数据就会频繁的加锁去 dirty 中查找、read 和 dirty 交换等开销,比常规的 map 加锁性能更差。因为普通的只是加锁,sync.Map 不仅仅是加锁,还得复制数据,这花销就更大了。")]),t._v(" "),s("p",[t._v("总结:"),s("strong",[t._v("高并发,读多写少可以使用 sync.Map,如果读少写多,不要使用 sync.Map,比常规锁+map 更慢")])]),t._v(" "),s("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),s("ul",[s("li",[t._v("https://zhuanlan.zhihu.com/p/353440086")])])])}),[],!1,null,null,null);a.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/55.7d9bf41e.js b/assets/js/55.7d9bf41e.js new file mode 100644 index 000000000..d35d97e48 --- /dev/null +++ b/assets/js/55.7d9bf41e.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[55],{484:function(t,o,i){"use strict";i.r(o);var _=i(36),v=Object(_.a)({},(function(){var t=this,o=t.$createElement,i=t._self._c||o;return i("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[i("h1",{attrs:{id:"go-编程范式"}},[t._v("go 编程范式")]),t._v(" "),i("h2",{attrs:{id:"创建模式"}},[t._v("创建模式")]),t._v(" "),i("ul",[i("li",[i("RouterLink",{attrs:{to:"/工程/go编程范式/创建者/"}},[t._v("创建者")])],1),t._v(" "),i("li",[i("RouterLink",{attrs:{to:"/工程/go编程范式/工厂方法/"}},[t._v("工厂方法")])],1),t._v(" "),i("li",[i("RouterLink",{attrs:{to:"/工程/go编程范式/对象池/"}},[t._v("对象池")])],1),t._v(" "),i("li",[i("RouterLink",{attrs:{to:"/工程/go编程范式/单例/"}},[t._v("单例")])],1),t._v(" "),i("li",[i("RouterLink",{attrs:{to:"/工程/go编程范式/原型/"}},[t._v("原型")])],1)]),t._v(" "),i("h2",{attrs:{id:"结构模式"}},[t._v("结构模式")]),t._v(" "),i("ul",[i("li",[i("RouterLink",{attrs:{to:"/工程/go编程范式/装饰器/"}},[t._v("装饰器")])],1),t._v(" "),i("li",[i("RouterLink",{attrs:{to:"/工程/go编程范式/代理/"}},[t._v("代理")])],1)]),t._v(" "),i("h2",{attrs:{id:"行为模式"}},[t._v("行为模式")]),t._v(" "),i("ul",[i("li",[i("RouterLink",{attrs:{to:"/工程/go编程范式/观察者/"}},[t._v("观察者")])],1),t._v(" "),i("li",[i("RouterLink",{attrs:{to:"/工程/go编程范式/策略/"}},[t._v("策略")])],1)]),t._v(" "),i("h2",{attrs:{id:"同步模式"}},[t._v("同步模式")]),t._v(" "),i("ul",[i("li",[i("RouterLink",{attrs:{to:"/工程/go编程范式/信号量/"}},[t._v("信号量")])],1)]),t._v(" "),i("h2",{attrs:{id:"并发模式"}},[t._v("并发模式")]),t._v(" "),i("ul",[i("li",[i("RouterLink",{attrs:{to:"/工程/go编程范式/有界并行/"}},[t._v("有界并行")])],1),t._v(" "),i("li",[i("RouterLink",{attrs:{to:"/工程/go编程范式/生成器/"}},[t._v("生成器")])],1),t._v(" "),i("li",[i("RouterLink",{attrs:{to:"/工程/go编程范式/并行/"}},[t._v("并行")])],1)]),t._v(" "),i("h2",{attrs:{id:"消息传递模式"}},[t._v("消息传递模式")]),t._v(" "),i("ul",[i("li",[i("RouterLink",{attrs:{to:"/工程/go编程范式/扇入/"}},[t._v("扇入")])],1),t._v(" "),i("li",[i("RouterLink",{attrs:{to:"/工程/go编程范式/扇出/"}},[t._v("扇出")])],1),t._v(" "),i("li",[i("RouterLink",{attrs:{to:"/工程/go编程范式/发布者订阅者/"}},[t._v("发布者订阅者")])],1)]),t._v(" "),i("h2",{attrs:{id:"稳定性模式"}},[t._v("稳定性模式")]),t._v(" "),i("ul",[i("li",[i("RouterLink",{attrs:{to:"/工程/go编程范式/断路器/"}},[t._v("断路器")])],1)]),t._v(" "),i("h2",{attrs:{id:"分析模式"}},[t._v("分析模式")]),t._v(" "),i("ul",[i("li",[i("RouterLink",{attrs:{to:"/工程/go编程范式/定时函数/"}},[t._v("定时函数")])],1)]),t._v(" "),i("h2",{attrs:{id:"函数式编程模式"}},[t._v("函数式编程模式")]),t._v(" "),i("ul",[i("li",[i("RouterLink",{attrs:{to:"/工程/go编程范式/功能选项/"}},[t._v("功能选项")])],1),t._v(" "),i("li",[i("RouterLink",{attrs:{to:"/工程/go编程范式/k8s_visitor/"}},[t._v("k8s visitor")])],1),t._v(" "),i("li",[i("RouterLink",{attrs:{to:"/工程/go编程范式/pipeline/"}},[t._v("pipeline")])],1)]),t._v(" "),i("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),i("ul",[i("li",[t._v("https://github.com/shgopher/go-patterns")]),t._v(" "),i("li",[t._v("https://time.geekbang.org/column/article/386238")]),t._v(" "),i("li",[t._v("https://mp.weixin.qq.com/s/Sv-SXDYxea_K_TtiI_CjTw")]),t._v(" "),i("li",[t._v("https://mp.weixin.qq.com/s/KRgNwJt1C7q2ckeqCu9pCQ")])])])}),[],!1,null,null,null);o.default=v.exports}}]); \ No newline at end of file diff --git a/assets/js/56.4127148d.js b/assets/js/56.4127148d.js new file mode 100644 index 000000000..db9a23491 --- /dev/null +++ b/assets/js/56.4127148d.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[56],{485:function(t,s,e){"use strict";e.r(s);var i=e(36),n=Object(i.a)({},(function(){var t=this.$createElement,s=this._self._c||t;return s("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}},[s("h1",{attrs:{id:"k8s-visitor"}},[this._v("k8s visitor")])])}),[],!1,null,null,null);s.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/57.ba1b4c58.js b/assets/js/57.ba1b4c58.js new file mode 100644 index 000000000..0041bf3a3 --- /dev/null +++ b/assets/js/57.ba1b4c58.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[57],{487:function(t,e,n){"use strict";n.r(e);var s=n(36),i=Object(s.a)({},(function(){var t=this.$createElement,e=this._self._c||t;return e("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}},[e("h1",{attrs:{id:"pipeline"}},[this._v("pipeline")])])}),[],!1,null,null,null);e.default=i.exports}}]); \ No newline at end of file diff --git a/assets/js/58.d6c32d73.js b/assets/js/58.d6c32d73.js new file mode 100644 index 000000000..b5c2b5d6c --- /dev/null +++ b/assets/js/58.d6c32d73.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[58],{488:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/59.a733d97b.js b/assets/js/59.a733d97b.js new file mode 100644 index 000000000..679024ca0 --- /dev/null +++ b/assets/js/59.a733d97b.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[59],{489:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/6.39778f4f.js b/assets/js/6.39778f4f.js new file mode 100644 index 000000000..ecf7e9599 --- /dev/null +++ b/assets/js/6.39778f4f.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[6],{426:function(t,r,e){t.exports=e.p+"assets/img/wechat.4a3030a4.png"},441:function(t,r,e){"use strict";e.r(r);var a=e(36),E=Object(a.a)({},(function(){var t=this,r=t.$createElement,a=t._self._c||r;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("p",{attrs:{align:"left"}},[t._v("\nhey~,我是科科,目前就职于国内一家互联网公司,你们可以加我"),a("a",{attrs:{href:"#wechat.png"}},[t._v("微信")]),t._v(",交个朋友吧~\n")]),t._v(" "),a("br"),t._v(" "),a("p",{attrs:{align:"center"}},[a("a",{attrs:{href:"#wechat.png",target:"_blank"}},[a("img",{attrs:{src:"https://img.shields.io/static/v1?label=%E7%A7%91%E7%A7%91%E4%BA%BA%E7%A5%9E&message=%E5%85%AC%E4%BC%97%E5%8F%B7&color="}})]),t._v(" "),a("a",{attrs:{href:"https://space.bilibili.com/478621088",target:"_blank"}},[a("img",{attrs:{src:"https://img.shields.io/static/v1?label=bilibili&message=b%E7%AB%99&color=blue"}})]),t._v(" "),a("a",{attrs:{href:"https://www.zhihu.com/people/shgopher",target:"_blank"}},[a("img",{attrs:{src:"https://img.shields.io/static/v1?label=zhihu&message=%E7%9F%A5%E4%B9%8E&color=blue"}})]),t._v(" "),a("a",{attrs:{href:"https://blog.csdn.net/zyfljxzby",target:"_blank"}},[a("img",{attrs:{src:"https://img.shields.io/static/v1?label=csdn&message=CSDN&color=red"}})]),t._v(" "),a("a",{attrs:{href:"https://www.toutiao.com/c/user/token/MS4wLjABAAAAIGeO1-kCUelF-G8GW3AvJlrEL7tiO24WHJmnX4nV1bs",target:"_blank"}},[a("img",{attrs:{src:"https://img.shields.io/static/v1?label=toutiao&message=%E5%A4%B4%E6%9D%A1&color=red"}})])]),t._v(" "),a("p",[t._v("添加微信公众号:"),a("a",{attrs:{href:"#wechat.png"}},[t._v("科科人神")]),t._v(",回复:")]),t._v(" "),a("ul",[a("li",[a("p",[a("code",[t._v("面试题")]),t._v(",获取经典 go 面试大全 (巨全,强推,不仅有 go 面试题还有大厂的算法和系统设计原题以及面试技巧等等内容)")])]),t._v(" "),a("li",[a("p",[a("code",[t._v("好友")]),t._v(",可以添加作者的微信好友")])])]),t._v(" "),a("h2",{attrs:{id:"赞助作者"}},[t._v("赞助作者")]),t._v(" "),a("ul",[a("li",[a("a",{attrs:{href:"https://justmysocks.net/members/aff.php?aff=29885",target:"_blank",rel:"noopener noreferrer"}},[t._v("just-my-socks 机场"),a("OutboundLink")],1),t._v("不怕 ip 被 ban,每月 500GB 流量,2.5 Gbps 带宽,支持 5 台设备,价格 5 刀/月。可支付宝付款!")]),t._v(" "),a("li",[a("a",{attrs:{href:"https://app.cloudcone.com/?ref=2525",target:"_blank",rel:"noopener noreferrer"}},[t._v("cloudcone 服务器"),a("OutboundLink")],1),t._v("主打一个性价比高,每月 3T 流量,带宽 1 GB/s,服务器地址美国洛杉矶,价格 5 刀/月。可支付宝付款!")])]),t._v(" "),a("h2",{attrs:{id:"基础"}},[t._v("基础")]),t._v(" "),a("ul",[a("li",[a("RouterLink",{attrs:{to:"/基础/helloWorld/"}},[t._v("hello world")])],1),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%9F%BA%E7%A1%80/%E5%8F%98%E9%87%8F%E5%A3%B0%E6%98%8E"}},[t._v("变量声明")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%9F%BA%E7%A1%80/%E5%B8%B8%E9%87%8F%E5%A3%B0%E6%98%8E"}},[t._v("常量声明")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%9F%BA%E7%A1%80/%E9%9B%B6%E5%80%BC"}},[t._v("零值")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%9F%BA%E7%A1%80/%E5%A4%8D%E5%90%88%E5%AD%97%E9%9D%A2%E9%87%8F"}},[t._v("复合字面量")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%9F%BA%E7%A1%80/%E6%95%B0%E5%AD%97%E7%B1%BB%E5%9E%8B"}},[t._v("数字类型")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%9F%BA%E7%A1%80/%E7%BB%93%E6%9E%84%E4%BD%93"}},[t._v("结构体")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%9F%BA%E7%A1%80/slice"}},[t._v("slice")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%9F%BA%E7%A1%80/string"}},[t._v("string")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%9F%BA%E7%A1%80/map"}},[t._v("map")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%9F%BA%E7%A1%80/%E6%B1%82%E5%80%BC%E9%A1%BA%E5%BA%8F"}},[t._v("求值顺序")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%9F%BA%E7%A1%80/%E4%BD%9C%E7%94%A8%E5%9F%9F"}},[t._v("作用域")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%9F%BA%E7%A1%80/%E5%87%BD%E6%95%B0%E6%96%B9%E6%B3%95"}},[t._v("函数方法")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%9F%BA%E7%A1%80/interface"}},[t._v("接口")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%9F%BA%E7%A1%80/%E9%80%BB%E8%BE%91%E5%92%8C%E5%88%A4%E6%96%AD%E8%AF%AD%E5%8F%A5"}},[t._v("逻辑和判断语句")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%9F%BA%E7%A1%80/%E6%B3%9B%E5%9E%8B"}},[t._v("泛型")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%9F%BA%E7%A1%80/%E9%94%99%E8%AF%AF%E5%A4%84%E7%90%86"}},[t._v("错误处理")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%9F%BA%E7%A1%80/%E5%85%B6%E4%BB%96%E5%86%85%E5%AE%B9"}},[t._v("其他内容")])])]),t._v(" "),a("h2",{attrs:{id:"并发"}},[t._v("并发")]),t._v(" "),a("ul",[a("li",[a("a",{attrs:{href:"./%E5%B9%B6%E5%8F%91/%E5%86%85%E5%AD%98%E6%A8%A1%E5%9E%8B"}},[t._v("内存模型")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B9%B6%E5%8F%91/%E5%B9%B6%E5%8F%91%E6%A8%A1%E5%9E%8B"}},[t._v("并发模型")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B9%B6%E5%8F%91/%E5%90%8C%E6%AD%A5%E5%8E%9F%E8%AF%AD"}},[t._v("同步原语")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B9%B6%E5%8F%91/channel"}},[t._v("channel")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B9%B6%E5%8F%91/context"}},[t._v("context")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B9%B6%E5%8F%91/atomic"}},[t._v("atomic")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B9%B6%E5%8F%91/%E5%B9%B6%E5%8F%91%E4%BC%98%E5%8C%96"}},[t._v("并发优化")])])]),t._v(" "),a("h2",{attrs:{id:"runtime"}},[t._v("runtime")]),t._v(" "),a("ul",[a("li",[a("a",{attrs:{href:"./runtime/%E4%B8%89%E8%89%B2gc%E7%AE%97%E6%B3%95"}},[t._v("三色 gc 算法")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./runtime/gmp"}},[t._v("G:M:P")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./runtime/%E5%A0%86%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D"}},[t._v("堆内存分配")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./runtime/%E6%A0%88%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86"}},[t._v("栈内存管理")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./runtime/%E7%B3%BB%E7%BB%9F%E7%9B%91%E6%8E%A7"}},[t._v("系统监控")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./runtime/netpool"}},[t._v("netpool")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./runtime/%E5%AE%9A%E6%97%B6%E5%99%A8"}},[t._v("定时器")])])]),t._v(" "),a("h2",{attrs:{id:"编译器"}},[t._v("编译器")]),t._v(" "),a("ul",[a("li",[a("a",{attrs:{href:"./%E7%BC%96%E8%AF%91%E5%99%A8/gc"}},[t._v("gc")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E7%BC%96%E8%AF%91%E5%99%A8/llvm"}},[t._v("llvm")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E7%BC%96%E8%AF%91%E5%99%A8/gccgo"}},[t._v("gccgo")])]),t._v(" "),a("li",[a("RouterLink",{attrs:{to:"/编译器/实现一个编程语言/"}},[t._v("实现一个编程语言")])],1)]),t._v(" "),a("h2",{attrs:{id:"工程"}},[t._v("工程")]),t._v(" "),a("ul",[a("li",[a("RouterLink",{attrs:{to:"/工程/go编程范式/"}},[t._v("go 编程范式/设计模式")])],1),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B7%A5%E7%A8%8B/%E5%8C%85%E5%8F%8A%E5%85%B6%E6%9E%84%E5%BB%BA%E5%B7%A5%E5%85%B7"}},[t._v("包管理工具")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B7%A5%E7%A8%8B/log"}},[t._v("日志")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B7%A5%E7%A8%8B/%E6%B5%8B%E8%AF%95"}},[t._v("测试")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B7%A5%E7%A8%8B/%E5%8A%A8%E6%80%81%E8%B0%83%E8%AF%95"}},[t._v("动态调试")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B7%A5%E7%A8%8B/cgo"}},[t._v("cgo")])]),t._v(" "),a("li",[a("RouterLink",{attrs:{to:"/工程/内存对齐实践/"}},[t._v("内存对齐实践")])],1),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B7%A5%E7%A8%8B/%E4%BB%A3%E7%A0%81%E6%A3%80%E6%9F%A5"}},[t._v("代码检查")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B7%A5%E7%A8%8B/%E5%8F%8D%E5%B0%84"}},[t._v("反射")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B7%A5%E7%A8%8B/goweb%E7%BC%96%E7%A8%8B"}},[t._v("go web 编程")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B7%A5%E7%A8%8B/wasm"}},[t._v("wasm")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B7%A5%E7%A8%8B/ioc"}},[t._v("ioc")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B7%A5%E7%A8%8B/%E5%91%BD%E4%BB%A4"}},[t._v("命令")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B7%A5%E7%A8%8B/%E5%AD%97%E7%AC%A6%E7%BC%96%E7%A0%81"}},[t._v("字符编码")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B7%A5%E7%A8%8B/%E7%B3%BB%E7%BB%9F%E4%BF%A1%E5%8F%B7%E7%9A%84%E5%A4%84%E7%90%86"}},[t._v("系统信号的处理")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B7%A5%E7%A8%8B/go%E8%AF%AD%E8%A8%80%E7%9A%84%E7%A7%98%E5%AF%86%E5%AD%A6"}},[t._v("go 语言的密码学")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B7%A5%E7%A8%8B/%E4%BC%98%E7%A7%80%E7%AC%AC%E4%B8%89%E6%96%B9%E5%8C%85"}},[t._v("优秀第三方包")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B7%A5%E7%A8%8B/go%E6%A0%87%E5%87%86%E5%BA%93"}},[t._v("go 标准库")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B7%A5%E7%A8%8B/%E9%A1%B9%E7%9B%AE%E7%BB%84%E7%BB%87%E5%BD%A2%E5%BC%8F"}},[t._v("go 项目组织形式")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B7%A5%E7%A8%8B/go%E5%91%BD%E5%90%8D%E6%83%AF%E4%BE%8B"}},[t._v("go 命名惯例")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B7%A5%E7%A8%8B/go%E8%AF%AD%E8%A8%80%E8%A7%84%E8%8C%83"}},[t._v("go 语言规范")])]),t._v(" "),a("li",[a("a",{attrs:{href:"./%E5%B7%A5%E7%A8%8B/go%E9%9D%A2%E8%AF%95%E9%A2%98"}},[t._v("go 面试题")])])]),t._v(" "),a("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),a("ul",[a("li",[a("a",{attrs:{href:"https://go.dev",target:"_blank",rel:"noopener noreferrer"}},[t._v("go.dev"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://github.com/golang/go",target:"_blank",rel:"noopener noreferrer"}},[t._v("go src"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://www.gopl.io/",target:"_blank",rel:"noopener noreferrer"}},[t._v("the go programming language"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://book.douban.com/subject/35720729/",target:"_blank",rel:"noopener noreferrer"}},[t._v("GO 语言精进之路"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://shgopher.github.io/pdf/mastering-go-cn.pdf",target:"_blank",rel:"noopener noreferrer"}},[t._v("Mastering Go"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://draveness.me/golang/",target:"_blank",rel:"noopener noreferrer"}},[t._v("Go 语言设计与实现"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://book.douban.com/subject/36104087/",target:"_blank",rel:"noopener noreferrer"}},[t._v("深度探索 GO 语言"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://juejin.cn/post/7241452578125824061",target:"_blank",rel:"noopener noreferrer"}},[t._v("我为什么放弃 GO 语言?"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://github.com/teivah/100-go-mistakes",target:"_blank",rel:"noopener noreferrer"}},[t._v("100-go-mistakes"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://time.geekbang.org/opencourse/intro/100069501",target:"_blank",rel:"noopener noreferrer"}},[t._v("Go 语言编程模式实战"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://time.geekbang.org/column/intro/100061801",target:"_blank",rel:"noopener noreferrer"}},[t._v("Go 并发编程实战课"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://time.geekbang.org/column/intro/100079601",target:"_blank",rel:"noopener noreferrer"}},[t._v("Go 语言项目开发实战"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://time.geekbang.org/column/intro/100013101",target:"_blank",rel:"noopener noreferrer"}},[t._v("Go 语言核心 36 讲"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://time.geekbang.org/column/intro/100090601",target:"_blank",rel:"noopener noreferrer"}},[t._v("手把手带你写一个 Web 框架"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://coding.imooc.com/class/chapter/180.html#Anchor",target:"_blank",rel:"noopener noreferrer"}},[t._v("Google 资深工程师深度讲解 Go 语言"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://github.com/LeoYang90/Golang-Internal-Notes",target:"_blank",rel:"noopener noreferrer"}},[t._v("Golang-Internal-Notes"),a("OutboundLink")],1)]),t._v(" "),a("li",[a("a",{attrs:{href:"https://space.bilibili.com/373073810",target:"_blank",rel:"noopener noreferrer"}},[t._v("刘丹冰 Aceld bilibili"),a("OutboundLink")],1)])]),t._v(" "),a("h2",{attrs:{id:"扫一扫添加我的公众号-回复-加群-可以加入微信群。"}},[t._v("扫一扫添加我的公众号,回复 “加群”,可以加入微信群。")]),t._v(" "),a("p",{attrs:{id:"wechat.png",align:"center"}},[a("br"),t._v(" "),a("br"),t._v(" "),a("img",{attrs:{src:e(426),alt:"公众号搜:科科人神"}})]),t._v(" "),a("h2",{attrs:{id:"star"}},[t._v("star")]),t._v(" "),a("p",[a("a",{attrs:{href:"https://starchart.cc/shgopher/GOFamily",target:"_blank",rel:"noopener noreferrer"}},[a("img",{attrs:{src:"https://starchart.cc/shgopher/GOFamily.svg",alt:"Stargazers over time"}}),a("OutboundLink")],1)]),t._v(" "),a("h2",{attrs:{id:"证书"}},[t._v("证书")]),t._v(" "),a("p",[a("a",{attrs:{rel:"license",href:"http://creativecommons.org/licenses/by/3.0/"}},[a("img",{staticStyle:{"border-width":"0"},attrs:{alt:"知识共享许可协议",src:"https://i.creativecommons.org/l/by/3.0/88x31.png"}})]),a("br"),t._v("本作品采用"),a("a",{attrs:{rel:"license",href:"http://creativecommons.org/licenses/by/3.0/"}},[t._v("知识共享署名 3.0 未本地化版本许可协议")]),t._v("进行许可。")])])}),[],!1,null,null,null);r.default=E.exports}}]); \ No newline at end of file diff --git a/assets/js/60.9d927a66.js b/assets/js/60.9d927a66.js new file mode 100644 index 000000000..a17070816 --- /dev/null +++ b/assets/js/60.9d927a66.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[60],{492:function(t,s,e){"use strict";e.r(s);var n=e(36),r=Object(n.a)({},(function(){var t=this.$createElement,s=this._self._c||t;return s("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}},[s("h1",{attrs:{id:"创建者模式"}},[this._v("创建者模式")])])}),[],!1,null,null,null);s.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/61.155a3987.js b/assets/js/61.155a3987.js new file mode 100644 index 000000000..dce5f1bf5 --- /dev/null +++ b/assets/js/61.155a3987.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[61],{490:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/62.3ecc77bf.js b/assets/js/62.3ecc77bf.js new file mode 100644 index 000000000..b4d811433 --- /dev/null +++ b/assets/js/62.3ecc77bf.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[62],{493:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/63.87a8106f.js b/assets/js/63.87a8106f.js new file mode 100644 index 000000000..8fb1fdc47 --- /dev/null +++ b/assets/js/63.87a8106f.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[63],{491:function(t,s,e){"use strict";e.r(s);var n=e(36),r=Object(n.a)({},(function(){var t=this.$createElement,s=this._self._c||t;return s("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}},[s("h1",{attrs:{id:"原型模式"}},[this._v("原型模式")])])}),[],!1,null,null,null);s.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/64.3240a531.js b/assets/js/64.3240a531.js new file mode 100644 index 000000000..d9d335e36 --- /dev/null +++ b/assets/js/64.3240a531.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[64],{494:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/65.c6f6ac65.js b/assets/js/65.c6f6ac65.js new file mode 100644 index 000000000..ab5389835 --- /dev/null +++ b/assets/js/65.c6f6ac65.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[65],{495:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/66.949483d3.js b/assets/js/66.949483d3.js new file mode 100644 index 000000000..cc4fd883b --- /dev/null +++ b/assets/js/66.949483d3.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[66],{496:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/67.613f77e3.js b/assets/js/67.613f77e3.js new file mode 100644 index 000000000..e4558768d --- /dev/null +++ b/assets/js/67.613f77e3.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[67],{497:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/68.42cf41b2.js b/assets/js/68.42cf41b2.js new file mode 100644 index 000000000..38dc01bbd --- /dev/null +++ b/assets/js/68.42cf41b2.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[68],{498:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/69.a1051de8.js b/assets/js/69.a1051de8.js new file mode 100644 index 000000000..d9096a77f --- /dev/null +++ b/assets/js/69.a1051de8.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[69],{499:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/7.34b0a1c7.js b/assets/js/7.34b0a1c7.js new file mode 100644 index 000000000..c2b8a51be --- /dev/null +++ b/assets/js/7.34b0a1c7.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[7],{427:function(t,s,a){t.exports=a.p+"assets/img/hash.b54950a0.svg"},451:function(t,s,a){"use strict";a.r(s);var n=a(36),p=Object(n.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"map"}},[t._v("map")]),t._v(" "),n("p",[t._v("导读:")]),t._v(" "),n("ul",[n("li",[t._v("map 的基本操作")]),t._v(" "),n("li",[t._v("map 基础知识")]),t._v(" "),n("li",[t._v("map 底层知识")]),t._v(" "),n("li",[t._v("iussues")])]),t._v(" "),n("h2",{attrs:{id:"map-的基本操作"}},[t._v("map 的基本操作")]),t._v(" "),n("p",[t._v("因为 go 使用拉链法去构造 go 语言的 map,所以只要内存不被消耗光,map 中的元素是无限的。跟 slice 不同,map 不分 lenth 和 cap,在 make 中"),n("strong",[t._v("只有")]),t._v("一个位置,这个位置表示的含义就是 cap")]),t._v(" "),n("p",[t._v("map 的基础操作")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 声明一个新的map")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" m "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 给引用类型map分配底层数据结构")]),t._v("\n\tm "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// or m = make(map[string]int,100)")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 插入数据")]),t._v("\n\tm"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ok "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" ok "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 在初始化的时候无法初始化两个一样的key,这个检查是编译器就开始的")]),t._v("\n\tm "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"a"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"a"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// error")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 在初始化的时候无法初始化两个一样的key,但是如果是一个变量的话是可以的,")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 因为编译器时无法获取变量的值的,所以这种方式通过。")]),t._v("\n\tm "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这样是正确的。")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 遍历数据")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" k"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" m "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("k"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('":"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 删除数据,不存在这个数据不会panic")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("delete")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 删除 map 所有条目")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("clear")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("h2",{attrs:{id:"map-基础知识"}},[t._v("map 基础知识")]),t._v(" "),n("p",[t._v("映射的第一步就是把键值转化为哈希值 (通常是一个很大的整数),然后根据哈希值的映射,把 key-value 本体,以及对应的哈希值存储在哈希筒中,go 使用链表法充当哈希筒,当寻找值的时候,通过哈希计算,以及取模映射,先查找到哈希筒,然后使用哈希值的方式去寻找有没有符合的哈希值,如果找到了,那么再使用 key 原来的值二次比较,这一步主要是为了规避哈希碰撞,即:俩 key 算出来的哈希值是一样的这种行为。")]),t._v(" "),n("p",[t._v("因为存 "),n("code",[t._v("==")]),t._v(" 这种行为,所以 go 语言 map 的键值是有局限的,"),n("strong",[t._v("不可以是函数类型、字典类型和切片类型")]),t._v(",如果是接口类型,传入的实际类型也不能是函数,字典和切片,例如")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// panic")]),t._v("\n\t\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"1"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("p",[t._v("**虽然,map 的 key 可以设置为接口,但是最好不要这么干,因为会在运行时引入风险,比如上述代码就是风险。同样的,如果 key 是数组类型,亦或者是 struct,参数值都不能是函数,切片和 map。**无论被埋藏的有多深,都不能出现切片,map,函数这几种类型,比如说 "),n("code",[t._v("[10][2][]string")]),t._v(",运行时都会看出来。")]),t._v(" "),n("p",[t._v("那么用什么类型的作为 key 值是比较推荐的呢?")]),t._v(" "),n("p",[t._v("这里有两个关键词:求哈希的速度,判断相等的速度,基本原理是越简单的类型速度越快,比如 bool,int8,就比 int64 快,因为 int8 单个值只占了一个字节,int64 是 8 个字节,所以越复杂的越慢,比如一个 struct,因为求一个 struct 的哈希值,需要对里面的字段都进行哈希计算,然后合并起来,大大影响了速度,所以优先选用数值类型和指针类型 (因为指针类型也就是一个 16 进制的正整数而已),如果选 string,最好短一些的比较好")]),t._v(" "),n("p",[t._v("在内存不爆炸的情况下,map 的 key 是无限量的,随意添加。")]),t._v(" "),n("p",[t._v("map 有一个最佳实践是使用形如 "),n("code",[t._v("value,ok := map[key]")]),t._v(" 的 “comma OK” 的方法去获取 map 的值,OK 的意义就是为了获得 key 是否存在这个 map 里,因为就算不存在,map 也不会报错或者 Panic,返回的是这个值的零值,比如 int 就返回 0,那如果某一个 key 刚好结果是 0 就说不清了对吧,所以引入了这个 “comma ok” 机制,另外还存在一个不存在也不会 Panic 的操作,就是使用 "),n("code",[t._v("delete(map,key)")]),t._v(" 的方法去删除 key 值")]),t._v(" "),n("p",[t._v("map 的遍历跟 slice 一样,使用 m := range map 的方法,但是输出的顺序是不固定的,如果想输出稳定的值,可以将 range 改成普通的 for 循环,然后 key 值使用一个切片存储,这样的话,读取 key 值的时候顺序就是固定的了。")]),t._v(" "),n("h2",{attrs:{id:"map-底层知识"}},[t._v("map 底层知识")]),t._v(" "),n("p",[t._v("当 map 使用字面量的方式进行初始化的时候,数量小于25时,它会首先启用 make 关键字创建一个 map,然后使用 "),n("code",[t._v('m["k"] = v')]),t._v(" 的方式进行初始化,当超过 25 的时候,其实也差不多,它会使用两个切片分别存储 key 和 value,然后使用 "),n("code",[t._v("m[k] = v")]),t._v(" 的方式进行初始化,实际上 go 的复合类型使用字面量进行初始化基本上都是这种方式,基本上的流程就是使用关键字创建底层数据,然后使用最简单的方式进行 k-v 赋值,所以你也可以把字面量的赋值当作一种语法糖。")]),t._v(" "),n("p",[t._v("map 的语法在运行时会转化为另一套对应关系,这个转化是在编译器搞定的。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[t._v("m "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" m "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" runtime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("makemap")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("maptype"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// maptype 下文有解释")]),t._v("\n\nv "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" runtime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("mapaccess1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("maptype"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("m "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v('// 这里实际上引入的是 "hello"的 指针')]),t._v("\n\nv"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("ok "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" v"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("ok "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" runtime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("mapacess2")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("maptype"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("m "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\nm"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" v "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" runtime"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("mapassign")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("maptype"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("m "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("delete")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("m"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" runtiem"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("mapdelete")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("maptype"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("m "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n")])])]),n("p",[t._v("hmap 是一个 struct,这个结构体拥有众多字段,它用来描述这个 map 底层应用具有的所有字段。可以理解为它是描述 map 的头部文件,存储了所有的字段,但是并不存储真实的 body。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" hmap "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tcount "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" \n\tflags "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint8")]),t._v("\n\tB "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint8")]),t._v(" \n\tnoverflow "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint16")]),t._v(" \n\thash0 "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint32")]),t._v(" \n\tbuckets unsafe"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Pointer "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 重点")]),t._v("\n\toldbuckets unsafe"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Pointer \n\tnevacuate "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uintptr")]),t._v(" \n\textra "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("mapextra \n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" mapextra "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\toverflow "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("bmap\n\toldoverflow "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("bmap\n\tnextOverflow "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("bmap\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这是一个桶本身的数据结构,但是在运行时阶段会重塑这个结构体,添加更多的字段")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" bmap "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ttophash "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("bucketCnt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint8")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这是添加字段以后的样子")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 所以三个数组就完成了数据的存储,也可以避免padding的发生")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" bmap "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n topbits "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("8")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint8")]),t._v("\n keys "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("8")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("keytype\n values "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("8")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("valuetype\n pad "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uintptr")]),t._v("\n overflow "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uintptr")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("ul",[n("li",[t._v("count:当前 map 的 value 个数,len 返回的就是这个值")]),t._v(" "),n("li",[t._v("flags:map 的状态标志 iterator oldterator hashwriting samesizegrow")]),t._v(" "),n("li",[t._v("B:"),n("code",[t._v("2 ^ B = 桶数量")])]),t._v(" "),n("li",[t._v("noverflow:指的是 overflow 的桶的数量")]),t._v(" "),n("li",[t._v("hash0:哈希函数的种子值,用作哈希函数的参数,引入随机性")]),t._v(" "),n("li",[t._v("buckets:指向桶数组的指针")]),t._v(" "),n("li",[t._v("oldbuckets:在 map 扩容阶段指向前一个桶的指针")]),t._v(" "),n("li",[t._v("nevacuate:map 扩容阶段充当扩容进度计数器,所有下标号小于 nevacuate 的桶都是已经完成了数据排空和迁移的操作的")]),t._v(" "),n("li",[t._v("extra:【此字段是可选字段】如果有 overflow 的桶出现,该字段保证 overflow 的桶不会被 gc,具体操作就是该字段存储所有指向 overflow 的桶的指针")])]),t._v(" "),n("p",[t._v("当声明一个 map 的时候,运行时就会为这个 map 具体的变量生成一个实例,上门那个是 map 的描述符号,这里说的这个数据结构是定义这个 map 中所有元信息的。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" maptype "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ttyp _type\n\tkey "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("_type\n\telem "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("_type\n\tbucket "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("_type "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 表示hash bucket 内部的类型")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// function for hashing keys (ptr to key, seed) -> hash")]),t._v("\n\thasher "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("unsafe"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Pointer"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uintptr")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uintptr")]),t._v("\n\tkeysize "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint8")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// size of key slot")]),t._v("\n\telemsize "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint8")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// size of elem slot")]),t._v("\n\tbucketsize "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint16")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// size of bucket")]),t._v("\n\tflags "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint32")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("go 的 map 只有一套 method,只要使用这个 maptype 的具体不同实现就可以满足操作。比如 map[int]int 和 map[int]string,他们都使用 maptype 来定义自己的元信息,但是操作这这个元信息的函数是一个,只要取不同的 typ key 这种指针类型取操作就可以了。")]),t._v(" "),n("p",[t._v("存储数据的 body 本体是由一个 "),n("code",[t._v("类似二维数组")]),t._v(" + "),n("code",[t._v("链表")]),t._v(" 组成的,具体的图像如下:")]),t._v(" "),n("p",[n("img",{attrs:{src:a(427),alt:""}})]),t._v(" "),n("p",[t._v("tophash 区域就是为了寻找 key 和 value 使用空间换时间的原理做的索引,当我们把 hashcode 的高位值逐一比较的时候就可以确定 key 和 value 的位置。")]),t._v(" "),n("p",[t._v("key 和 value 都是连续的内存区域,key 和 value 在一个桶中只能存在 8 个,多余的在不满足扩容的情况下就会存储在溢出桶中,寻找 key 和 value 使用 tophash 的位置即可,go 使用分开存储 tophash,key,value 的方式,所以 go 就避免了数据 padding,满足了内存对齐,避免了内存的浪费。需要注意的是,当 key 和 value 大于 128 个字节的时候,存储的就是他们的指针")]),t._v(" "),n("p",[t._v("map 的扩容有一个具体的衡量指标,负载因子,当 hmap 中的 "),n("code",[t._v("count > 负载因子 * 2^B")]),t._v(" (map 的负载因子为 6.5),或者溢出桶过多的时候,就会扩容。当因为溢出桶太多的时候,创建的新 map 的桶和现有的桶一样,当因为不满足负载因子导致的扩容时,会创建两倍于现有 bucket 的新 bucket,但是旧的桶 data 并不会立刻被迁移到新的 bucket 中,在 map 进行插入和删除的过程中旧的内容会被逐步迁移到新的桶中。原来旧的内容就会被 gc 掉。")]),t._v(" "),n("p",[t._v("普通 map 的扩容并不是原子性的,所以 map 的扩容过程会去检查是否已经处于扩容中。")]),t._v(" "),n("p",[t._v("当持续向 map 中写入数据,并且删除,然后继续写入删除,这个时候如果没有造成装载因子的超出,就会造成溢出桶过多的情况,这个时候就会造成内存的泄漏,所以会创建一个一样大的 map,存储没有被删除的数据,并且将那些被标记为 delete 的数据进行垃圾回收,没错,你进行 delete 的时候并没有真的 delete,只有 gc 以后才是真的 delete。")]),t._v(" "),n("p",[t._v("将旧的数据放在 runtime.hmap 中的 oldbuckets 字段上,然后将新的数据结构放在 buckets 上,溢出桶的设置也是一样的,因为 extra 指向的数据结构也是三个字段,老的,新的,正在使用的。")]),t._v(" "),n("p",[t._v("运行时会将一个旧桶的数据分流到两个新的桶子里,但是如果是内存泄漏的等量扩容时,就只会把一个旧桶的数据导入到一个新的桶子里,请注意这里的迁移指的是拷贝,在计数器计算数据已经被分流完全以后,旧的桶和旧的溢出桶的数据就会被 gc 掉")]),t._v(" "),n("p",[t._v("因为存在旧桶和新桶,所以在查找数据的时候,会先从旧桶查找数据,如果没有再去新的桶中查找,当我们写入和删除数据的时候,除了写入的新数据到新的桶中,也会把旧的桶中的一部分数据拷贝到新的桶中,当然了不会拷贝全部,这是为了效率考虑的,")]),t._v(" "),n("h2",{attrs:{id:"issues"}},[t._v("issues")]),t._v(" "),n("p",[n("code",[t._v("问题一:")]),t._v(" "),n("strong",[t._v("map 元素可以取地址吗?")])]),t._v(" "),n("p",[n("RouterLink",{attrs:{to:"/基础/其他内容/#go可寻址类型"}},[t._v("不能")]),t._v(",map 元素 (例如 ma [“12”]) 属于结果值,所以无法获取地址")],1),t._v(" "),n("p",[n("code",[t._v("问题二:")]),t._v(" "),n("strong",[t._v("map 可以并发读写吗?可以 recover 吗?")])]),t._v(" "),n("p",[t._v("map 是线程不安全类型,读写得加互斥锁;被 recover 的 Panic 有几项是不能的:")]),t._v(" "),n("ul",[n("li",[t._v("数据竞争 (比如:"),n("strong",[t._v("对 map 进行并发读写")]),t._v(")")])]),t._v(" "),n("blockquote",[n("p",[t._v("可以通过 go 的编译标记 race 对代码进行检测是否存在数据竞争")])]),t._v(" "),n("ul",[n("li",[t._v("内存不足出现的 Panic")]),t._v(" "),n("li",[t._v("死锁出现的 Panic")])]),t._v(" "),n("p",[n("code",[t._v("问题三:")]),t._v(" "),n("strong",[t._v("sync.Map 适合的场景,和 map 加锁的区别")])]),t._v(" "),n("p",[t._v("sync.Map 在读多写少性能比较好,否则并发性能很差")]),t._v(" "),n("blockquote",[n("p",[t._v("有关 sync.Map 的"),n("RouterLink",{attrs:{to:"/工程/go标准库/sync.html#syncmap"}},[t._v("详细内容")])],1)]),t._v(" "),n("p",[t._v("map 不支持并发的读或者是写 (go1.6 以后就不支持并发的读和写了,之前的版本支持并发的读,但是不支持并发的写),所以 map+锁性能在读多写少和读少写多,读和写一样多的情况下是一样的。")]),t._v(" "),n("p",[t._v("最优解是使用多把锁即:分段锁的方式,并发读写,大幅提高性能")]),t._v(" "),n("p",[n("code",[t._v("问题四:")]),t._v(" "),n("strong",[t._v("在值为 nil 的字典上执行读操作会成功吗,那写操作呢?")])]),t._v(" "),n("p",[t._v("答案是在值为 nil 的字典写会 Panic,读是没问题的。")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 结果是int的一个初始值 0 ")]),t._v("\n a"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// panic")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),n("p",[n("code",[t._v("问题五:")]),t._v(" "),n("strong",[t._v("为什么不用向下寻址式?")])]),t._v(" "),n("p",[t._v("我们知道解决哈希碰撞的问题有向下寻址法,链表法,这是因为哈希函数只能把数据尽可能分布的均匀,如果哈希函数的输出的范围大于输入的范围,这是不现实的,这就要求映射无穷多,这显然不可能,所以必然会有两个不同的 key 算出来的哈希值是相同的,那么如果很多 key 算出的哈希值都是一样的,这就出现了查找效率从 O(1) 下降到了 O(n) 这就是所谓的哈希冲突。")]),t._v(" "),n("blockquote",[n("p",[t._v("这里提到的哈希值一样,有可能是后几位一样,也就是部分一样,比如后 9 位相同")])]),t._v(" "),n("p",[t._v("向下寻址法的意思是:依次向下一位去探究是否是要找的哈希对,当然插入的时候也是这,向没有数据的地方插入,所以这种方法必须要使用数组这种结构,而且遍历的时候还得使用循环数组的这种思想 "),n("code",[t._v('index := hash("author") % array.len')]),t._v(",开放寻址法有一个数据指标叫做装载因子,就是说元素的个数/数组长度,一旦装载因子大于 70 %乃至 90 % 基本上就倒退为了 O(n) 的时间复杂度,并且底层是数组的情况下,必须使用连续的内存地址,并且数组长度是有限的,并且大概率会发生内存 padding 的情况,因为 kv 要存储在一起,这就又造成了更大的浪费。")]),t._v(" "),n("p",[t._v("总结一下:不使用向下寻址使用拉链法的原因在于,1 可以利用碎片式的内存,2 不用内存 padding 造成浪费 3 原则上链表长度无限,可以无限增加。")]),t._v(" "),n("p",[n("code",[t._v("问题六:")]),t._v(" "),n("strong",[t._v("map 如何操作真缩容?")])]),t._v(" "),n("p",[t._v("可以使用 cap 重新生成一个 map,然后使用遍历的方式将老的 map 数据导入到新的小的 map 中,如果你知道数据不会再次增大的情况下是可以这么做的。")]),t._v(" "),n("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),n("ul",[n("li",[t._v("https://github.com/golang/go/")]),t._v(" "),n("li",[t._v("https://blog.csdn.net/EDDYCJY/article/details/120465701")])])])}),[],!1,null,null,null);s.default=p.exports}}]); \ No newline at end of file diff --git a/assets/js/70.ae50c48a.js b/assets/js/70.ae50c48a.js new file mode 100644 index 000000000..0e27e2620 --- /dev/null +++ b/assets/js/70.ae50c48a.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[70],{500:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/71.7e2f9033.js b/assets/js/71.7e2f9033.js new file mode 100644 index 000000000..cd89eb455 --- /dev/null +++ b/assets/js/71.7e2f9033.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[71],{501:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/72.4cc3186e.js b/assets/js/72.4cc3186e.js new file mode 100644 index 000000000..cf1e3cfb4 --- /dev/null +++ b/assets/js/72.4cc3186e.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[72],{502:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/73.7fe20629.js b/assets/js/73.7fe20629.js new file mode 100644 index 000000000..b185c0599 --- /dev/null +++ b/assets/js/73.7fe20629.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[73],{504:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/74.68c618b1.js b/assets/js/74.68c618b1.js new file mode 100644 index 000000000..712d854f5 --- /dev/null +++ b/assets/js/74.68c618b1.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[74],{503:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/75.d44fc2e7.js b/assets/js/75.d44fc2e7.js new file mode 100644 index 000000000..c2ec40c16 --- /dev/null +++ b/assets/js/75.d44fc2e7.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[75],{505:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/76.f53bf284.js b/assets/js/76.f53bf284.js new file mode 100644 index 000000000..3f76b7fb7 --- /dev/null +++ b/assets/js/76.f53bf284.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[76],{506:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/77.bad2588a.js b/assets/js/77.bad2588a.js new file mode 100644 index 000000000..2e10c97ea --- /dev/null +++ b/assets/js/77.bad2588a.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[77],{507:function(t,e,o){"use strict";o.r(e);var s=o(36),i=Object(s.a)({},(function(){var t=this,e=t.$createElement,o=t._self._c||e;return o("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[o("h1",{attrs:{id:"go-语言规范"}},[t._v("go 语言规范")]),t._v(" "),o("p",[t._v("总结了 go,google 官方以及腾讯的 go 语言规范。")]),t._v(" "),o("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),o("ul",[o("li",[t._v("https://go.dev/ref/spec")]),t._v(" "),o("li",[t._v("https://github.com/Tencent/secguide/blob/main/Go%E5%AE%89%E5%85%A8%E6%8C%87%E5%8D%97.md")]),t._v(" "),o("li",[t._v("https://google.github.io/styleguide/go/best-practices")]),t._v(" "),o("li",[t._v("https://colobu.com/2023/11/17/golang-quick-reference-top-20-best-coding-practices/")])])])}),[],!1,null,null,null);e.default=i.exports}}]); \ No newline at end of file diff --git a/assets/js/78.2ee31447.js b/assets/js/78.2ee31447.js new file mode 100644 index 000000000..04b0bc024 --- /dev/null +++ b/assets/js/78.2ee31447.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[78],{508:function(t,o,r){"use strict";r.r(o);var e=r(36),i=Object(e.a)({},(function(){var t=this,o=t.$createElement,r=t._self._c||o;return r("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[r("h1",{attrs:{id:"go-面试题"}},[t._v("go 面试题")]),t._v(" "),r("ul",[r("li",[r("RouterLink",{attrs:{to:"/工程/go面试题/中高级go面试题一.html"}},[t._v("中高级 go 面试题一")])],1)]),t._v(" "),r("h2",{attrs:{id:"按照公司排序"}},[t._v("按照公司排序")]),t._v(" "),r("h2",{attrs:{id:"最终总结出的面试题"}},[t._v("最终总结出的面试题")]),t._v(" "),r("ul",[r("li",[r("RouterLink",{attrs:{to:"/工程/go面试题/primary.html"}},[t._v("初级")])],1),t._v(" "),r("li",[r("RouterLink",{attrs:{to:"/工程/go面试题/intermediate.html"}},[t._v("中级")])],1),t._v(" "),r("li",[r("RouterLink",{attrs:{to:"/工程/go面试题/senior.html"}},[t._v("高级")])],1),t._v(" "),r("li",[r("RouterLink",{attrs:{to:"/工程/go面试题/architect.html"}},[t._v("架构师")])],1)])])}),[],!1,null,null,null);o.default=i.exports}}]); \ No newline at end of file diff --git a/assets/js/79.f5c3f20d.js b/assets/js/79.f5c3f20d.js new file mode 100644 index 000000000..015de86df --- /dev/null +++ b/assets/js/79.f5c3f20d.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[79],{509:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/8.706832d3.js b/assets/js/8.706832d3.js new file mode 100644 index 000000000..9bb3385c3 --- /dev/null +++ b/assets/js/8.706832d3.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[8],{428:function(t,s,n){t.exports=n.p+"assets/img/gofunc.32e2f0aa.svg"},465:function(t,s,n){"use strict";n.r(s);var a=n(36),p=Object(a.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"函数和方法"}},[t._v("函数和方法")]),t._v(" "),a("p",[t._v("重点内容前情提要")]),t._v(" "),a("ul",[a("li",[t._v("函数初始化的顺序")]),t._v(" "),a("li",[t._v("init 函数")]),t._v(" "),a("li",[t._v("defer 的运用")]),t._v(" "),a("li",[t._v("变长参数")]),t._v(" "),a("li",[t._v("类型嵌入来完成继承")]),t._v(" "),a("li",[t._v("define 类型的方法集合")]),t._v(" "),a("li",[t._v("类型别名的方法集合")]),t._v(" "),a("li",[t._v("函数式编程")])]),t._v(" "),a("p",[t._v("go 语言中函数的基本使用方法如下:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 函数的一般用法, go 拥有多个返回值")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 返回值具有变量的返回模式")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Post")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n value "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 函数作为value值赋值给一个变量变量")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" Get0 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"函数的初始化顺序"}},[t._v("函数的初始化顺序")]),t._v(" "),a("p",[a("img",{attrs:{src:n(428),alt:""}})]),t._v(" "),a("ul",[a("li",[t._v("main 函数是所有 go 可执行函数的起始位置")]),t._v(" "),a("li",[t._v("在 main 函数运行前,需要先运行导入子包的流程")]),t._v(" "),a("li",[t._v("在导包的过程中,最先运行的是最深层的子包")]),t._v(" "),a("li",[t._v("按照"),a("strong",[t._v("全局常量,全局变量,init 函数")]),t._v("的顺序进行初始化的操作")]),t._v(" "),a("li",[t._v("子包初始化之后,开始返回前一层的包中进行常量,全局变量,init 函数的初始化,图示流程非常清晰的画出了这个过程。")]),t._v(" "),a("li",[t._v("多个相同的包只会导入一次,并且取相同包不同版本的可满足的最小版本导入,例如 "),a("code",[t._v("v1.1.0 v1.2.0")]),t._v(",只导入 "),a("code",[t._v("v1.2.0")]),t._v(",不会默认导入最新的版本。")])]),t._v(" "),a("h2",{attrs:{id:"init-函数"}},[t._v("init 函数")]),t._v(" "),a("p",[t._v("在一个包中,以及在一个包的某个文件中,可以存在多个 init 函数,一般来说,同一个包的不同 file,按照文件字符串比较的 “从小到大” 的顺序先被编译 file 中的 init 先执行,同一个 file 中的 init 函数按照先后顺序执行,但是 go 语言规范告诉我们,不要依赖 init 的执行顺序。")]),t._v(" "),a("p",[t._v("init 函数无法主动执行,它的执行是系统自动执行的,所以显式的去调用 init 函数会发生报错。")]),t._v(" "),a("p",[t._v("通常来说,init 的目的就是系统的初始化,因为它一定会被执行,下面我们介绍一下 init 函数的几个用途。")]),t._v(" "),a("p",[a("em",[a("strong",[t._v("重置包级变量值。")])])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// src/context/context.go")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" closedchan "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("init")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("close")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("closedchan"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("我们的包级变量是一个 chan,并且需要它是一个已经关闭的 chan,我们使用 init 来确保它提前一定处于一个已关闭的状态。")]),t._v(" "),a("p",[a("em",[a("strong",[t._v("对包级变量进行初始化,保证其后续可用。")])])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n OutBox "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n InBox "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("init")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n OutBox "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n InBox "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[a("em",[a("strong",[t._v("init 函数中的注册模式。")])])]),t._v(" "),a("p",[t._v("这种模式在 go 中有两处经典的案例,其一是 "),a("code",[t._v("database/sql")]),t._v(" 包的使用,其二是 image 包的使用。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// databsae/sql 包的使用")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"database/sql"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"github.com/lib/pq"')]),t._v(" \n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n db"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" sql"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Open")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"postgres"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这里使用 "),a("code",[t._v("import _")]),t._v(" 就是只有导包的过程,并没有任何除了 init 函数之外的函数调用。那么这么做的原因是什么呢?")]),t._v(" "),a("p",[a("code",[t._v("工厂方法 + 导包的顺序")])]),t._v(" "),a("p",[t._v("下面我们看一下这两个包的源码")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// github.com/golnag/go/src/database/sql/sql.go")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" drivers "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("driver"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Driver"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Open")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("driverName"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" dataSourceName "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("DB"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tdriveri"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ok "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" drivers"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("driverName"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Register")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("name "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" driver driver"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Driver"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tdrivers"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" driver\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("ul",[a("li",[t._v("sql 包有一个全局的 map,这里存放了所有的 driver,这个 map 的 key 是 driver 的名字,value 是 driver.Driver 的接口类型,通常来说存入的都是实现了这个接口的动态类型。")]),t._v(" "),a("li",[t._v("open 函数会直接判断是否拥有含有这个 key 的 value 值")]),t._v(" "),a("li",[t._v("register 函数,也就是注册函数,这个函数通常由实现了 sql.driver 的数据库第三方库的 init 函数去进行最终的注册。")])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// github.com/lib/pq")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("init")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tsql"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Register")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"postgres"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Driver"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("ul",[a("li",[t._v("这里就是实现了 sql.Driver 的 pq.Driver 结构体进行了名为 “postgress” 的注册。")])]),t._v(" "),a("p",[t._v("下面我们进行整体的分析:")]),t._v(" "),a("ul",[a("li",[t._v("在 pq 包实现的时候,它引入了 “database/sql”,“database/sql/driver” 这两个包,它将实现的数据库实例注册到 sql 的 map 中")]),t._v(" "),a("li",[t._v("用户调用的时候,我们调用了 "),a("code",[t._v("sql.Open")]),t._v(" 使用不同的 name 就可以使用不同的数据库,这里是工厂方法设计模式的运用")])]),t._v(" "),a("p",[t._v("这种方法可以使 pq 这个包完全没有暴露到用户面前,仅需要使用 sql 包就可以间接的使用 pq 的代码,很好的做了隔绝。")]),t._v(" "),a("p",[t._v("下面看一下 image 包的运用。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"image"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"image/png"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"image/gif"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"image/jpeg"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"os"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n width"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("height"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("image")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Args"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("width"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" height"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("image")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n file"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Open")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" file"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Close")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n img"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" image"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Decode")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("file"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n b "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" img"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Bounds")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Max"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("X"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Max"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Y"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("我们发现,image.Decode(f) 应该就如同上文提到的 Open 函数一样的功能,也就是工厂方法中的工厂。")]),t._v(" "),a("p",[t._v("下面我们看一下 image/gif 包中的 init 函数:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//https://github.com/golang/go/blob/master/src/image/gif/reader.go")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("init")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\timage"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("RegisterFormat")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"gif"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"GIF8?a"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" Decode"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" DecodeConfig"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("可以看到,这个 gif 包也是调用了 image 包,并且通过 init 函数,将动态类型注册到了 image 提供的注册器中。")]),t._v(" "),a("p",[a("em",[a("strong",[t._v("init 函数中检查失败的处理方法。")])])]),t._v(" "),a("p",[t._v("使用 init 去检查某些数据的正确性,相当于做最后的质检工作,一旦发生了错误,直接使用 Panic,快速停掉程序。")]),t._v(" "),a("h2",{attrs:{id:"defer-函数"}},[t._v("defer 函数")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Open")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"test.txt"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("panic")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 此处就是 defer 函数")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Close")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" \n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("defer 函数有下面几个规则")]),t._v(" "),a("ul",[a("li",[t._v("defer 后面只能跟函数或者是方法")]),t._v(" "),a("li",[t._v("一个函数中的 defer 函数,会使用 “栈” 的方式运行")]),t._v(" "),a("li",[t._v("defer 后面的函数和方法,返回值会被直接舍弃")])]),t._v(" "),a("p",[t._v("下面看一下 defer 函数的几种用法")]),t._v(" "),a("p",[a("em",[a("strong",[t._v("配合 recover 函数处理 panic")])])]),t._v(" "),a("p",[t._v("在 go 里,如果要处理函数内的 panic,有且仅有一种方法,那就是使用 recover 函数。而且 recover 函数必须运行在 defer 之中。那么让我们看一下具体的代码实现")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("recover")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"recoverd"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("panic")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"panic"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[a("em",[a("strong",[t._v("修改函数的具名返回值")])])]),t._v(" "),a("p",[t._v("具名返回值,就是返回值带有变量的,比如:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("hi")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("b "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("y "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n x "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n y "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n x "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v("b\n y "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("b\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" \n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// hi(1,3) 返回值为 (5,-1)")]),t._v("\n")])])]),a("p",[t._v("在分析这段代码的时候,我们要牢记一句话,在具名返回值的函数中,go 的 return 并不会立刻 return,它会等待 defer 执行完毕后再真的 return。也就是说,它 return 的是 x y 的最后时刻。")]),t._v(" "),a("p",[t._v("那么,让我们看一下非具名的函数:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("hi")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("b "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v(" \n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" x"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("y "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n x "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n y "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"defer 会执行的"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n x "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v("b\n y "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("b\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" x"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// hi(1,3) ")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// output:")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// defer 会执行的 5 -1")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 4 -2")]),t._v("\n")])])]),a("p",[t._v("在非具名的函数中,return x,y 的时候就已经决定返回值的最终结果了,但是这个 defer 还是会执行,这个是真的,xy 的值确实也是在 defer 里改变了,但是 return 的是之前的中间量,defer 里面的 xy,并不会左右之前已经设定好的 x y 的复制值了。")]),t._v(" "),a("p",[t._v("所以输出的是 4 -2,而且 defer 里面的输出 “defer 会执行的 5 -1” 也执行了。")]),t._v(" "),a("p",[t._v("defer 函数虽然是先入后出,在所有的正式指令执行完成以后执行,但是它的变量初始化可是***顺序的***,这里强调一下,仅仅是变量的初始化是顺序执行,defer 函数是不会顺序执行的:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("hi")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" b "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tj "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// j 顺序的初始化数据了,")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// println却不会顺序执行!")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("j "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("j"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("j"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tj"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// output: 0")]),t._v("\n")])])]),a("p",[t._v("这里的 defer 顺序执行完变量的初始化,所以它里面的 j 变量完成了值的复制,为 0,那么下文的 j++ 就不会对上面的复制品起任何作用了,即便 prinln(j) 确实发生在 j++ 之后,总结一句话,defer 函数执行是最后,但是初始化是正常的先后顺序。")]),t._v(" "),a("p",[t._v("如果想得到 1 的答案,可以这么改:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("hi")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" b "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tj "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("j"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tj"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这里的 j "),a("strong",[t._v("就会")]),t._v("受到下面 j++的影响了。")]),t._v(" "),a("p",[t._v("还有一点要注意,defer 函数的作用域也是正常的作用域,也就是说,上文 hi 函数演示内容,j:= 0 必须声明在 defer 函数之前,虽然我们知道 defer 的内容最后执行,但是它也遵循正常的作用域。如果 j := 0 发生在 defer 之后,它就会无法找到这个变量。")]),t._v(" "),a("p",[t._v("看了上面那么多容易搞混的 defer 用法,这里要说明一下,正常的使用方法是这样的:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("hi")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("b "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("y "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n x "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n y "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n x "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v("b\n y "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("b\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" \n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("也就是我们第一种使用的惯例,它的效果就是最后改变返回值,这也是这几种方式中最常用的方式。")]),t._v(" "),a("p",[t._v("看了上文关于 defer 函数的初始化 int 类型参数以后,我们还得注意切片在 defer 函数初始化参数的问题,因为它更容易出现误判。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n s1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n s1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("6")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n s1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("s1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n s1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("6")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("ul",[a("li",[t._v("前者输出 1 2 3")]),t._v(" "),a("li",[t._v("后者输出 4 5 6")])]),t._v(" "),a("p",[t._v("我们分析一下:")]),t._v(" "),a("blockquote",[a("p",[a("strong",[t._v("❗️ 这个地方很容易搞错,望周知。")])])]),t._v(" "),a("p",[t._v("首先我们确定的一件事就是:"),a("strong",[t._v("defer 函数的参数初始化是顺序执行的")]),t._v(",它顺序执行之后,将数据保存在了一个专用的栈中。")]),t._v(" "),a("p",[t._v("所以,第一个函数中,v 就等于 []int {1,2,3},换言之,v 初始化的时候等于一个指向底层数据是 1 2 3 的数组,所以当 s1 重新指向一个新的数据 4 5 6 的时候,v 的数据并不会有任何的影响,它仍然执行的还是底层数据是 1 2 3 的数组,所以它的最终结果就是输出 1 2 3")]),t._v(" "),a("p",[t._v("第二个函数,在初始化阶段,v 正常的初始化,它的值是 s1 的地址,那么当下文中 s1 重新指向了一个新的底层数组为 4 5 6 的数据之后,v 的数据是一直跟着 s1 走的,因为它的本质是 s1 的地址,这把钥匙一直都没有变化,所以它的最终结果就是新的内容 4 5 6")]),t._v(" "),a("p",[t._v("最后提一嘴,defer 函数的性能消耗 (go 1.14+),跟不使用 defer 相比,几乎没有差距,所以放心大胆的使用 defer。")]),t._v(" "),a("p",[a("em",[a("strong",[t._v("输出调试信息")])])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("trace")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"prepare"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" s\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("un")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"out"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("a")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("un")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("trace")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"a"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"in a"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("b")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("un")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("trace")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"b"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"in b"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("a")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("b")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// prepare b")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// in b")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// prepare a")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// in a")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// out a")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// out b ")]),t._v("\n")])])]),a("p",[t._v("这是 go 文档提供的一个日志记录的例子,接下来我们分析一下")]),t._v(" "),a("ul",[a("li",[t._v("首先 b 的执行,b 中的 defer 函数 un 开始初始化,它的初始化就是执行 trace (“b”),所以最先执行的是 prepare b")]),t._v(" "),a("li",[t._v("然后开始执行 println (“in b”)")]),t._v(" "),a("li",[t._v("接下来执行 a,跟 b 一样,先执行初始化的 un 中的 trace (“a”) 函数,然后执行 println (“in a”)")]),t._v(" "),a("li",[t._v("然后开始执行 a 的 defer 函数,所以 a 的 out 先执行,(因为它后入,先出)")]),t._v(" "),a("li",[t._v("接下来最后执行的是 b 中的 defer,那么就是 out b")])]),t._v(" "),a("p",[a("em",[a("strong",[t._v("还原变量的旧值")])])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("init")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n oldfile "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" firstfile\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n firstfile "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" oldfile\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n firstfile "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"README.md"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//...")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[a("em",[a("strong",[t._v("我们知道,defer 后面只能放置函数,包括自匿名函数,或者是自定义的函数以及方法 (如果有返回值自动舍弃),那么 go 内置的函数是否可以放到 defer 后面执行呢?")])])]),t._v(" "),a("p",[t._v("答案是,部分内置函数可以直接放到 defer 后面,部分不能直接放到 defer 后面执行,需要放到一个匿名或者自定义函数中。")]),t._v(" "),a("ul",[a("li",[t._v("可以直接放到 defer 后面的函数有:close copy delete print recover")]),t._v(" "),a("li",[a("strong",[t._v("不能")]),t._v("直接放到 defer 后面的函数有:append cap len make new")])]),t._v(" "),a("p",[t._v("但是通常来说,我们使用 defer 的时候都是会跟一个匿名函数或者是自定义的函数,并不会直接跟一个内置函数。")]),t._v(" "),a("p",[t._v("比如直接跟匿名函数的:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("recover")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("panic")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"error"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("跟一个自定义函数的:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Open")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"README.md"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n \n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// defer 后面是一个方法,并且舍弃了这个方法的error参数")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Close")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h3",{attrs:{id:"defer-函数因为无法-return-造成的内存泄露-bug"}},[t._v("defer 函数因为无法 return 造成的内存泄露 bug")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("readFiles")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ch "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" path "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" ch "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tfile"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Open")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" file"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Close")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Do something with file")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("在这个案例中,defer 函数用于关闭一个文件的句柄,假设我们在读取文件的过程中发生了 bug,readFile 永远无法 return nil,那么这个 defer 函数就永远不会实行,就会造成内存的泄露。")]),t._v(" "),a("p",[t._v("改正的话,我们可以给 defer 函数 wrap 一层函数,在这个子函数中只要能 return,运行 defer 函数即可")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("readFiles")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ch "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("<-")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("chan")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" path "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" ch "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("readFile")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 我们设置了一个新的函数,只要在这个函数里可以正常return 即可")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("readFile")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tfile"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Open")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("path"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" file"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Close")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// Do something with file")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h3",{attrs:{id:"再次强调-有无返回值具体变量对于-defer-的影响"}},[t._v("再次强调:有无返回值具体变量对于 defer 的影响")]),t._v(" "),a("p",[t._v("有返回变量")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 13")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 12")]),t._v("\n fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Print")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("a")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("a")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\ti"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n\t\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//13")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ti"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n\ti "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("执行顺序是这样的:\n1。i 初始化为原始数据 0\n2。i++\n3。i+= 10\n4。i++\n5。fmt.Println(i+1) // 13\n6。return 这个时候最终的 i // 12")]),t._v(" "),a("p",[t._v("所以当拥有返回值变量的时候,return 返回的是最终的 i,就连 defer 中的 i 的变量也算上。")]),t._v(" "),a("p",[t._v("无返回变量")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 3")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 11")]),t._v("\n fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Print")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("a")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("a")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\ti"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n\t\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//3")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ti"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n\t\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("执行的顺序:\n1。i 初始化为 0\n2。i++\n3。执行 i + 10 并把这个数据记录到 return 上去,但是并不会真的 return\n4。i++\n5。fmt.Println(i+1)// 3\n6。defer 执行完毕以后,return 开始返回之前记录的那个值。")]),t._v(" "),a("p",[t._v("为什么在这个没有返回变量的时候,i 在 defer 中的变化不会影响返回值呢,因为返回值记录的那个值发生在 defer 之前,所以 defer 再将 i 变化也不会影响之前记录的那个值了,那个值是已经固定的了,它没有立即返回是因为要执行 defer,你也可以理解为")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("a")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\ti"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n\t\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//3")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ti"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n\ta "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("10")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" a\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("所以 a 的值是不会再受到 defer 中的 i 的变化的。")]),t._v(" "),a("h2",{attrs:{id:"变长参数"}},[t._v("变长参数")]),t._v(" "),a("p",[t._v("举个变长参数函数的例子:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),t._v("any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("n "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Fprintln")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Stdout"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("我们平时使用的 "),a("code",[t._v("fmt.Println(...any)")]),t._v(" 就是标准的变长参数的例子。")]),t._v(" "),a("p",[t._v("它的组织形式就是 "),a("code",[t._v("...")]),t._v(" + "),a("code",[t._v("类型")]),t._v(",比如 "),a("code",[t._v("...int")]),t._v(" "),a("code",[t._v("...string")])]),t._v(" "),a("ol",[a("li",[t._v("一个函数的参数中只能有一个变长参数,且必须为最后一位")]),t._v(" "),a("li",[t._v("变长参数在函数内部以 slice 的方式存在")]),t._v(" "),a("li",[t._v("变长参数只能接受两种形式的值,其一就是多个同样类型的值,例如 "),a("code",[t._v('fmt.Println("hi","there",12)')]),t._v(",或者直接接受一个 "),a("code",[t._v("slice...")]),t._v(",例如 "),a("code",[t._v('fmt.Println([]any{1, 2, "hi"}...)')]),t._v(",并且,这两种用法不能混用,比如 "),a("code",[t._v("fmt.Println(1,2,[]any{1,2}...)")]),t._v(" 这种混写的方法错误。")])]),t._v(" "),a("p",[t._v("any 类型和 string 类型是绝对的不同的两种类型,因为 any 是 interface {} 的别名 ("),a("code",[t._v("type any = interface{}")]),t._v("),string 类型虽然实现了空接口,但是它不是空接口类型,如果要转换成空接口,必须显式的转换:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ta "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tb "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("any")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[a("em",[a("strong",[t._v("go 语言在 append 字符串到[]byte 的时候提供了一个语法糖")])])]),t._v(" "),a("p",[a("code",[t._v("func append(slice []Type, elems ...Type) []Type")])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ta "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" b "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"ee"')]),t._v("\n\ta "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" b"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这种语法糖只适用于 append 内置函数。底层应该是帮你把字符串转化为了 "),a("code",[t._v("[]byte()")])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 类似这种转换")]),t._v("\n\ta "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" b "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"ee"')]),t._v("\n\tc "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("byte")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\ta "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("append")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" c"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("如果自己实现一个的时候不进行转换就会报错。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"123"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ❌")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v('//cannot use "123" (untyped string constant)')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// as []byte value in argument to get")]),t._v("\n\n")])])]),a("h3",{attrs:{id:"使用变长函数去模拟重载函数"}},[t._v("使用变长函数去模拟重载函数")]),t._v(" "),a("p",[t._v("重载函数就是同一个作用域下,可以有相同名称的函数,只不过他们的参数不同,go 语言是不支持这种类型的函数的。")]),t._v(" "),a("p",[t._v("类似这种")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("如果真的想使用这种重载函数,我们可以使用这种方法来间接实现:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" args "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),t._v("any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" args "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("switch")]),t._v(" v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int8")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int16")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("vi"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t\t\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("h3",{attrs:{id:"使用变长函数去实现默认参数"}},[t._v("使用变长函数去实现默认参数")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("CheckIn")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"shgopher"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("12")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"男"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("CheckIn")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"jackie"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("20")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"男"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"上海"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Contents "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tName "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\tAge "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\tSex "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\tAddress "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("CheckIn")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("arges "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),t._v("any"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Contents"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tc "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Contents"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tAddress"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Beijing "')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" k"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" arges "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("switch")]),t._v(" k "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t\tname"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ok "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("ok "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"name is not a string"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t\tc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Name "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" name\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t\tage"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ok "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("ok "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"age is not a int"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t\tc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Age "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" age\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t\tsex"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ok "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("ok "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"sex is not a string"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t\tc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Sex "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" sex\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("case")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t\taddress"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ok "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("ok "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"address is not a string"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t\t\tc"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Address "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" address\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("default")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v("\n\t\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Errorf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"unknown argument"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" c"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这段代码的意思就是先给定一个默认值,比如这里地址是默认值的,也即是它是可选的内容,鉴于这段代码标识的意义,这个可省略的一定是在最后一位的。")]),t._v(" "),a("h1",{attrs:{id:"方法"}},[t._v("方法")]),t._v(" "),a("p",[t._v("接下来,我们介绍方法,所谓方法,其实只是函数的一种语法糖,它跟函数并没有本质的区别,我们看一个简单的例子:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Student "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n year "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n addr "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Student"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("GetName")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("name "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" name\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("可以看到,这是一个定义在指针类型 "),a("code",[t._v("*student")]),t._v(" 上的方法 "),a("code",[t._v("GetName")]),t._v(",实际上,它完全等于函数的这种形态:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("GetName")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Student"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" name "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" name\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("go 语言本身对于方法的使用是很宽泛的:")]),t._v(" "),a("ul",[a("li",[t._v("指针类型的变量可以使用值类型的方法")]),t._v(" "),a("li",[t._v("值类型的变量可以使用指针类型的方法")])]),t._v(" "),a("p",[t._v("这跟后文要讲的接口绝对是差距极大,因为接口的要求非常严格,这个可以看接口和函数的对比。")]),t._v(" "),a("p",[t._v("值类型的变量可以使用指针类型的方法")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Student "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n year "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n addr "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Student"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("GetName")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("name "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" name\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s Student\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("GetName")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"张三"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("当值类型来使用指针类型的方法时,系统默认会调用这个值的指针,因此这是系统给予的语法糖。")]),t._v(" "),a("p",[t._v("指针类型的变量可以使用值类型的方法")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Student "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n year "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n addr "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s Student"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("GetName")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("name "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" name\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("new")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("Student"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("GetName")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"张三"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"out:"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//out:")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("当指针类型来使用值类型的方法时,系统默认会调用这个指针指向的值,因此这是系统给予的语法糖。")]),t._v(" "),a("p",[t._v("这个例子其实是错误的行为,因为 s 的方法是定义在值类型的,使用一个 s 去调用它上面的 GetName,改变的只是方法中,s 的***复制品上***的值,外部调用的这个 s,实际上是不会有任何的改变的,我们如果从实质出发 "),a("code",[t._v("func GetName(s Student, name string)")]),t._v(",就能看出来了。")]),t._v(" "),a("p",[t._v("跟函数以及全局变量,常量是一样的,方法也是首字母大写可以导出包,小写无法导出包。")]),t._v(" "),a("p",[t._v("这里有个细节要注意一下,不能跨越包去定义方法,go 语言不支持,比如,原生类型 "),a("code",[t._v("(int,map,slice,bool 等)")]),t._v(" 是无法提供方法的,例如")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ❌")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("所以通常来说,我们就定义一个底层为 int 的新类型才可以")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a A"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("那么我们总结一下关于方法的几个小细节:")]),t._v(" "),a("ul",[a("li",[t._v("方法首字母的大小写注定了是否可以导出包")]),t._v(" "),a("li",[t._v("不能跨越包去定义方法")]),t._v(" "),a("li",[t._v("每一个方法只能被一个类型去定义,不能俩类型去定义一个方法")]),t._v(" "),a("li",[a("em",[a("strong",[t._v("指针类型和接口类型不能作为方法的基底类型")])])])]),t._v(" "),a("p",[t._v("最后一条,我们详细展开一下:")]),t._v(" "),a("p",[t._v("以下定义是错误的:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ❌ 指针类型不能有自己的方法")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a A"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ✅ 类型的指针可以有自己的方法")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("A"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ❌ 接口类型不能有自己的方法")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("A"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// ❌ 以接口为基底的类型不能有自己的方法")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A xxInterface\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a A"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("h2",{attrs:{id:"类型嵌入来完成继承"}},[t._v("类型嵌入来完成继承")]),t._v(" "),a("p",[t._v("go 语言使用类型的嵌入来实现继承母体的对象以及对象上的方法。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" People "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("p "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("People"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Name")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("p"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("现在我们将类型嵌入到一个新的类型来实现继承:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Student "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n People\n address "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Student"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Address")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s Student\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Name")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Address")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("我们还可以重写继承的方法来完成重载操作:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Student"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Name")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("People"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Name")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"学生"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("当发生嵌入类型和本类型,字段重合的时候,优先调用本类型的字段,嵌入类型的只需要加上前缀就可以了。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Student "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n People\n name "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" People "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s Student\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"1"')]),t._v("\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("People"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"2"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("如果你不想直接嵌入,也可以在前面加上变量名称:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Student "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 不嵌入也可以")]),t._v("\n people People\n name "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" People "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s Student\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"1"')]),t._v("\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("people"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"2"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("不过,如果是不嵌入的方式,就无法直接调用方法了,需要加上前缀。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s Student\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("people"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("name")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("内置类型也可以作为字段直接嵌入到新类型中")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("不过使用的时候比较非常规了:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a1 a\n a1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),t._v("\n a1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"2"')]),t._v("\n fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("指针类型也可以直接嵌入:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("b\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" b "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a1 a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('""')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\tb"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("b"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("我们可以看到,直接嵌入的时候,其实是省略了写法。但是通常,int string,这种内置的类型我们都会指定一个变量给他们。例如常规写法:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n People\n name "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n year "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n b "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("b\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("无论嵌入的是值类型还是指针类型,函数都可以直接调用他们身上的方法:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" People "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n name "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("p "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("People"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Name")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("p"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Address "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n value "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Value")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n fmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("value"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Student "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n People\n "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Address\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s Student\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//由于这里的address字段是指针,所以我们必须给address赋予实际的值的地址:")]),t._v("\n s "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" Student"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n value"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"1"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Name")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Value")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("s 是指针类型和值类型在调用实质上还是区别的,但是在实际使用中,并不会有什么区别,这主要还是因为要看方法是定义在值类型还是指针类型上。")]),t._v(" "),a("p",[t._v("值类型")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s Student\n s "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" Student"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n value"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"1"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Name")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Value")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Student\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//由于这里的address字段是指针,所以我们必须给address赋予实际的值的地址:")]),t._v("\n s "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Student"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n value"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"1"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Name")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Value")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("当 s 是值时,它调用的就是 People 的方法 + *Address 的方法,")]),t._v(" "),a("p",[t._v("但是如果 s 这里是指针类型的话,那么它调用的就是 *People 的方法 +*Address 的方法")]),t._v(" "),a("p",[t._v("不过即便 s 是值的时候,people 上本身是定义在指针上的方法,那么它在底层调用的时候也势必是 *People")]),t._v(" "),a("p",[t._v("总结一下:结构体变量是什么类型不重要,重要的是定义方法时使用的是什么类型。")]),t._v(" "),a("p",[t._v("你也可以不使用嵌入,使用 "),a("code",[t._v("s.Address.Value()")]),t._v(" 来调用方法:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Student "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n People People\n Address "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Address\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" s Student\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//由于这里的address字段是指针,所以我们必须给address赋予实际的值的地址:")]),t._v("\n s "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" Student"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n value"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"1"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("People"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Name")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Address"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Value")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("h2",{attrs:{id:"define-类型的方法集合"}},[t._v("define 类型的方法集合")]),t._v(" "),a("p",[t._v("define 就是 "),a("code",[t._v("type A int")]),t._v(" 的意思 (type A = int 是另一个意思表示 alias),其中新类型 A 叫做 define 类型,int 叫做 underlying 类型")]),t._v(" "),a("p",[t._v("define 类型的底层如果是接口,那么它完全可以 “继承” 底层数据的方法,比如底层接口拥有三个抽象函数,那么它也有三个一模一样的三个抽线函数")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Hello, 世界"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" b B\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" d D\n\tb "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" d\n\tb"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" B A\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" D "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("D"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hi"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("但是,如果不是接口类型,那么这个类型上就什么方法都没有。它跟它底层的 underlying 将没有任何的联系。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Hello, 世界"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a1 a\n\ta1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" b1 b\n\tb1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hi"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" b a\n\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// error: b1.get undefined (type b has no field or method get)")]),t._v("\n")])])]),a("h2",{attrs:{id:"类型别名的方法集合"}},[t._v("类型别名的方法集合")]),t._v(" "),a("p",[t._v("接下来我们介绍一个类型的别名 alias")]),t._v(" "),a("p",[t._v("使用方法是这样的:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("rune")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int32")]),t._v("\n")])])]),a("p",[t._v("可以看到跟")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("rune")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int32")]),t._v("\n")])])]),a("p",[t._v("非常像,但是,我要强调一下,这两者是完全不同的东西。前者是类型别名,rune 就是 int32 的一个分身,它跟 int32 完全拥有相同的权利,后者,rune 和 int32 是完全两个类型,只是 rune 使用了 int32 作为自己的底层数据而已。")]),t._v(" "),a("p",[t._v("我们看一个例子:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Hello, 世界"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" h hiInterface\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" hi hiStruct\n\th "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" hi\n\n\th"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" myInterface "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" myStruct "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("myStruct"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hi"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" hiInterface "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" myInterface\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" hiStruct "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" myStruct\n\n")])])]),a("p",[t._v("可以看到,别名和类型之间,完全相同,完全等价,不管是接口还是结构体,亦或者是其它的东西。")]),t._v(" "),a("h2",{attrs:{id:"函数式编程"}},[t._v("函数式编程")]),t._v(" "),a("p",[t._v("函数就是一个普通的类型,它跟 int,string,拥有相同的地位,所以你会发现函数式编程在 go 语言的代码里运用的很广泛。")]),t._v(" "),a("p",[t._v("比如:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" f "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("f")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("函数作为 go 语言中的一等公民,拥有以下特征:")]),t._v(" "),a("ul",[a("li",[t._v("在源码的顶层正常的创建函数")]),t._v(" "),a("li",[t._v("函数可以存在于函数内部")]),t._v(" "),a("li",[t._v("函数可以作为类型")]),t._v(" "),a("li",[t._v("函数可以赋值给一个变量")]),t._v(" "),a("li",[t._v("函数可以作为参数")]),t._v(" "),a("li",[t._v("函数可以作为返回值,并且拥有闭包")])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 在源码的顶层正常的创建函数")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// main.go")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 函数可以存在于函数内部")]),t._v("\n\nfun "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 函数可以作为类型")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" A "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n\n")])])]),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 函数可以赋值给一个变量")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 函数可以作为参数")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" f1 "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("f1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 函数可以作为返回值,并且拥有闭包")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n\tg "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("g")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hello"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("g")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"world"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("g")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"--------------------------------"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("g")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hi"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("g")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"你好"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Get")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ti "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\ti"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("++")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" i\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//out")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//hello")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 1")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// world")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 2")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// --------------------------------")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 3")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// hi")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 4")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 你好")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 5")]),t._v("\n\n")])])]),a("h3",{attrs:{id:"函数式编程的实际应用"}},[t._v("函数式编程的实际应用")]),t._v(" "),a("p",[a("em",[a("strong",[t._v("柯里化函数")])])]),t._v(" "),a("p",[t._v("概念:接受多个参数的函数,变成接受一个单一参数的函数,并且返回接受剩余参数以及返回值的新函数。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("sum")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" c "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" x "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" y "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" c\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("partialSum")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("y"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" c "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("sum")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("x"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" y"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" c"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tt1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("partialSum")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tt2 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("partialSum")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tt3 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("partialSum")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("t1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("5")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("t2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("6")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("7")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("t3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("8")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("9")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[a("em",[a("strong",[t._v("函子")])])]),t._v(" "),a("p",[t._v("概念:functor (函子) 本身是一个容器 (slice map channel),容器类型实现一个方法,该方法接受一个函数类型参数,并且每一个容器参数都要被这个函数去改变,这里会得到一个新的 functor,原有的容器没有任何的影响。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" IntSliceFunctor "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Fmap")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" IntSliceFunctor\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" IntSliceFunctorImpl "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tints "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f IntSliceFunctorImpl"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Fmap")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f1 "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" IntSliceFunctor "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tnewInts "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("len")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ints"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" f"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("ints "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tnewInts"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("f1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" IntSliceFunctorImpl"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("newInts"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewIntSliceFunctorImpl")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("ints "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" IntSliceFunctor "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" IntSliceFunctorImpl"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("ints"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\ti "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewIntSliceFunctorImpl")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tm1 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Fmap")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tm1p "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" i"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Fmap")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("20")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tm2 "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" m1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Fmap")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" i "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("20")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("m1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" m1p"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" m2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[a("em",[a("strong",[t._v("配置选项问题")])])]),t._v(" "),a("p",[a("em",[a("strong",[t._v("最基础的方法就是全部暴露出去")])])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Server "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Addr "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n Port "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n Protocol "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewDefalutServer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("addr "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("port "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("protocol "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Server "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Server"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n addr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n port"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n protocol"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewPortServer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("addr "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Server "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Server"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n addr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"8080"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"tcp"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("直接暴露这是一种最基础的方案,这种方法可扩展性很差。")]),t._v(" "),a("p",[t._v("如果想改进,完全可以把非固定的字段单独的封装在一个 struct 中,比如这种写法:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Server "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Addr "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n options "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Options\n \n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Options "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Port "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n Protocol "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewServer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("addr "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" options "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Options"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Server "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Server"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n addr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n options"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("还有一种场景是这样的,我们输出的 API 是一定的,但是我们的配置信息,因为是共同使用的,它可能会越来越多,这个时候改如何处理呢?")]),t._v(" "),a("p",[a("em",[a("strong",[t._v("设置一个固定的 struct,以及一个可共用的 opintions struct")])])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Server "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Addr "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n Port "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n Protocol "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Options "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Addr "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n Port "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n Protocol "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这种写法,API内容不变,共同的options即便是变化了也无关紧要。")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewServer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("options "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Options"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Server "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" addr "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" port "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" protocol "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" options "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n addr "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" options"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Addr\n port "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" options"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Port\n protocol "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" options"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Protocol\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("Server"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n addr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n port"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n protocol"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[a("em",[a("strong",[t._v("我们还可以使用链式调用的方式去写这种参数")])])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Server "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Addr "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n Port "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n Protocol "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" ServerBulder "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Server\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("sb "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("ServerBulder"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Build")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("addr "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("ServerBulder "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n sb"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Addr "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" addr\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" sb\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("sb "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("ServerBulder"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("BuildWithPort")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("port "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("ServerBulder "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n sb"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Port "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" port\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" sb\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("sb "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("ServerBulder"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("BuildWithProtocol")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("protocol "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("ServerBulder "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n sb"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Protocol "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" protocol\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" sb\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("sb "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("ServerBulder"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Run")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("Server"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" sb"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Server\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[a("em",[a("strong",[t._v("functional Options --- 功能选项模式")])])]),t._v(" "),a("p",[t._v("使用场景:在一个配置中心,我们并不想把配置的 struct 暴漏出去,那么我们可以将这个 struct 定义为非导出类型,然后我们定义一个可导出的函数类型,将这个 struct 设置为函数的参数,使用这个可导出的函数来完成配置的操作。")]),t._v(" "),a("p",[t._v("首先是定义一个函数类型")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Option "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("server"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" server "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n addr "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n port "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\n protocol "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("我们使用函数式的方式去定义一组函数")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithPort")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("port "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" Options "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("server"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("port "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" port\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithProtocol")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("protocol "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" Options "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n retrun "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("server"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("protocol "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" protocol\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewServer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("addr "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" options "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("...")]),t._v("Option"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("server "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n serv "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" server"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n addr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"8080"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"tcp"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" opt "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" options "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("opt")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("serv"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 接下来的处理")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"xx/example"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n example"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewServer")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"bj"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("example"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithPort")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"8080"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("example"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("WithProtocol")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"tcp"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("如你所见,使用了函数作为返回值,函数作为参数,变长函数以及闭包等知识,去完成了 “functional options” 这种函数式编程的模式。\n在这个场景下,我们扩展配置变得非常容易,并不需要更改现有的代码,并且也防止了配置 struct 的外漏。")]),t._v(" "),a("h3",{attrs:{id:"这里还有关于函数式编程其它相关内容"}},[t._v("这里还有关于函数式编程其它相关内容:")]),t._v(" "),a("blockquote",[a("p",[t._v("这里的主要内容来自酷壳 https://coolshell.cn/?s=GO+编程模式 (R.I.P 耗子叔)")])]),t._v(" "),a("ul",[a("li",[a("RouterLink",{attrs:{to:"/基础/函数方法/0.html"}},[t._v("反转控制")])],1),t._v(" "),a("li",[a("RouterLink",{attrs:{to:"/基础/函数方法/1.html"}},[t._v("map-reduce")])],1),t._v(" "),a("li",[a("RouterLink",{attrs:{to:"/基础/函数方法/3.html"}},[t._v("修饰器")])],1),t._v(" "),a("li",[a("RouterLink",{attrs:{to:"/基础/函数方法/4.html"}},[t._v("pipeline")])],1),t._v(" "),a("li",[a("RouterLink",{attrs:{to:"/基础/函数方法/5.html"}},[t._v("k8s visitor")])],1),t._v(" "),a("li",[t._v("k8s builder")]),t._v(" "),a("li",[a("RouterLink",{attrs:{to:"/基础/函数方法/7.html"}},[t._v("综合题")])],1)]),t._v(" "),a("h2",{attrs:{id:"issues"}},[t._v("issues")]),t._v(" "),a("p",[a("code",[t._v("问题一:")]),t._v(" "),a("em",[a("strong",[t._v("关于方法的一道题:判断输出")])])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Student"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"一"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"二"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"三"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" a "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("pName")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" b "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("Student"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"四"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"五"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"六"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" v "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" b "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" v"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("pName")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\ttime"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sleep")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("time"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Second"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Student "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tname "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Student"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("pName")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("答案是:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("三\n六\n六\n一\n二\n六\n")])])]),a("p",[t._v("回答:")]),t._v(" "),a("p",[t._v("可以看到,我们期望的一二三四五六并没有输出,这里不考虑顺序,那么四和五为什么没有输出呢?这个时候我们应该考虑方法的本质。")]),t._v(" "),a("p",[t._v("首先我们定了一个在指针类型上的方法 pName,所以第一个 for 循环中,实际上的运行是,每一个指针类型,然后定义在他们上面的方法,并且输出,但是第二个他们是值类型,go 的编译器自动给引出了指针类型,所以说按照指针的实质")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("pName")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("s "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("Student"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n \n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("这里放置的就是 &v,还有个大的原因,就是整个 for 循环比开辟一个新的 goroutine 并且运行完毕远远的快,所以当三个 goroutine 开辟完成的时候,所引用的&v 就是同一个数据了。所以这个时候输出的就是同样的最后的数据值,也就是 “六”,不过这里如果 for 的每一次循环事件都很长,那么 goroutine 运行将会输出 “四五六”。")]),t._v(" "),a("p",[t._v("如果想正常的输出,可以把定义在指针上的方法,改成定义在值上的方法即可。")]),t._v(" "),a("p",[a("code",[t._v("问题二:")]),t._v(" "),a("em",[a("strong",[t._v("panic(nil) 时,defer 函数中的 recover() == nil 成立吗")])])]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"reflect"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("defer")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\ta "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("recover")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("panic")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("答案是:")]),t._v(" "),a("p",[t._v("当 go1.21+ 的时候,不成立,在小于 1.21 的版本中是成立的。")]),t._v(" "),a("p",[t._v("在 1.21 以下,panic 中的 nil 是会传递给 recover() 函数的,所以必定是 true,但是在 1.21+的版本中,"),a("code",[t._v("panic(nil)")]),t._v(" 在编译时,底层修改为了 "),a("code",[t._v("panic(new(runtime.PanicNilError))")])]),t._v(" "),a("p",[t._v("所以说,nil 是不等于 *runtime.PanicNilError 的,综上所述,小于 1.21 的版本成立,大于 1.21 的版本不成立。")]),t._v(" "),a("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),a("ul",[a("li",[t._v("https://book.douban.com/subject/35720728/ 170 页 - 243 页")]),t._v(" "),a("li",[t._v("https://coolshell.cn/?s=GO+编程模式")]),t._v(" "),a("li",[t._v("https://github.com/golang/go/blob/06264b740e3bfe619f5e90359d8f0d521bd47806/src/database/sql/sql.go#L813")]),t._v(" "),a("li",[t._v("https://github.com/lib/pq/blob/922c00e176fb3960d912dc2c7f67ea2cf18d27b0/conn.go#L60")])])])}),[],!1,null,null,null);s.default=p.exports}}]); \ No newline at end of file diff --git a/assets/js/80.5aa44af4.js b/assets/js/80.5aa44af4.js new file mode 100644 index 000000000..7ff2f0cc8 --- /dev/null +++ b/assets/js/80.5aa44af4.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[80],{510:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/81.1584ddc2.js b/assets/js/81.1584ddc2.js new file mode 100644 index 000000000..71010e989 --- /dev/null +++ b/assets/js/81.1584ddc2.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[81],{512:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/82.4e698f70.js b/assets/js/82.4e698f70.js new file mode 100644 index 000000000..7930d119b --- /dev/null +++ b/assets/js/82.4e698f70.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[82],{511:function(t,e,n){"use strict";n.r(e);var s=n(36),l=Object(s.a)({},(function(){var t=this.$createElement;return(this._self._c||t)("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}})}),[],!1,null,null,null);e.default=l.exports}}]); \ No newline at end of file diff --git a/assets/js/83.5b3104c6.js b/assets/js/83.5b3104c6.js new file mode 100644 index 000000000..24c2195d1 --- /dev/null +++ b/assets/js/83.5b3104c6.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[83],{513:function(v,_,l){"use strict";l.r(_);var i=l(36),n=Object(i.a)({},(function(){var v=this,_=v.$createElement,l=v._self._c||_;return l("ContentSlotsDistributor",{attrs:{"slot-key":v.$parent.slotKey}},[l("ol",[l("li",[v._v("自我介绍")]),v._v(" "),l("li",[v._v("代码效率分析,考察局部性原理")]),v._v(" "),l("li",[v._v("多核 CPU 场景下,cache 如何保持一致、不冲突?")]),v._v(" "),l("li",[v._v("uint 类型溢出")]),v._v(" "),l("li",[v._v("介绍 rune 类型")]),v._v(" "),l("li",[v._v("编程题:3 个函数分别打印 cat、dog、fish,要求每个函数都要起一个 goroutine,按照 cat、dog、fish 顺序打印在屏幕上 100 次。")]),v._v(" "),l("li",[v._v("介绍一下 channel,无缓冲和有缓冲区别")]),v._v(" "),l("li",[v._v("是否了解 channel 底层实现,比如实现 channel 的数据结构是什么?")]),v._v(" "),l("li",[v._v("channel 是否线程安全?")]),v._v(" "),l("li",[v._v("Mutex 是悲观锁还是乐观锁?悲观锁、乐观锁是什么?")]),v._v(" "),l("li",[v._v("Mutex 几种模式?")]),v._v(" "),l("li",[v._v("Mutex 可以做自旋锁吗?")]),v._v(" "),l("li",[v._v("介绍一下 RWMutex")]),v._v(" "),l("li",[v._v("项目中用过的锁?")]),v._v(" "),l("li",[v._v("介绍一下线程安全的共享内存方式")]),v._v(" "),l("li",[v._v("介绍一下 goroutine")]),v._v(" "),l("li",[v._v("goroutine 自旋占用 cpu 如何解决 (go 调用、gmp)")]),v._v(" "),l("li",[v._v("介绍 linux 系统信号")]),v._v(" "),l("li",[v._v("goroutine 抢占时机 (gc 栈扫描)")]),v._v(" "),l("li",[v._v("Gc 触发时机")]),v._v(" "),l("li",[v._v("是否了解其他 gc 机制")]),v._v(" "),l("li",[v._v("Go 内存管理方式")]),v._v(" "),l("li",[v._v("Channel 分配在栈上还是堆上?哪些对象分配在堆上,哪些对象分配在栈上?")]),v._v(" "),l("li",[v._v("介绍一下大对象小对象,为什么小对象多了会造成 gc 压力?")]),v._v(" "),l("li",[v._v("项目中遇到的 oom 情况?")]),v._v(" "),l("li",[v._v("项目中使用 go 遇到的坑?")]),v._v(" "),l("li",[v._v("工作遇到的难题、有挑战的事情,如何解决?")]),v._v(" "),l("li",[v._v("如何指定指令执行顺序?")])])])}),[],!1,null,null,null);_.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/84.fd4850fb.js b/assets/js/84.fd4850fb.js new file mode 100644 index 000000000..306688a06 --- /dev/null +++ b/assets/js/84.fd4850fb.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[84],{515:function(t,s,i){"use strict";i.r(s);var n=i(36),e=Object(n.a)({},(function(){var t=this.$createElement,s=this._self._c||t;return s("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}},[s("h1",{attrs:{id:"ioc"}},[this._v("IOC")]),this._v(" "),s("h2",{attrs:{id:"参考资料"}},[this._v("参考资料")]),this._v(" "),s("ul",[s("li",[this._v("https://mp.weixin.qq.com/s/Ar-JdkrQ5NnCWcGOoCuVgg")])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/85.17171820.js b/assets/js/85.17171820.js new file mode 100644 index 000000000..e67c04b80 --- /dev/null +++ b/assets/js/85.17171820.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[85],{514:function(t,s,o){"use strict";o.r(s);var l=o(36),e=Object(l.a)({},(function(){var t=this,s=t.$createElement,o=t._self._c||s;return o("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[o("h1",{attrs:{id:"日志"}},[t._v("日志")]),t._v(" "),o("h2",{attrs:{id:"log"}},[t._v("log")]),t._v(" "),o("h2",{attrs:{id:"zap"}},[t._v("zap")]),t._v(" "),o("h2",{attrs:{id:"slog"}},[t._v("slog")]),t._v(" "),o("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),o("ul",[o("li",[t._v("https://github.com/uber-go/zap")]),t._v(" "),o("li",[t._v("https://pkg.go.dev/golang.org/x/exp/slog")]),t._v(" "),o("li",[t._v("https://mp.weixin.qq.com/s/LToRDGPAYIsmR_WBtHo27A")])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/86.411754fa.js b/assets/js/86.411754fa.js new file mode 100644 index 000000000..349c81eac --- /dev/null +++ b/assets/js/86.411754fa.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[86],{516:function(t,s,e){"use strict";e.r(s);var l=e(36),o=Object(l.a)({},(function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("h1",{attrs:{id:"wasm-在-go-中的实践"}},[t._v("wasm 在 go 中的实践")]),t._v(" "),e("h2",{attrs:{id:"参考文章"}},[t._v("参考文章")]),t._v(" "),e("ul",[e("li",[t._v("https://github.com/golang/go/wiki/WebAssembly")]),t._v(" "),e("li",[t._v("https://blog.csdn.net/jojo1001/article/details/130611146")])])])}),[],!1,null,null,null);s.default=o.exports}}]); \ No newline at end of file diff --git a/assets/js/87.c1433965.js b/assets/js/87.c1433965.js new file mode 100644 index 000000000..8cf2e56e3 --- /dev/null +++ b/assets/js/87.c1433965.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[87],{518:function(t,s,a){"use strict";a.r(s);var n=a(36),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"代码检查工具"}},[t._v("代码检查工具")]),t._v(" "),a("p",[t._v("代码检查通常会被配置再 CI 中,用于自动检查代码的质量,本次我们介绍三个用于代码检查的工具")]),t._v(" "),a("ul",[a("li",[t._v("go vet / go tool vet")]),t._v(" "),a("li",[t._v("golangci-lint")]),t._v(" "),a("li",[t._v("govulncheck")])]),t._v(" "),a("h2",{attrs:{id:"go-vet-go-tool-vet"}},[t._v("go vet / go tool vet")]),t._v(" "),a("p",[t._v("go vet 命令是 go tool vet 的简单封装,go vet 实际上还是需要调用 go tool vet 才能完成工作,这俩命令的主要目的就是为了基础的代码检查。不过这个命令只能做简单的检查,下面我们介绍一下更常用的工具。")]),t._v(" "),a("h2",{attrs:{id:"golangci-lint"}},[t._v("golangci-lint")]),t._v(" "),a("p",[t._v("首先在介绍 golangci-lint 之前我们先下载它,它是一个 go 语言写的可执行文件,使用")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" install github"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("com"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("golangci"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("golangci"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("lint"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("cmd"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("golangci"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("lint@latest \n")])])]),a("p",[t._v("即可下载到本地的~/go/bin/ 目录,这里专门存储使用 go install 下载的使用 go 写的可执行文件,记得将这个路径加入 PATH。")]),t._v(" "),a("p",[t._v("通过在要检查的项目中设置配置文件,用来配置 lint 工具的选项,使用 "),a("code",[t._v(".golangci.yaml")]),t._v(" 即可")]),t._v(" "),a("p",[t._v("例如:")]),t._v(" "),a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[t._v("\nrun:\n skip-dirs: "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 设置要忽略的目录")]),t._v("\n - util\n - .*~\n - api/swagger/docs\n skip-files: "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 设置不需要检查的go源码文件,支持正则匹配,这里建议包括:_test.go")]),t._v("\n - "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('".*'),a("span",{pre:!0,attrs:{class:"token entity",title:"\\\\"}},[t._v("\\\\")]),t._v(".my"),a("span",{pre:!0,attrs:{class:"token entity",title:"\\\\"}},[t._v("\\\\")]),t._v('.go$"')]),t._v("\n - _test.go\nlinters-settings:\n errcheck:\n check-type-assertions: "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 这里建议设置为true,如果确实不需要检查,可以写成`num, _ := strconv.Atoi(numStr)`")]),t._v("\n check-blank: "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),t._v("\n gci:\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 将以`github.com/marmotedu/iam`开头的包放在第三方包后面")]),t._v("\n local-prefixes: github.com/marmotedu/iam\n godox:\n keywords: "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 建议设置为BUG、FIXME、OPTIMIZE、HACK")]),t._v("\n - BUG\n - FIXME\n - OPTIMIZE\n - HACK\n goimports:\n "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 设置哪些包放在第三方包后面,可以设置多个包,逗号隔开")]),t._v("\n local-prefixes: github.com/marmotedu/iam\n gomoddirectives: "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 设置允许在go.mod中replace的包")]),t._v("\n replace-local: "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),t._v("\n replace-allow-list:\n - github.com/coreos/etcd\n - google.golang.org/grpc\n - github.com/marmotedu/api\n - github.com/marmotedu/component-base\n - github.com/marmotedu/marmotedu-sdk-go\n gomodguard: "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 下面是根据需要选择可以使用的包和版本,建议设置")]),t._v("\n allowed:\n modules:\n - gorm.io/gorm\n - gorm.io/driver/mysql\n - k8s.io/klog\n domains: "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# List of allowed module domains")]),t._v("\n - google.golang.org\n - gopkg.in\n - golang.org\n - github.com\n - go.uber.org\n blocked:\n modules:\n - github.com/pkg/errors:\n recommendations:\n - github.com/marmotedu/errors\n reason: "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"'),a("span",{pre:!0,attrs:{class:"token variable"}},[a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("`")]),t._v("github.com/marmotedu/errors"),a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("`")])]),t._v(' is the log package used by marmotedu projects."')]),t._v("\n versions:\n - github.com/MakeNowJust/heredoc:\n version: "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"> 2.0.9"')]),t._v("\n reason: "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"use the latest version"')]),t._v("\n local_replace_directives: "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("false")]),t._v("\n lll:\n line-length: "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("240")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 这里可以设置为240,240一般是够用的")]),t._v("\n importas: "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 设置包的alias,根据需要设置")]),t._v("\n jwt: github.com/appleboy/gin-jwt/v2 \n metav1: github.com/marmotedu/component-base/pkg/meta/v1\n\n")])])]),a("p",[t._v("使用 golangci-lint run (等于 golangci-lint run ./... 意思就是把所有的包,子包,遍历完全) 你会得到一个类似:")]),t._v(" "),a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[t._v("\ncollie.go:171:41: composites: image/jpeg.Options struct literal uses unkeyed fields "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("govet"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err :"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" jpeg.Encode"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("file, i.img, "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("jpeg.Options"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("q"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" nil "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t\t\t ^\ncollie.go:241:2: printf: "),a("span",{pre:!0,attrs:{class:"token variable"}},[a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("`")]),t._v("fmt.Println"),a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("`")])]),t._v(" arg list ends with redundant newline "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("govet"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt.Println"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"声明:本程序来自GitHub:shgopher,欢迎关注公众号:科科人神;'),a("span",{pre:!0,attrs:{class:"token entity",title:"\\n"}},[t._v("\\n")]),t._v("免费软件,如果使用期间出现任何后果,本软件不承担任何责任谢谢"),a("span",{pre:!0,attrs:{class:"token entity",title:"\\n"}},[t._v("\\n")]),t._v('"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t^\ncollie.go:244:2: printf: "),a("span",{pre:!0,attrs:{class:"token variable"}},[a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("`")]),t._v("fmt.Println"),a("span",{pre:!0,attrs:{class:"token variable"}},[t._v("`")])]),t._v(" arg list ends with redundant newline "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("govet"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt.Println"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"运行结束 ☕️ ☕ ☕'),a("span",{pre:!0,attrs:{class:"token entity",title:"\\n"}},[t._v("\\n")]),t._v('"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t^\ncollie.go:162:5: SA9001: defers "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("in")]),t._v(" this range loop won't run unless the channel gets closed "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("staticcheck"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t\tdefer file.Close"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\t\t\t^\n")])])]),a("p",[t._v("这样的结果,这样你就会发现是哪个配置的 linter 发出的警告,以及是什么样子的警告。")]),t._v(" "),a("h2",{attrs:{id:"govulncheck"}},[t._v("govulncheck")]),t._v(" "),a("p",[t._v("go 官方维护了一个 https://vuln.go.dev/ 的漏洞库,我们可以使用 "),a("code",[t._v("go install golang.org/x/vuln/cmd/govulncheck@latest")]),t._v(" 的方式,下载目前 (go 1.19) 还在测试阶段的这一功能,"),a("code",[t._v("govulncheck")]),t._v(" 将会是一个独立的工具,并且 go 在 https://pkg.go.dev/golang.org/x/vuln/vulncheck 还提供了相关功能的 API,可以更灵活的去使用这个功能,目前已知的即将推出的功能分别是提供 vscode 插件,以及将此功能集成在 pkg.go.dev 这个 go 包的集合地,也就是说只要被收录在这个网站的 go 包都将自动接受漏洞检查,另外,go 以后可能还会将这个功能直接集成在例如 "),a("code",[t._v("go build")]),t._v(" 这种常用命令上。")]),t._v(" "),a("ul",[a("li",[t._v("下载 govulncheck "),a("code",[t._v("go install golang.org/x/vuln/cmd/govulncheck@latest")])]),t._v(" "),a("li",[t._v("在一个拥有 go.mod 的目录下,使用 govulncheck 跟上一个有 go 文件的路径,例如:"),a("code",[t._v("govulncheck ./pkg/watcher")])])]),t._v(" "),a("p",[t._v("只需要这样简单的设置就可以去检查代码中存在的风险和漏洞,govulncheck 就会打印出这样的信息:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("Vulnerability #"),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" GO"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2022")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0493")]),t._v("\n When called with a non"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("zero flags parameter"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" the Faccessat\n function can incorrectly report that a file is accessible"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\n Found in"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" golang"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("org"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("x"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("sys"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("unix@v0"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.0")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("20211020064051")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("0ec99a608a1b\n Fixed in"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" golang"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("org"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("x"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("sys"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("unix@v0"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.0")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("20220412211240")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("33da011f77ad\n More info"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" https"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("pkg"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("dev"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("vuln"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("GO"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("2022")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0493")]),t._v("\n")])])]),a("p",[t._v("信息中包括了你引用的某些包出现的一些漏洞,在 fix 中有修复的信息,可以把你引用的包进行一个升级。")]),t._v(" "),a("h2",{attrs:{id:"x-tools-工具系列"}},[t._v("x/tools 工具系列")]),t._v(" "),a("blockquote",[a("p",[t._v("https://pkg.go.dev/golang.org/x/tools#section-readme")])]),t._v(" "),a("p",[t._v("比如检测变量 shadow 的工具")]),t._v(" "),a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[t._v("go "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("install")]),t._v(" golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow\n")])])]),a("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),a("ul",[a("li",[t._v("https://go.dev/blog/vuln")]),t._v(" "),a("li",[t._v("https://time.geekbang.org/column/article/390401")])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/88.0ee83428.js b/assets/js/88.0ee83428.js new file mode 100644 index 000000000..068d31492 --- /dev/null +++ b/assets/js/88.0ee83428.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[88],{520:function(t,n,e){"use strict";e.r(n);var s=e(36),r=Object(s.a)({},(function(){var t=this,n=t.$createElement,e=t._self._c||n;return e("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[e("h1",{attrs:{id:"优秀第三方包"}},[t._v("优秀第三方包")]),t._v(" "),e("ul",[e("li",[e("a",{attrs:{href:"./gin"}},[t._v("gin")]),t._v(" 优秀的 web 框架")]),t._v(" "),e("li",[e("a",{attrs:{href:"./sonic"}},[t._v("sonic")]),t._v(" 字节跳动出品的 json 引擎")])])])}),[],!1,null,null,null);n.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/89.32dbf192.js b/assets/js/89.32dbf192.js new file mode 100644 index 000000000..bcbfac439 --- /dev/null +++ b/assets/js/89.32dbf192.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[89],{517:function(t,e,n){"use strict";n.r(e);var i=n(36),o=Object(i.a)({},(function(){var t=this,e=t.$createElement,n=t._self._c||e;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"gin"}},[t._v("gin")]),t._v(" "),n("blockquote",[n("p",[t._v("https://github.com/gin-gonic/gin")])]),t._v(" "),n("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),n("ul",[n("li",[t._v("https://betterprogramming.pub/a-do-it-yourself-implementation-of-the-golang-middleware-6f02f155ed17")])])])}),[],!1,null,null,null);e.default=o.exports}}]); \ No newline at end of file diff --git a/assets/js/9.0852bf61.js b/assets/js/9.0852bf61.js new file mode 100644 index 000000000..67275371c --- /dev/null +++ b/assets/js/9.0852bf61.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[9],{429:function(t,n){t.exports="data:image/png;base64,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"},469:function(t,n,a){"use strict";a.r(n);var s=a(36),e=Object(s.a)({},(function(){var t=this,n=t.$createElement,s=t._self._c||n;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h1",{attrs:{id:"go-数字类型"}},[t._v("go 数字类型")]),t._v(" "),s("p",[s("strong",[t._v("导读:")])]),t._v(" "),s("ul",[s("li",[t._v("int")]),t._v(" "),s("li",[t._v("float")]),t._v(" "),s("li",[t._v("complex")]),t._v(" "),s("li",[t._v("issues")])]),t._v(" "),s("h2",{attrs:{id:"int"}},[t._v("int")]),t._v(" "),s("p",[t._v("int 是 go 语言的整数类型,一共分为下面这么几类")]),t._v(" "),s("table",[s("thead",[s("tr",[s("th",{staticStyle:{"text-align":"center"}},[t._v("类型")]),t._v(" "),s("th",{staticStyle:{"text-align":"center"}},[t._v("描述")])])]),t._v(" "),s("tbody",[s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("int")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("有符号的整数类型,具体占几个字节要看操作系统的分配,不过至少分配给32位")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("uint")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("非负整数类型,具体占几个字节要看操作系统的分配,不过至少分配给32位")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("int8")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("有符号的整数类型,占8位bit,1个字节。范围从负的2的7次方到正的2的7次方减1")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("int16")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("有符号的整数类型,占16位bit,2个字节。范围从负的2的15次方到正的2的15次方减1")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("int32")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("有符号的整数类型,占32位bit,4个字节。范围从负的2的31次方到正的2的31次方减1")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("int64")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("有符号的整数类型,占64位bit,8个字节。范围从负的2的63次方到正的2的63次方减1")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("uint8")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("无符号的正整数类型,占8位,从0到2的8次方减1.也就是0到255")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("uint16")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("无符号的正整数类型,占16位,从0到2的16次方减1")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("uint32")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("无符号的正整数类型,占32位,从0到2的32次方减1")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("uint64")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("无符号的正整数类型,占64位,从0到2的64次方减1")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("uintptr")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("无符号整数类型。它大到足以容纳任何指针")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("rune")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("int32的别名,代表一个 UTF-8 字符")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("byte")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("uint8别名,代表了ASCII 码的一个字符")])])])]),t._v(" "),s("p",[t._v("go 使用 "),s("code",[t._v("'\\x12'")]),t._v(" 或者使用 "),s("code",[t._v("0x12")]),t._v(" 来表示 16 进制")]),t._v(" "),s("p",[t._v("go 使用 "),s("code",[t._v("'\\012'")]),t._v(" 或者使用 "),s("code",[t._v("012")]),t._v(" 来表示 8 进制")]),t._v(" "),s("p",[t._v("go 不能直接显示 2 进制,使用 "),s("code",[t._v('fmt.Printf("%b",12)')]),t._v(" "),s("code",[t._v("1000")]),t._v(" 来输出一个二进制")]),t._v(" "),s("h2",{attrs:{id:"float"}},[t._v("float")]),t._v(" "),s("table",[s("thead",[s("tr",[s("th",{staticStyle:{"text-align":"center"}},[t._v("类型")]),t._v(" "),s("th",{staticStyle:{"text-align":"center"}},[t._v("描述")])])]),t._v(" "),s("tbody",[s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("float32")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("浮点型,包括正负小数,IEEE-754 32位的集合,提供大约 6 个十进制数的精度,math.MaxFloat32 表示 float32 能取到的最大数值,math.SmallestNonzeroFloat32表示最小值")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("float64")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("浮点型,包括正负小数,IEEE-754 64位的集合,提供约 15 个十进制数的精度,math.MaxFloat64 表示 float64 能取到的最大数值,math.SmallestNonzeroFloat64表示最小值")])])])]),t._v(" "),s("p",[t._v("浮点数使用 "),s("code",[t._v('fmt.Printf("%.4f\\n", math.Pi)')]),t._v(","),s("code",[t._v("%.nf")]),t._v(" 来控制保留几位小数")]),t._v(" "),s("p",[t._v("我们知道根据 IEEE 754 标准,使用有限个位置的二进制数字去代表无限个数字,那么必然是不精确的,是近似的。")]),t._v(" "),s("p",[t._v("由于浮点数计算的时候并不精确,容易发生较大误差,所以我们可以使用现有的高精度第三方库进行替换:https://github.com/shopspring/decimal")]),t._v(" "),s("h3",{attrs:{id:"基础浮点数-math-big-包-decimal-三方包的对比"}},[t._v("基础浮点数,math/big 包,decimal 三方包的对比")]),t._v(" "),s("p",[t._v("让我们看一下著名的 0.1 + 0.2 != 0.3 这个问题")]),t._v(" "),s("div",{staticClass:"language-go extra-class"},[s("pre",{pre:!0,attrs:{class:"language-go"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" a "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.1")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" b "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.2")]),t._v("\n\t\n\t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// false")]),t._v("\n\tfmt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("a"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v("b "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("==")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.3")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[s("em",[s("strong",[t._v("基础浮点数 IEEE 754 详细介绍")])])]),t._v(" "),s("p",[t._v("这是一个 IEEE 754 的内部存储示意图,这个标准下,数字使用二进制的方式进行存储:")]),t._v(" "),s("p",[s("img",{attrs:{src:a(429),alt:"IEEE 754"}})]),t._v(" "),s("table",[s("thead",[s("tr",[s("th",{staticStyle:{"text-align":"center"}},[t._v("sign")]),t._v(" "),s("th",{staticStyle:{"text-align":"center"}},[t._v("exponent")]),t._v(" "),s("th",{staticStyle:{"text-align":"center"}},[t._v("fraction")])])]),t._v(" "),s("tbody",[s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("代表正负的符号位")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("指数位")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("尾数")])])])]),t._v(" "),s("ul",[s("li",[s("p",[t._v("sign:"),s("code",[t._v("(-1)^sign")]),t._v(",所以当 sign 等于 0 时,就是正数,当 sign 等于 1 时就是负数")])]),t._v(" "),s("li",[s("p",[t._v("exponent:指数,也就是 2 的指数,在计算的时候,我们要减去一个偏移量 (127 或者 1023,这取决于 32 位还是 64 位),在 32 位的情况下,0 - 255,然后减去偏移量 127,结果是:[-126,127] (正常情况下,不算上 00000000 和 11111111 这两种情况)")])]),t._v(" "),s("li",[s("p",[t._v("fraction:尾数,表示为 1.fraction 或者 0。fraction 的样子,后者通常表示的极小浮点数")])])]),t._v(" "),s("p",[t._v("例如,图示的 0.15625,使用 IEEE 754 来表示:")]),t._v(" "),s("div",{staticClass:"language-go extra-class"},[s("pre",{pre:!0,attrs:{class:"language-go"}},[s("code",[s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("^")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("^")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1111100")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1.01")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("^")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("124")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("127")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1.01")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("^")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1.01")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.00101")]),t._v("(二进制)\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.15625")]),t._v(" (十进制)\n")])])]),s("p",[t._v("可以看到,一个十进制的 0.15625 要转化为一个 2 进制的信息进行保存,那么问题来了,为什么浮点数的 0.1 不等于整数的 0.1 呢?")]),t._v(" "),s("blockquote",[s("p",[t._v("10 进制小数转化为 2 进制:使用十进制的小数部分乘以 2,然后取整数部分,然后再把这个数的小数部分再次乘以 2... 直到小数部分为零结束;十进制的整数部分就按照普通的除法,每次都除以 2,然后取余数,直到商为 0:")])]),t._v(" "),s("blockquote",[s("p",[t._v("举一个例子:十进制 8.35;")])]),t._v(" "),s("blockquote",[s("p",[t._v("整数部分是 8,8 /2 = 4 余 0,4/2 = 2 余 0,2/2 = 1 余 0,1/2=0 余 1,那么根据倒序排列来得出结果就是 1000;")])]),t._v(" "),s("blockquote",[s("p",[t._v("小数部分是 0.35,0.35 "),s("em",[t._v("2 = 0.7 整数部分是 0,0.7")]),t._v(" 2 = 1.4 整数部分是 1,0.4 "),s("em",[t._v("2 = 0.8 整数部分是 0,0.8")]),t._v(" 2 = 1.6 整数部分是 1,0.6 "),s("em",[t._v("2 = 1.2 整数部分是 1,0.2")]),t._v(" 2 = 0.4 整数部分是 0,0.4 "),s("em",[t._v("2 = 0.8 整数部分是 0,0.8")]),t._v("2 = 1.6 ... 那么小数部分就是 0.01011001... 那么在满足某个精度的情况下,它的值用二进制表示就是 1000.01011001;")])]),t._v(" "),s("blockquote",[s("p",[t._v("将它还原也很简单,整数部分按照 2 的指数,小数部分按照 2 的负指数:1*2^3 + 2^-2 + 2^-4 + 2^-5 = 8.34375")])]),t._v(" "),s("p",[t._v("第一步,我们需要将 0.1 转化为二进制,那么等于多少呢?")]),t._v(" "),s("p",[t._v("答案是:0.0001100110011...")]),t._v(" "),s("p",[t._v("看到了吧,除不尽啊,那么我们取它的尾数 1.1001100110011001100 * 2^-4")]),t._v(" "),s("p",[t._v("如果填入他们的图就是")]),t._v(" "),s("ul",[s("li",[t._v("sign 为 0")]),t._v(" "),s("li",[t._v("指数的十进制为 -4 + 127 = 123,也就是 "),s("code",[t._v("1111011")]),t._v(" 填入上面的内容就是 01111011")]),t._v(" "),s("li",[t._v("尾数是 100110011001100")])]),t._v(" "),s("p",[t._v("所以你会发现从这里的存储再转化为 10 进制的结果就是")]),t._v(" "),s("div",{staticClass:"language-go extra-class"},[s("pre",{pre:!0,attrs:{class:"language-go"}},[s("code",[s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("^")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("4")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1.1001100110011")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.00011001100110011")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.0999984741210937")]),t._v("\n")])])]),s("p",[t._v("总结:由于某些数字,从 10 进制转化为 2 进制的时候,无法得到精确值,除不尽,所以,浮点数存储数字,有些数字不能精确存储")]),t._v(" "),s("p",[s("em",[s("strong",[t._v("math/big 包详细介绍")])])]),t._v(" "),s("p",[t._v("big 拥有下面三个类型")]),t._v(" "),s("ul",[s("li",[t._v("Int")]),t._v(" "),s("li",[t._v("Float")]),t._v(" "),s("li",[t._v("Rat (有理数)")])]),t._v(" "),s("p",[t._v("big 包提供的 "),s("code",[t._v("big.Int")]),t._v(" 底层数据是一个切片,所以说它可以保存超过 "),s("code",[t._v("uint64")]),t._v(" 的数据;"),s("code",[t._v("big.Rat")]),t._v(" 的底层数据是,分子和分母分别都是一个 "),s("code",[t._v("big.Int")]),t._v(" 类型,所以说它也可以保存超过 uint64 的数据。")]),t._v(" "),s("p",[t._v("big.Float 类型是 Go 语言中提供的一个高精度的浮点数计算库,它使用了任意精度的浮点数表示,可以处理大数和小数,并提供了一系列的计算方法")]),t._v(" "),s("p",[t._v("虽然它是高精度的浮点数计算,但是仍然不够精确,如果是绝对的精确的浮点数计算,通常要使用有理数,或者使用整数去取代真正的小数,下面这个第三方包就是使用整数去取代浮点数的方式来获取精确的浮点数运行结果。")]),t._v(" "),s("p",[s("em",[s("strong",[t._v("decimal")])])]),t._v(" "),s("p",[t._v("这个第三方包底层使用 "),s("code",[t._v("big.Int")]),t._v(" 进行处理数据,也就是说,它使用整数存储,计算然后得出结论的时候再把之前的指数还原即可,所以它可以进行精确的浮点数运算")]),t._v(" "),s("p",[t._v("https://github.com/shopspring/decimal")]),t._v(" "),s("p",[t._v("这个包中,0.1 + 0.2 就等于 0.3 了")]),t._v(" "),s("div",{staticClass:"language-go extra-class"},[s("pre",{pre:!0,attrs:{class:"language-go"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 创建一个新的Float类型")]),t._v("\n\ta "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" decimal"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewFromFloat")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.1")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tb "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" decimal"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewFromFloat")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.2")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tc "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" decimal"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("NewFromFloat")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.3")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tvalue "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" a"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Add")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("b"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 0")]),t._v("\n\tfmt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("value"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Cmp")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("c"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("h2",{attrs:{id:"complex-复数"}},[t._v("complex 复数")]),t._v(" "),s("table",[s("thead",[s("tr",[s("th",{staticStyle:{"text-align":"center"}},[t._v("类型")]),t._v(" "),s("th",{staticStyle:{"text-align":"center"}},[t._v("描述")])])]),t._v(" "),s("tbody",[s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("complex64")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("实部和虚部是 float32")])]),t._v(" "),s("tr",[s("td",{staticStyle:{"text-align":"center"}},[t._v("complex128")]),t._v(" "),s("td",{staticStyle:{"text-align":"center"}},[t._v("实部和虚部都是 float64")])])])]),t._v(" "),s("p",[s("code",[t._v("c := complex(3, 4)")]),t._v(" 创建一个复数,实部为 3,虚部为 4")]),t._v(" "),s("p",[t._v("或者还可以这样:"),s("code",[t._v("c := 3 + 4i")])]),t._v(" "),s("p",[t._v("使用 go 语言内置的函数 "),s("code",[t._v("real()")]),t._v(" 和 "),s("code",[t._v("imag()")]),t._v(" 来分别获取到复数的实部和虚部")]),t._v(" "),s("h2",{attrs:{id:"issues"}},[t._v("issues")]),t._v(" "),s("p",[t._v("***问题一:***go 语言数字之间的类型转化是如何进行的")]),t._v(" "),s("p",[t._v("go 语言的类型转换是显性转化的,数字类型之间可以使用这种方法")]),t._v(" "),s("div",{staticClass:"language-go extra-class"},[s("pre",{pre:!0,attrs:{class:"language-go"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" i "),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),t._v("\nf "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("float64")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("i"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),s("p",[t._v("***问题二:***能说说 "),s("code",[t._v("uintptr")]),t._v(" 和 "),s("code",[t._v("unsafe.Pointer")]),t._v(" 的区别吗")]),t._v(" "),s("ul",[s("li",[s("code",[t._v("unsafe.Pointer")]),t._v(" 是通用指针类型,它不能参与计算")]),t._v(" "),s("li",[s("code",[t._v("uintptr")]),t._v(" 是指针运算的工具,但是它不能持有指针对象 (意思就是它跟指针对象不能互相转换)")]),t._v(" "),s("li",[s("code",[t._v("unsafe.Pointer")]),t._v(" 和 "),s("code",[t._v("uintptr")]),t._v(" 可以相互转换,"),s("code",[t._v("unsafe.Pointer")]),t._v(" 和指针对象可以相互转换,但是 "),s("code",[t._v("uintptr")]),t._v(" 和指针对象不能相互转换")]),t._v(" "),s("li",[t._v("unsafe.Pointer 是指针对象进行运算 (也就是 uintptr) 的桥梁")])]),t._v(" "),s("div",{staticClass:"language-go extra-class"},[s("pre",{pre:!0,attrs:{class:"language-go"}},[s("code",[s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token string"}},[t._v('"unsafe"')]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 指针对象")]),t._v("\n\tv "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("new")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 将指针对象转化为通用指针类型")]),t._v("\n\tvp "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" unsafe"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Pointer")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 将通用指针类型转换为指针对象")]),t._v("\n\tvo "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("vp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//将通用指针对象转化为uintptr")]),t._v("\n\tuv "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("uintptr")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("vp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//将uintptr转换为通用指针对象")]),t._v("\n\tvpp "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" unsafe"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Pointer")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("uv"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("v"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" vp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" vo"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" uv"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" vpp"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n\t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 对指针对象的地址进行计算")]),t._v("\n\tt "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("new")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 首先先将t转化为unsafe.Pointer类型")]),t._v("\n\tpt "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" unsafe"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Pointer")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 然后将pointer再转化为 uintptr")]),t._v("\n\trt "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("uintptr")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("pt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//进行计算")]),t._v("\n\tnpt "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" rt "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("+")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("uintptr")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 计算完毕后,再将uintptr转化为unsafe.Pointer再转换为*string类型")]),t._v("\n\tnt "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("unsafe"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Pointer")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("npt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\tfmt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),s("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("t"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" pt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" rt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" npt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" nt"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),s("p",[t._v("***问题三:***rune 和 byte 的区别")]),t._v(" "),s("p",[t._v("rune 是 int32,byte 是 uint8,相比 byte 来说,rune 可以容纳的字符个数要多很多,所以 utf8 编码的字符使用 rune,而 ascii 使用 byte,例如 ‘中’ byte 无法承受,“中” 转为 "),s("code",[t._v("[]byte")]),t._v(" 的时候是 "),s("code",[t._v("[228 184 173]")]),t._v(",“中” 转换为 "),s("code",[t._v("[]rune")]),t._v(" 则是等于 "),s("code",[t._v("[20013]")]),t._v(" 因为 rune 可承受的数值更大,并且一个 utf8 的字符就等于一个 rune 的数值,如果是使用 ‘中’ 那默认就是 rune 类型")]),t._v(" "),s("blockquote",[s("p",[t._v("unicode 是一种字符编码,让每个字符和一个数字对应起来,仅此而已,至于这个数字如何存储它就不管了。utf8 就是定义了如何具体存储这个编码数字的一种方法")])]),t._v(" "),s("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),s("ul",[s("li",[t._v("https://zhuanlan.zhihu.com/p/145220416")]),t._v(" "),s("li",[t._v("http://www.manongjc.com/article/50416.html")]),t._v(" "),s("li",[t._v("http://c.biancheng.net/view/18.html")]),t._v(" "),s("li",[t._v("https://zhuanlan.zhihu.com/p/353013671")]),t._v(" "),s("li",[t._v("https://developer.aliyun.com/article/673258")])])])}),[],!1,null,null,null);n.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/90.86c77607.js b/assets/js/90.86c77607.js new file mode 100644 index 000000000..140bd532b --- /dev/null +++ b/assets/js/90.86c77607.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[90],{519:function(t,s,n){"use strict";n.r(s);var e=n(36),i=Object(e.a)({},(function(){var t=this.$createElement,s=this._self._c||t;return s("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}},[s("h1",{attrs:{id:"sonic"}},[this._v("sonic")]),this._v(" "),s("blockquote",[s("p",[this._v("https://github.com/bytedance/sonic")])])])}),[],!1,null,null,null);s.default=i.exports}}]); \ No newline at end of file diff --git a/assets/js/91.bc281b0a.js b/assets/js/91.bc281b0a.js new file mode 100644 index 000000000..c0a4126a2 --- /dev/null +++ b/assets/js/91.bc281b0a.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[91],{521:function(t,s,n){"use strict";n.r(s);var a=n(36),e=Object(a.a)({},(function(){var t=this,s=t.$createElement,n=t._self._c||s;return n("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[n("h1",{attrs:{id:"内存对齐实践"}},[t._v("内存对齐实践")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Person "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Sex "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 性别")]),t._v("\n Height "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint16")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 身高")]),t._v("\n Addr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),t._v("\n Postion "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),t._v("\n age "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int64")]),t._v("\n weight "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint16")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("我们分别算一下数据实际的大小和在内存中的的偏移量以及整个结构体因为内存对齐所一共占有的内存数据")]),t._v(" "),n("p",[t._v("实际大小")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Person "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Sex "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//1")]),t._v("\n Height "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint16")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//2")]),t._v("\n Addr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//1")]),t._v("\n Postion "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//1")]),t._v("\n age "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int64")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//8")]),t._v("\n weight "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint16")]),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//2")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("偏移量")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Person "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Sex "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 0")]),t._v("\n Height "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint16")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 2")]),t._v("\n Addr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//4")]),t._v("\n Postion "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 5")]),t._v("\n age "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int64")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 8")]),t._v("\n weight "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint16")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 16")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("实际在内存中的排序:")]),t._v(" "),n("div",{staticClass:"language-bash extra-class"},[n("pre",{pre:!0,attrs:{class:"language-bash"}},[n("code",[t._v("sex * height height addr postion * * age age age age age age age age weight weight \n")])])]),n("p",[t._v("在 Go 语言中,每种数据类型在内存中的字节对齐方式是不同的,主要遵循以下规则:")]),t._v(" "),n("ul",[n("li",[t._v("每个变量都会占用一块内存区域,并从这块区域的起始地址开始存储值。")]),t._v(" "),n("li",[t._v("结构体的起始地址必须是其最大字段的对齐值的倍数。")]),t._v(" "),n("li",[t._v("每个字段的起始地址必须是其类型对齐值的倍数。")]),t._v(" "),n("li",[t._v("内存对齐后的结构体大小必须是其最大字段的对齐值的倍数。")]),t._v(" "),n("li",[t._v("如果存在内存对齐的需求,编译器会在具体字段之间插入对齐字节 (padding)。")])]),t._v(" "),n("p",[t._v("我们都知道取内存地址的时候是通过下标去获取的,所以当 height 位于 sex 后面,但是 sex 只占有一个字节,但是 height 有两个字节的时候,如果要精确获取 height 的起始位置,那么必须要按照 uint16 的大小的倍数去获取,也就是 2 的倍数,那么 sex 后面肯定要跟一个空值才行,所以 height 的偏移量就是 2")]),t._v(" "),n("p",[t._v("position 也是一样,它的大小是 8,那么要获取它的位置,至少是 8 的倍数才行,所以前面要补充两个空值")]),t._v(" "),n("p",[t._v("具体的每个字段的偏移量具体分析如下:")]),t._v(" "),n("ul",[n("li",[t._v("Sex bool 占 1 字节,起始偏移为 0")]),t._v(" "),n("li",[t._v("Height uint16 需要以 2 字节对齐,所以从偏移 2 开始")]),t._v(" "),n("li",[t._v("Addr byte 占 1 字节,但前面已插入 1 字节 padding,所以从偏移 4 开始")]),t._v(" "),n("li",[t._v("Position byte 紧接着 Addr 的下一字节,所以偏移 5")]),t._v(" "),n("li",[t._v("age int64 需要以 8 字节对齐,所以从偏移 8 开始")]),t._v(" "),n("li",[t._v("weight uint16 需要以 2 字节对齐,并且要跟在 age 之后,所以从偏移 16 开始")])]),t._v(" "),n("p",[t._v("也就是说整个的结构体按照空值来算一共是 18 个字节的占有量,那么整个结构体是多少呢?")]),t._v(" "),n("p",[t._v("结构体的大小计算方法是最大值的倍数,这里就是 age 的 8 的倍数,必须要大于实际值 18,所以这里取 24")]),t._v(" "),n("h2",{attrs:{id:"下面对这个结构体进行优化"}},[t._v("下面对这个结构体进行优化")]),t._v(" "),n("h3",{attrs:{id:"顺序优化去除-padding"}},[t._v("顺序优化去除 padding")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Person "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Sex "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 0")]),t._v("\n Addr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//1")]),t._v("\n Postion "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 2")]),t._v("\n Height "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint16")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 4")]),t._v("\n weight "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint16")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 6")]),t._v("\n age "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int64")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 8")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("现在我们看一下具体的内存排布")]),t._v(" "),n("div",{staticClass:"language-bash extra-class"},[n("pre",{pre:!0,attrs:{class:"language-bash"}},[n("code",[t._v("sex addr postion * height height weight weight age age age age age age age age\n")])])]),n("p",[t._v("可以看到这个时候的一共需要的数据只有 16,刚好结构体的大小是 8 的倍数,那么这个时候结构体的大小就变成了 16 字节")]),t._v(" "),n("h3",{attrs:{id:"数据类型调整"}},[t._v("数据类型调整")]),t._v(" "),n("p",[t._v("我们可以使用更小的类型去承载数据,比如使用 uint8 去替代 uint16,和 int64")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Person "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n Sex "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 0")]),t._v("\n Addr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//1")]),t._v("\n Postion "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 2")]),t._v("\n Height "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint8")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 3")]),t._v("\n weight "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint8")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 4")]),t._v("\n age "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint8")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 5")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("p",[t._v("整个结构体的大小就变成了 6 个字节")]),t._v(" "),n("h3",{attrs:{id:"测试优化顺序的结构体大小"}},[t._v("测试优化顺序的结构体大小")]),t._v(" "),n("div",{staticClass:"language-go extra-class"},[n("pre",{pre:!0,attrs:{class:"language-go"}},[n("code",[n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"unsafe"')]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 原始结构体")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" OriginalPerson "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tSex "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 0 offset")]),t._v("\n\tHeight "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint16")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 2")]),t._v("\n\tAddr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 4")]),t._v("\n\tPostion "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 5")]),t._v("\n\tAge "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int64")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 8")]),t._v("\n\tWeight "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint16")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 16")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 优化后的结构体")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" OptimizedPerson "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tSex "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 0 offset")]),t._v("\n\tAddr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//1")]),t._v("\n\tPostion "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 2")]),t._v("\n\tHeight "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint16")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 4")]),t._v("\n\tweight "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint16")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 6")]),t._v("\n\tage "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int64")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 8")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" OptimizedPerson1 "),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tSex "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("bool")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 0")]),t._v("\n\tAddr "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//1")]),t._v("\n\tPostion "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("byte")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 2")]),t._v("\n\tHeight "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint8")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 3")]),t._v("\n\tweight "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint8")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 4")]),t._v("\n\tage "),n("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("uint8")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 5")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),n("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 测试原始结构体大小")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"原始结构体大小:"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" unsafe"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sizeof")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("OriginalPerson"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 测试优化后结构体大小")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"优化后结构体大小:"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" unsafe"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sizeof")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("OptimizedPerson"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),n("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 测试优化后结构体大小1")]),t._v("\n\tfmt"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),n("span",{pre:!0,attrs:{class:"token string"}},[t._v('"优化后结构体大小:"')]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" unsafe"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),n("span",{pre:!0,attrs:{class:"token function"}},[t._v("Sizeof")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("OptimizedPerson1"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),n("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),n("div",{staticClass:"language-bash extra-class"},[n("pre",{pre:!0,attrs:{class:"language-bash"}},[n("code",[t._v("原始结构体大小: "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("24")]),t._v("\n优化后结构体大小: "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("16")]),t._v("\n优化后结构体大小1: "),n("span",{pre:!0,attrs:{class:"token number"}},[t._v("6")]),t._v("\n")])])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/92.52bff9af.js b/assets/js/92.52bff9af.js new file mode 100644 index 000000000..24ed61204 --- /dev/null +++ b/assets/js/92.52bff9af.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[92],{522:function(t,s,i){"use strict";i.r(s);var e=i(36),n=Object(e.a)({},(function(){var t=this.$createElement,s=this._self._c||t;return s("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}},[s("h1",{attrs:{id:"动态调试"}},[this._v("动态调试")]),this._v(" "),s("h2",{attrs:{id:"资料"}},[this._v("资料")]),this._v(" "),s("ul",[s("li",[this._v("https://mp.weixin.qq.com/s/qfukFmJRcBAjn-e2K73VLQ")])])])}),[],!1,null,null,null);s.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/93.a157b4e1.js b/assets/js/93.a157b4e1.js new file mode 100644 index 000000000..ee7fe82ef --- /dev/null +++ b/assets/js/93.a157b4e1.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[93],{524:function(t,s,a){"use strict";a.r(s);var n=a(36),e=Object(n.a)({},(function(){var t=this,s=t.$createElement,a=t._self._c||s;return a("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[a("h1",{attrs:{id:"go-包管理工具"}},[t._v("go 包管理工具")]),t._v(" "),a("ul",[a("li",[t._v("go 导包的过程")]),t._v(" "),a("li",[t._v("go 包的版本管理")]),t._v(" "),a("li",[t._v("go 包的最小版本原则")]),t._v(" "),a("li",[t._v("go module 命令的使用\n"),a("ul",[a("li",[t._v("go get / go install")]),t._v(" "),a("li",[t._v("GOPROXY")]),t._v(" "),a("li",[t._v("GOSUMDB")]),t._v(" "),a("li",[t._v("go 使用私有服务器")]),t._v(" "),a("li",[t._v("workspace")])])])]),t._v(" "),a("h2",{attrs:{id:"go-语言的包导入过程"}},[t._v("go 语言的包导入过程")]),t._v(" "),a("ul",[a("li",[t._v("go 编译快的原因")]),t._v(" "),a("li",[t._v("go 程序的构建过程")])]),t._v(" "),a("p",[t._v("go 程序编译快的原因。1 是因为 go 要求每一个包都必须显式的标记处引入的包。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" hi\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"io"')]),t._v("\n\n "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"github.com/shgopher/hui"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("所以编译器无需去查找这个文件中具体引入了哪些包就可以在头部检索出全部的导包名单")]),t._v(" "),a("p",[t._v("2 是因为 go 语言不允许循环引用,比如说 a 引用了 b,b 又引入了 a,所以每一个包都是独立的存在,处于 “有向无环图 “这种格式下,进而可以并行去编译不同的包,提高编译效率。")]),t._v(" "),a("p",[t._v("3 包编译的结果里 ("),a("code",[t._v("xx.o")]),t._v(","),a("code",[t._v("xx.a")]),t._v(") 中不仅存储了此包的导出信息,还存储了它引入的包的导出信息,这样编译器只需要读取一个目标文件就可以获取全部的包信息,不需要读取全部的文件。比如 p 包要引入 q 包,那么 q 包的。a 文件已经包括了它引入的全部包的导出信息,这样编译器可以快速统计某一个包被引用的情况,比如包引用了 a b,a b 分别引用了 c 但是版本不同,那么这种情况下因为主包已经了解了他们的情况,所以就可以用最小版本原则去选择一个版本的包进行编译即可。有点类似分封制,我帝王 p 包,我的直接负责人是诸侯 q,q 掌握了它下级 s 的所有数据,假设 s 还有下级,s 掌握了 w 的所有数据,然后层层回报,当帝王 p 最终编译的时候,它已经完全拥有了所有的数据,可以精准调控。再结合每一个包都可以独立的并行编译")]),t._v(" "),a("p",[t._v("所以这一整套下来 go 的编译速度就会很快。并且在 go.mod 文件中也会详细记录使用过的包及其版本,那么并行下载,快速编译就不是梦了。")]),t._v(" "),a("h2",{attrs:{id:"go-程序的构建过程。"}},[t._v("go 程序的构建过程。")]),t._v(" "),a("p",[t._v("go 程序的建立是由编译和链接两个阶段组成的,举个例子\n有一个项目拥有一个 main 包,一个 lib 包,lib 包会被引入到 main 包中,那么编译的过程是这样的,首先创建一个临时工作区域,编译 lib 包为 lib.a,编译 main 包为 main.a,链接器将 lib.a 和 main.a 链接成 name.out,然后改名为 name (unix-like),"),a("strong",[t._v("使用第三方包的意思就是链接了该包的源代码编译的最新的。a 文件而已")]),t._v(",并且"),a("strong",[t._v("每次编译")]),t._v("都会重新编译最新的。a 文件 (所以只保留。a 文件,删除源码是不可行的),但是标准库除外,并不会每次编译都会重新编译标准库,所以说如果你修改了源码一定得把标准库。a 文件删除,并且 build 的时候使用 "),a("code",[t._v("build -a")]),t._v(" 的方式才可以使用自己更新的标准库。")]),t._v(" "),a("p",[t._v("一个小 tip:go 导包的时候引入的是路径名称,路径名称"),a("strong",[t._v("通常")]),t._v("最后一位路径名称跟包名保持一致,当然也可以不一致。例如 "),a("code",[t._v("github.com/shgopher/go-hui")]),t._v(" 但是实际上包名称是这个 "),a("code",[t._v("package hui")]),t._v(",所以导入的时候按照 "),a("code",[t._v("github.com/shgopher/go-hui")]),t._v(",使用的时候用 "),a("code",[t._v("hui.xxx()")]),t._v("。")]),t._v(" "),a("p",[t._v("因为包的名称很容易发生冲突,所以 go 接受包名称的重命名")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\n app "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"github.com/shgopher/app"')]),t._v("\n app1 "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"github.com/googege/app"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("除此之外还有 "),a("code",[t._v("_")]),t._v(" 方式,它会"),a("strong",[t._v("计算包级变量的初始化表达式")]),t._v("和"),a("strong",[t._v("执行导入包的 init 初始化函数")]),t._v(",意思就是跟 init 函数有关的内容都会被计算,并且导入进去。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"image"')]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"image/jpeg"')]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"image/png"')]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"io"')]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"os"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("toJPEG")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Stdin"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Stdout"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Fprintf")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Stderr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"jpeg: %v\\n"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t\tos"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Exit")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("toJPEG")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("in io"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Reader"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" out io"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Writer"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("error")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\timg"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" kind"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" image"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Decode")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("in"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" err "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("nil")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" err\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Fprintln")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("os"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Stderr"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"Input format ="')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" kind"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("return")]),t._v(" jpeg"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Encode")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("out"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" img"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("jpeg"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("Options"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("Quality"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("95")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("比如这个例子,image.Decode 会查询注册表,看看注册表里都有谁,这个时候我们引入的 _ iumage/png 的 init 函数就是将 png 中实现了接口的具体数据导入到了注册表中,所以说这里只需要导入这个 init 函数即可。")]),t._v(" "),a("p",[t._v("这种用法还是比较重要的,我们来自己编写一段代码,来看看实现这种注册器的基本原理:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里是实现这个注册器")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" go2\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 在某个包实现go2的时候,这里是go3,这个drivers就被初始化了。")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" drivers "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("make")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("map")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("Driver"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" Driver "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("interface")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Open")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Register")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("name "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" driver Driver"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tdrivers"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" driver\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("DDD")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("name "),a("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("string")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("var")]),t._v(" d Driver\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("if")]),t._v(" d1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" ok "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" drivers"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("name"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(";")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("!")]),t._v("ok "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("panic")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"no real driver registered"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("else")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\td "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" d1\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\td"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Open")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这里是实际的实现我们注册器那个包实现的抽象方法")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" go3\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"shgopher.com/go2"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("type")]),t._v(" ddd "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("struct")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("d "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("*")]),t._v("ddd"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Open")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tfmt"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"this is go3"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("init")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\tgo2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("Register")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"go3"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("&")]),t._v("ddd"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 最后我们来使用一下:")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"shgopher.com/go2"')]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"shgopher.com/go3"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n\tgo2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("DDD")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"go3"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n")])])]),a("p",[t._v("另外还有一种导入包的奇妙用法,就是")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"fmt"')]),t._v("\n\n\t"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("func")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("main")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t\t"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("println")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"hi"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("它的意思是省略包名,no!,请不要这么用。")]),t._v(" "),a("h2",{attrs:{id:"go-module-构建工具"}},[t._v("go module 构建工具")]),t._v(" "),a("ul",[a("li",[t._v("go111MODULE")]),t._v(" "),a("li",[t._v("GOPROXY")]),t._v(" "),a("li",[t._v("GOSUMDB")]),t._v(" "),a("li",[t._v("module 版本的升级")]),t._v(" "),a("li",[t._v("workspace")])]),t._v(" "),a("p",[t._v("go 自带包管理工具 go module,本文章写作时 go 的版本是 1.19,所以过去的 go path,go vendor,go dep 均不再提及,读者也不用去 care,鉴于干谈 go module 非常的枯燥,我设立一个例子来讲述 go module。")]),t._v(" "),a("p",[t._v("在桌面,我们使用 go module 的创建命令")]),t._v(" "),a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("cd")]),t._v(" ~/Desktop\n\n"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("mkdir")]),t._v(" hello\n\ngo mod init github.com/shgopher/hello \n\n")])])]),a("p",[t._v("创立了一个以 hello 作为包名称的包。这里我们使用 "),a("code",[t._v("github.com/shgopher/hello")]),t._v(" 是证明这个包使用 GitHub 进行存储,因为 go 使用 git 来管理版本,在项目内部,这个项目的包名称就是最后一个名称,hello 才是这个包的正式名称,此处是惯用,实际上真实管理包正式名称的是每一个文件上的 package xx 管理的,整个就是路径而已,不过习惯于最后一个路径名称为包的名称,即:最后一个名称 == package 后面的 xxx。")]),t._v(" "),a("p",[t._v("然后我们在这个项目的 root 路径下会发现 go 帮我们创建了一个 go.mod 的文件,它就是 go module 的版本文件,我们拿一个 go.mod 来作为举例")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 一个 go.mod 目录:")]),t._v("\n\nmodule github"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("com"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("shgopher"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("hello\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1.19")]),t._v("\n\n\nrequire "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n github"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("com"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("apache"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("thrift v0"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("13.0")]),t._v("\n github"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("com"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("bytedance"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("gopkg v0"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.0")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("20220531084716")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("665b4f21126f\n github"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("com"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("appleboy"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("gin"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("jwt"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("v2 v2"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("6.4")]),t._v("\n gopkg"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("in"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("yaml"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("v3 v3"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.1")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\nrequire "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\n\tgithub"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("com"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("beorn7"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("perks v1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("0.1")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// indirect")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\nreplace "),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 后面指向的要么是相对路径例如 ../shgopher/.com/net")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 要么一定要在后面加上版本,并且是可以获取到的包")]),t._v("\n\tgolang"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("org"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("x"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("net v1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1.2")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(">")]),t._v(" shgopher"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("com"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("net v1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("4.5")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n")])])]),a("p",[t._v("我们一个一个解释,首先最开头的是这个包的路径名称,实际上 go 会使用 "),a("code",[t._v("git clone https://github.com/shgopher/hello.git")]),t._v(" 的方式下载包,当然,假设你不使用 GitHub 的 git 作为项目的存储位置,使用自建的也是 OK 的,比如 "),a("code",[t._v("module shgopher.com/hello")]),t._v(" 只要同样配置了 git 服务器都可以,因为底层都是使用的 "),a("code",[t._v("git clone https://xxx.com/xx.git")]),t._v(" 模式。")]),t._v(" "),a("p",[t._v("接下来的 "),a("code",[t._v("go 1.19")]),t._v(" 是这个项目使用的 go 的大版本,比如你使用的是 "),a("code",[t._v("1.19.1")]),t._v(","),a("code",[t._v("1.19.2")]),t._v(",上面写的都是 "),a("code",[t._v("go 1.19")]),t._v(",比如你使用 "),a("code",[t._v("go mod edit -go=1.19")]),t._v(" 来更新此项目使用的 go 版本的时候,只能写到 minor,而不能加上 patch (minor 和 patch 下文有说)。")]),t._v(" "),a("p",[t._v("接下里有两个 require,其中第一个 require 指的是直接引入的包,后面的 require 是间接引用的包。意思就是你引用的包,它引入的包。")]),t._v(" "),a("p",[t._v("第一个 require 中,我给出了四种常见的版本用法")]),t._v(" "),a("ol",[a("li",[t._v("使用 git version 命名的版本 "),a("code",[t._v("v0.13.0")])]),t._v(" "),a("li",[t._v("项目没有使用 version 命名,go 官方使用最新的文件,并且给予了它一个临时的版本号 "),a("code",[t._v("v0.0.0-20220531084716-665b4f21126f")])]),t._v(" "),a("li",[t._v("当项目的版本超过 1.x 的时候,go 推荐使用再加上一个/v2 的方式进行命名,但是实际上它的包名称仍然是 gin-jwt,并且 1.x 和 2.x 的包可以同时引入项目中,因为他们算两个包,只需要重命名即可。版本 2 和版本 1 的 module 后面写的也不一样,比如一个是 "),a("code",[t._v("module github.com/shgopher/collie")]),t._v(" 版本 2 就是 "),a("code",[t._v("module github.com/shgopher/collie/v2")]),t._v(" 不过虽然最后结尾的是 v2 但是 package 后面写的仍然是 collie")]),t._v(" "),a("li",[t._v("也可以使用 yaml.v3 的方式进行命名,因为路径名称并不是包的名称,所以这种方式也有不少项目使用")])]),t._v(" "),a("p",[t._v("第二个 require,全部都是间接引用的包名称,go 也会一并下载到本地缓存,go 会使用 "),a("code",[t._v("~/go/pkg/mod/包名称@版本号")]),t._v(" 的地方去保存下载下来的包。")]),t._v(" "),a("p",[t._v("使用 "),a("code",[t._v("go list -m -json all")]),t._v(" 命令可以查看当前项目的所有依赖的包名称,例如")]),t._v(" "),a("div",{staticClass:"language-json extra-class"},[a("pre",{pre:!0,attrs:{class:"language-json"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"Path"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"github.com/marmotedu/iam"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"Main"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"Dir"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/shgopher/Desktop/github-projects/iam"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"GoMod"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/shgopher/Desktop/github-projects/iam/go.mod"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"GoVersion"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"1.18"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"Path"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"cloud.google.com/go"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"Version"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"v0.93.3"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"Time"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"2021-08-17T22:38:11Z"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"Indirect"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("true")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"GoMod"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/shgopher/go/pkg/mod/cache/download/cloud.google.com/go/@v/v0.93.3.mod"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"GoVersion"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"1.11"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"Path"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"github.com/marmotedu/component-base"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"Version"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"v1.6.2"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"Time"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"2021-12-21T06:47:41Z"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"GoMod"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"/Users/shgopher/go/pkg/mod/cache/download/github.com/marmotedu/component-base/@v/v1.6.2.mod"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token property"}},[t._v('"GoVersion"')]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"1.17"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),a("p",[t._v("我们重点关注几个字段,首先第一个对象中,"),a("code",[t._v('"Main":true')]),t._v(",表示这个对象描述的包属于主包,即,这个项目的 root 路径下的 "),a("code",[t._v("go.mod")]),t._v(";第二个对象中,我们看到 "),a("code",[t._v('"Indirect": true,')]),t._v(" 意思就是指这个包是间接引入的包,第三个对象里,并没有出现 indirect 的字段,就证明这个包是直接引入的。因为这个列举的主要是 go.mod 的目录,又因为拥有一个 go.mod 的包拥有同样的版本号,所以包和包的子包是公用一个 go.mod 的,只会出现一次。")]),t._v(" "),a("h3",{attrs:{id:"如何控制-go-引入的包的版本号。"}},[t._v("如何控制 go 引入的包的版本号。")]),t._v(" "),a("p",[t._v("当我们更新一个旧的项目中的依赖时,我们可以使用 "),a("code",[t._v("go clean -modcache")]),t._v(" 的命令去删除 go 缓存的 go 包。然后显式的为包设置版本,第一种方法是直接在 go.mod 写入要引入的包的版本,第二种方法是使用 "),a("code",[t._v("go mod -require=github.com/shgopher/hello@v0.1.1")]),t._v(" 的方式写入你想要的包的版本,除了第二种这种比较精确的方式,go mod 还支持 query 的方式去指定范围,例如说 "),a("code",[t._v("go mod -require=github.com/shgopher/hello@>=v0.1.1")]),t._v("。")]),t._v(" "),a("p",[t._v("go 包采用 “最小版本” 选择的理念。举个例子,我的项目 hello,直接引入了 c 包和 d 包,然后 c 和 d 又分别引入了 e 包,那么最后本项目使用的 c,d,e 包采用的是哪个版本呢?")]),t._v(" "),a("p",[t._v("我们详细说明一下,假设我们使用的 cde 都是@latest,那么每次 build 的时候,都会去下载最新的包;如果我们使用了具体的版本,假设我们在 go.mod 中给定的 c d 分别是 v0.1.0 v1.0.1 但是呢,c 存在多个版本,比如现在有 v0.1.1 v0.2.1 那么根据最小版本的选择问题,go 会选择一个符合 v0.1.0 的最小版本,即:>=v0.1.0,所以此处会选用 v0.1.1,也就是说 go 的版本的隐藏含义是大于等于选最小,很多别的语言都是大于等于选最大,例如 rust,但是强调一下,"),a("strong",[t._v("这个大于等于选最大或者最小说的是一个版本的情况下,超过版本号的时候,算俩包 (例如 v1.1.0 和 v2.1.0 就算俩大版本了,所以说他们是俩包都不为过)")]),t._v(",而且我们在改变版本的时候应该先把缓存给清除了 "),a("code",[t._v("go clean -modcache")]),t._v(",接下来我们的 cd 包分别引入了 e 包,假如 e 存在 v1.1.0 v1.2.0 v1.3.0 v1.4.0,c d 分别引入的是 v1.1.0 v1.3.0 那么 go 会"),a("strong",[t._v("合并需求 (只引入包众多版本的一个版本)")]),t._v(",并且依然采用最小版本的方式即:最终只选用 e 包的 v1.3.0 这一个版本的包引入,总结一下:单个包,使用 >= 的最小值引入,多个包引入同一个包的情况,使用满足他们共同条件下的最小值。")]),t._v(" "),a("p",[t._v("使用 go mod tidy 可以对这个项目的包进行梳理,比如使用 latest 的包会重新比对,然后下载最新版本的包,比如在 require 中明确引入的包,但是在实际上线前发现没有继续使用这个包了,那么使用 go mod tidy 也可以删除这个包,这个命令类似于 “刷新” 这个概念。")]),t._v(" "),a("h3",{attrs:{id:"如何优雅的升级和降级引入的包版本"}},[t._v("如何优雅的升级和降级引入的包版本?")]),t._v(" "),a("p",[t._v("我们会用到下面两个命令")]),t._v(" "),a("ul",[a("li",[t._v("go list")]),t._v(" "),a("li",[t._v("go get")])]),t._v(" "),a("p",[t._v("首先,我们使用 "),a("code",[t._v("go list -m -versions github.com/shgopher/hello")]),t._v(" 的命令查找这个包的众多版本 (没有使用 git 给定版本的就没办法了,给定@latest,直接用最新的了),例如说会有 v1.1.0 v1.2.0,假设我们本来用的是 v1.1.0 想升级一下,我们使用 "),a("code",[t._v("go get github.com/shgopher/hello@v1.2.0")]),t._v(" 即可优雅的升级这个包的版本,当然降级也是一样的,反正指定一个包即可,要注意的是,go 在升级或者降级的时候,会自动将间接使用的包也做出相应的版本调整。假如我们现在想把所有的依赖包升级为这个版本下的最新包,使用 "),a("code",[t._v("go get -u")]),t._v(" 就可以更新所有的直接依赖和间接依赖的包为最新包。如果只想升级 patch 而不是 minor (v1.2.0;1 是 version,2 是 minor,3 是 patch) 使用 "),a("code",[t._v("go get -u=patch")]),t._v(",如果只想升级某一个包 (及其依赖包) 到最新版本,在后面指出来具体的包名称即可,"),a("code",[t._v("go get -u github.com/shgopher/hello")]),t._v("。")]),t._v(" "),a("p",[t._v("go install 和 go get 是两个比较像的命令,其中 go get 仅仅用在 go module 中调整版本的时候,比如升级,降级,go install 用于下载包,两者都是一样的,如果包的后面带上版本那么就是指定版本,如果不带就是最新版本。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" install github"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("com"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("shgopher"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("hello \n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" install github"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("com"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("shgopher"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("hello@v1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("1.2")]),t._v("\n")])])]),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" get "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("u github"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("com"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("shgopher"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("hello\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" get github"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("com"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("shgopher"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("hello@v1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("3.4")]),t._v("\n")])])]),a("p",[t._v("在使用 go install 的时候其实是忽略 go.mod 的,所以 go install 跟 go.mod 没有任何关系,也不会记录 go.mod 中,它的作用就是下载包,go get 跟 go.mod 紧密相关,它需要 go.mod,并且每次更改都会记录在 go.mod 中,当你在一个没有 go.mod 的路径下 (指的是此路径,此路径的父路径也没有) 使用 go get 会提示如下错误")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("mod file not found in current directory or any parent directory"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token char"}},[t._v("'go get'")]),t._v(" is no longer supported outside a module"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\n\tTo build and install a command"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" use "),a("span",{pre:!0,attrs:{class:"token char"}},[t._v("'go install'")]),t._v(" with a version"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v("\n\tlike '"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" install example"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("com"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("cmd@latest'\n\tFor more information"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),t._v(" see https"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("golang"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("org"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("doc"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("get"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("install"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("deprecation\n\tor run '"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" help get"),a("span",{pre:!0,attrs:{class:"token char"}},[t._v("' or '")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),t._v(" help install'"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("\n")])])]),a("p",[t._v("所以这个时候应该使用的是 go install。")]),t._v(" "),a("h3",{attrs:{id:"引入的-go-包-git-更改名称了怎么办"}},[t._v("引入的 go 包 git 更改名称了怎么办?")]),t._v(" "),a("p",[t._v("Go 语言提出了一个模块代号 (Module Path) 的概念来解决这个问题。")]),t._v(" "),a("p",[t._v("简单来说,模块代号就是一个模块的唯一标识,即使模块仓库的路径名改变了,但其模块代号不变。")]),t._v(" "),a("p",[t._v("例如一个模块:")]),t._v(" "),a("p",[t._v("模块路径:"),a("code",[t._v("github.com/user/project")]),t._v("\n模块代号:"),a("code",[t._v("github.com/user/project")]),t._v("\n如果模块 rename 后路径变成了 "),a("code",[t._v("github.com/newUser/newProj")]),t._v(",但模块代号不变:")]),t._v(" "),a("p",[t._v("模块新路径:"),a("code",[t._v("github.com/newUser/newProj")]),t._v("\n模块代号:"),a("code",[t._v("github.com/user/project")]),t._v("\n这样通过模块代号,其他依赖它的模块导入语句就不用修改了,还是导入老的模块代号,但实际上会去新路径下找。")]),t._v(" "),a("p",[t._v("在发布新版本代码时,只需要在 go.mod 文件中声明:")]),t._v(" "),a("p",[a("code",[t._v("module github.com/user/project")])]),t._v(" "),a("p",[t._v("就可以确保模块代号不变,其他依赖不受影响。")]),t._v(" "),a("p",[t._v("所以 Go 语言通过模块代号很好地解决了模块路径变更的问题,最大程度保证了导入的稳定性。")]),t._v(" "),a("h3",{attrs:{id:"go-包代理"}},[t._v("go 包代理")]),t._v(" "),a("p",[t._v("GOPROXY 是 go 的代理服务器。go 之前使用 GitHub,gitlab 等托管平台,goproxy 命令可以设置一个集中式的代理服务,比如")]),t._v(" "),a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("export")]),t._v(" GOPROXY "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" https://goproxy.cn,direct \n\n"),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("export")]),t._v(" GOPROXY "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" https://proxy.golang.org,direct\n\n")])])]),a("p",[t._v("其中前者是国内常用的代理服务器,后者是 go 官方的代理服务器;"),a("code",[t._v("direct")]),t._v(" 的意思是,"),a("strong",[t._v("直接")]),t._v("使用代理服务器的内容,"),a("code",[t._v(",")]),t._v(" 的意思是前面的服务器"),a("strong",[t._v("只有")]),t._v("出现 404 和 410 错误的时候才会去选择逗号后面的服务,如果想设置只要发生错误就使用后者的命令,那么可以使用 "),a("code",[t._v("|")]),t._v(",例如使用")]),t._v(" "),a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[t._v("go "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("env")]),t._v(" -w "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("GOPROXY")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("https://proxy.golang.org"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v("https://goproxy.cn"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v("direct`\n")])])]),a("p",[t._v("这里有个小知识,因为 "),a("code",[t._v("|")]),t._v(" 在 unix-like 操作系统中通常还表示通道的含义,就是前面的输出等于后面的输入,所以我们需要将这个符号进行转义才能正常使用:")]),t._v(" "),a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[t._v("go "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("env")]),t._v(" -w "),a("span",{pre:!0,attrs:{class:"token assign-left variable"}},[t._v("GOPROXY")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v("https://proxy.golang.org"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v("https://goproxy.cn"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("\\")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("|")]),t._v("direct\n\n")])])]),a("p",[t._v("go module 还有 go.sum 这个文件,它存储的是包的一些基础信息,最重要的是对于一个包求 hash 值,记录这个 hash,在每次 build 的时候对于缓存的包和 go.sum 中的 hash 值做对比,来规避恶意更改,go.sum 会在项目的更新换代过程中保存多个版本的包信息。")]),t._v(" "),a("p",[t._v("GOSUMDB 命令指向的服务就是保存公有包的校验和的数据库。一个新的包,在一切运行正确的情况下,go 会通过 GOSUMDB 配置的数据库去查询这个包的校验和,查询出结果后和下载的包进行比对,正确的情况下存入 go.sum;如果一个已经缓存的包,每次 run build 的时候都会将缓存的包文件校验跟 go.sum 进行比对来保证正确性。")]),t._v(" "),a("p",[t._v("当然,如果你不想使用 GOSUMDB,使用 "),a("code",[t._v("go env -w GOSUMDB=off")]),t._v(" 即可。这样就无法对比包和数据库中的校验和,只能做本地校验了。")]),t._v(" "),a("h3",{attrs:{id:"如何使用一个私有包"}},[t._v("如何使用一个私有包")]),t._v(" "),a("p",[t._v("我们讲解了如果配置公有的代理服务器 GOPROXY,文件校验和数据库 GOSUMDB,接下来我们谈一下如果我们想使用一个私有的包,比如一个 GitHub 上的私有包,一个本地 git 服务器上的包,我们使用 GOPRIVATE,它的目的就是绕过 GOPROXY 和 GOSUMDB,因为是私有的所以在代理服务器和文件校验和数据库都不会有它的记录,我们可以这么设置")]),t._v(" "),a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 意思是这个路径下的所有包都不会经过代理服务器了。")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 这个命令支持多个路径使用逗号分隔")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token builtin class-name"}},[t._v("export")]),t._v(" GOPRIVATE "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" shgopher.com,shgopher.io,*.api.shgopher.com\n")])])]),a("p",[t._v("除了设置这个命令之外,还需要设置一个密钥用来 ssh 的方式去访问 GitHub 上的私有仓库,或者是 GitHub --- personal access tokens")]),t._v(" "),a("p",[t._v("使用 ssh,将主机公钥 (~/.ssh/id_rsa.pub) 添加到 github.com 的 ssh keys 中。")]),t._v(" "),a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[t._v("\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("#我们谈一下如果生成公钥:")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("## 在~/.ssh/ 路径下")]),t._v("\n\tssh-keygen -t rsa -C "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"个人邮箱"')]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("## 将这个id_rsa.pub中的公钥 添加到GitHub中的ssh keys 中")]),t._v("\n")])])]),a("p",[t._v("如果使用 ssh 的方式获取代码,那么在~/.gitconfig 中添加 (这一步其实就是一个映射:保持你的日常习惯的情况下,使用了 ssh)")]),t._v(" "),a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("url "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"ssh://git@github.com"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n\tinsteadOf "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" https://github.com\n")])])]),a("p",[t._v("如果是本地服务器那么就是")]),t._v(" "),a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("url "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"ssh://git@local.com"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n\tinsteadOf "),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" https://git.local.com\n")])])]),a("p",[t._v("不过要注意一下,如果使用 ssh 那么远程的服务就得变更名称,因为通常我们的 GitHub 给我们的都是 https 的方式,使用 ssh 的话就是:")]),t._v(" "),a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[t._v(" "),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("git")]),t._v(" remote set-url origin git@github.com:USERNAME/REPOSITORY.git\n")])])]),a("p",[t._v("从 ssh 更改为 https 就是")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("git remote set"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("-")]),t._v("url origin https"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("github"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("com"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("USERNAME"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("REPOSITORY"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("git\n")])])]),a("p",[t._v("使用 GitHub personal access token 的方式")]),t._v(" "),a("div",{staticClass:"language-bash extra-class"},[a("pre",{pre:!0,attrs:{class:"language-bash"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("# 在GitHub personal access tokens中申请即可,然后配置在~/.netrc")]),t._v("\n\nmachine github.com login shgopher password 你的 personal access tokens\n")])])]),a("p",[t._v("在 Linux 中要配置~/.netrc,但在 macOS 中,git 输入的 username 和令牌会自动的缓存,不用设置这个配置文件。")]),t._v(" "),a("p",[t._v("更多关于 GitHub 访问的信息可以访问"),a("a",{attrs:{href:"https://docs.github.com/en/get-started/getting-started-with-git/managing-remote-repositories#switching-remote-urls-from-ssh-to-https",target:"_blank",rel:"noopener noreferrer"}},[t._v("这里"),a("OutboundLink")],1)]),t._v(" "),a("p",[t._v("我个人建议直接使用 GitHub 的 personal access tokens (仅支持 https) 这种令牌的方式代替密码,并且使用 https 即可,简单,安全,好用,\n另外如果你的令牌更新了,假设是 Macos 的情况下,可以去钥匙串访问的互联网密码种类中去更新令牌")]),t._v(" "),a("p",[t._v("如果我们引入的包是不支持 https 协议的,那么我们可以设置 "),a("code",[t._v("GOINSECURE = private.res.com")]),t._v(" 来使用这种私有库。")]),t._v(" "),a("p",[t._v("go 仅仅支持 https 和 http 标准端口的带有域名的包,比如我们使用 ip,或者端口不是 80 和 443,那么设置 GOPROXY 和 GOPRIVATE GOINSECURE 都没用了。")]),t._v(" "),a("p",[t._v("比如类似这种:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 这种无法使用")]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("192.168")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v(".1")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v(".1")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(":")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("9090")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("test"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("t\n")])])]),a("p",[t._v("这种直接使用 ip 的方式不能写到 go 的代码中,所以我们可以使用 git 的 insteadof 功能来改变一下,使用一个正常的 url 去改变掉这个 ip")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[t._v("# ~"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v("gitconfig\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),t._v("url "),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"132.148.1.1:9090/test/t"')]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),t._v("\n\tinsteadof"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"shgopher.com/test/t"')]),t._v("\n")])])]),a("p",[t._v("这个时候你引入的时候写 shgopher.com/test/t 就可以了,它会自己找 192.168.1.1:9090/test/t 真实值去代理,并且 shgopher.com/test/t 也要加入到 GOPRIVATE 中才可以。")]),t._v(" "),a("p",[t._v("这个时候有个问题,就是如果 shgopher.com 没有配置版本管理软件,例如 git 这种,go 是无法获取数据的,go 一般会存储一些网站,例如 GitHub,gitlab,所以它看到类似 github.com/xxx/的地址就去找使用 git,或者你手动写上 git,比如 shgopher/d/x.git go 也会明白你使用的 git,go 还能发送请求的方式去获取你使用的版本软件,比如 golang.org/x/net go 就会请求 https://golang.org/x/net?go-get=1 这个服务提供了一个 html:")]),t._v(" "),a("div",{staticClass:"language-html extra-class"},[a("pre",{pre:!0,attrs:{class:"language-html"}},[a("code",[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("head")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("meta")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("name")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("go-import"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("content")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("golang/x/net git https://go.googlesource.com/net"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("meta")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("name")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("go-source"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),t._v(" "),a("span",{pre:!0,attrs:{class:"token attr-name"}},[t._v("content")]),a("span",{pre:!0,attrs:{class:"token attr-value"}},[a("span",{pre:!0,attrs:{class:"token punctuation attr-equals"}},[t._v("=")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')]),t._v("golang.org/x/net https://github.com/golang/net"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v('"')])]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token tag"}},[a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("<")]),t._v("head")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(">")])]),t._v("\n")])])]),a("p",[t._v("这表示这个 golang.org/x/net 的包实际是通过 git 的方式从 https://go.googlesource.com/net 获取的。")]),t._v(" "),a("p",[t._v("我们可以让 "),a("code",[t._v("GET shgopher.com?go-get=1")]),t._v(" 返回一个 html,在里面设置为 content=“shgopher.com/test/t git 132.148.1.1:9090/test/t”。")]),t._v(" "),a("p",[t._v("其实,老老实实的使用 go 推荐的导包方式挺好的,即便是私有包也好好的安排一个 git 版本服务器,仅限内部使用的话,配置好 GOPRIVATE 并且使用 ssh 公钥和私钥或者 GPG 的方式加密获取包,这种才是王道。")]),t._v(" "),a("p",[t._v("配置私有的 GOPROXY,你不能总想着靠别人,如果想搭建一个属于自己的 goproxy 服务器,那么可以使用 https://github.com/goproxy/goproxy 这个项目")]),t._v(" "),a("h3",{attrs:{id:"本地开发使用的配置文件-go-work"}},[t._v("本地开发使用的配置文件 go.work")]),t._v(" "),a("p",[t._v("go 推出了仅用于本地开发的 workspace,我们来介绍一下这个功能,比如说我们要在一个项目中你引入一个还未公开到公共仓库的包 github.com/shgopher/hui,那么在这个时候就需要 workspace 了。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("package")]),t._v(" main\n\n"),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("import")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token string"}},[t._v('"github.com/shgopher/hui"')]),t._v("\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("很显然这个包还未发布,所以不可能引入,在之前通常使用 replace 的方式,但是有了 workspace 以后,不到特定的场景就不需要使用 replace,比如尚未发布的包这种场景用 workspace 最好用 (包的子包不需要任何设置,就可以主包直接引入这个子包了,这一点要搞清楚)。")]),t._v(" "),a("p",[t._v("使用 go work init 来创建一个工作区,go.work 形如:")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//go.work")]),t._v("\ngo1"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),a("span",{pre:!0,attrs:{class:"token number"}},[t._v("19")]),t._v("\n\n"),a("span",{pre:!0,attrs:{class:"token function"}},[t._v("use")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(".")]),t._v(" "),a("span",{pre:!0,attrs:{class:"token comment"}},[t._v("//go.work当前路径是可以省略的")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("Users"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("ddd"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("src"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),a("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("go")]),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("hui\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n\n")])])]),a("p",[t._v("go.work 可以设置在需要工作区的路径的父路径 (如果父路径没有就会一直忘外寻找直到根路径),工作区中的命令会向外部去寻找 go.work,所以我们通常可以在需要 workspace 的地方的父路行下设置 go.work,并且设置为绝对路径这样简单高效,记住,路径是不包含子路径的,比如本来是/workspace/go1 但是你设置的是/workspace 那么就是错的,它会寻找/workspace 中有没有 go.mod 它以 go.mod 作为寻找对象。当然了一个包的子包 (例如/workspace/go1/a) 无需再写进去 go.work 中,它跟外部的包是一个包。")]),t._v(" "),a("div",{staticClass:"language-go extra-class"},[a("pre",{pre:!0,attrs:{class:"language-go"}},[a("code",[a("span",{pre:!0,attrs:{class:"token function"}},[t._v("use")]),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("(")]),t._v("\n\t"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("workspace"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("a\n\t"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("workspace"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("b\n\t"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("workspace"),a("span",{pre:!0,attrs:{class:"token operator"}},[t._v("/")]),t._v("c\n"),a("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(")")]),t._v("\n")])])]),a("p",[t._v("在工作区同样可以设置 replace,但是级别没有 go.mod 中的高,会被 go.mod 覆盖。")]),t._v(" "),a("p",[t._v("go 的包和包的管理工具 go module 基本上已经讲解完毕了,以后有了新的见解再更新这篇文章。")]),t._v(" "),a("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),a("ul",[a("li",[t._v("https://go.dev/blog/get-familiar-with-workspaces")]),t._v(" "),a("li",[t._v("https://book.douban.com/subject/35720728/ 图书下册 321 页 - 349 页;图书上册 120 页 - 131 页")])])])}),[],!1,null,null,null);s.default=e.exports}}]); \ No newline at end of file diff --git a/assets/js/94.bd1a1b8b.js b/assets/js/94.bd1a1b8b.js new file mode 100644 index 000000000..94cd49a63 --- /dev/null +++ b/assets/js/94.bd1a1b8b.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[94],{523:function(t,s,e){"use strict";e.r(s);var n=e(36),r=Object(n.a)({},(function(){var t=this.$createElement,s=this._self._c||t;return s("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}},[s("h1",{attrs:{id:"反射包的使用"}},[this._v("反射包的使用")])])}),[],!1,null,null,null);s.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/95.471eafef.js b/assets/js/95.471eafef.js new file mode 100644 index 000000000..5ea52c2bb --- /dev/null +++ b/assets/js/95.471eafef.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[95],{525:function(t,o,s){"use strict";s.r(o);var v=s(36),a=Object(v.a)({},(function(){var t=this,o=t.$createElement,s=t._self._c||o;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h1",{attrs:{id:"命令"}},[t._v("命令")]),t._v(" "),s("p",[t._v("go 拥有众多命令操作,这里将讲述关于这些命令的使用方法")]),t._v(" "),s("p",[t._v("介绍一下最常见的命令")]),t._v(" "),s("ul",[s("li",[t._v("go help 显示一个命令基本的用法,例如:"),s("code",[t._v("go help fmt")])]),t._v(" "),s("li",[t._v("go doc 显示一个命令全部的用法,例如:"),s("code",[t._v("go doc cmd/gofmt")])])]),t._v(" "),s("p",[t._v("使用 go help 可以显示全部的形如 "),s("code",[t._v("go fmt")]),t._v(" "),s("code",[t._v("go build")]),t._v(" 这种挂靠在 go 后面的命令,然后 help 加具体的命令,就可以显示基本用法,然后在 help 提示的内容中,通常会有提示你,如果使用 go doc 命令去寻找更加详细的内容,比如下文要写到的,使用 "),s("code",[t._v("go help fmt")]),t._v(" 就会显示去寻找 "),s("code",[t._v("go doc cmd/gofmt")])]),t._v(" "),s("h2",{attrs:{id:"gofmt"}},[t._v("gofmt")]),t._v(" "),s("blockquote",[s("p",[t._v("go fmt 命令简单封装了 gofmt 命令")])]),t._v(" "),s("p",[t._v("gofmt 的目的是标准化 go 语言的代码,增加代码的亲切感,消除不同人员写的代码的之间的隔阂感")]),t._v(" "),s("p",[t._v("介绍几个常见的使用方法,详细内容可以使用 "),s("code",[t._v("go doc cmd/gofmt")])]),t._v(" "),s("ul",[s("li",[s("p",[s("code",[t._v("gofmt -s")]),t._v(" 简化代码")]),t._v(" "),s("div",{staticClass:"language-go extra-class"},[s("pre",{pre:!0,attrs:{class:"language-go"}},[s("code",[t._v("v "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v(":=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("[")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("]")]),s("span",{pre:!0,attrs:{class:"token builtin"}},[t._v("int")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("1")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("2")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v(",")]),s("span",{pre:!0,attrs:{class:"token number"}},[t._v("3")]),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 复杂")]),t._v("\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token boolean"}},[t._v("_")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token operator"}},[t._v("=")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" v "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token comment"}},[t._v("// 使用 -s 后")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("for")]),t._v(" "),s("span",{pre:!0,attrs:{class:"token keyword"}},[t._v("range")]),t._v(" v "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("{")]),t._v("\n\n "),s("span",{pre:!0,attrs:{class:"token punctuation"}},[t._v("}")]),t._v("\n")])])]),s("p",[t._v("不过,这个命令虽然会显示出来要优化的简单写法,但是,并不会更改用户的代码,需要自己去更改。")])]),t._v(" "),s("li",[s("p",[s("code",[t._v("gofmt -r")]),t._v(" 代码重构 replace 能力")]),t._v(" "),s("p",[t._v("例子:"),s("code",[t._v("gofmt -r 'a -> Student'")]),t._v(" 意思可不是 a 字符改变成 Student,这里是采用的通配符,意思就是所有的英文字符都要改成 student。只要是小写字母都会被视为通配符。再举一个例子,"),s("code",[t._v("gofmt -r 'a[b:len(a)] -> a[b:]'")]),t._v(" 这里的 a 代表所有的英文字符串,b 就会代表整数类型")])]),t._v(" "),s("li",[s("p",[s("code",[t._v("gofmt -l")]),t._v(" 输出不满足 gofmt 要求的文件")]),t._v(" "),s("p",[t._v("比如 "),s("code",[t._v("gofmt -l $GOROOT")]),t._v(" 就会输出这个路径下不满足的文件列表,可以看出 go 的标准库不满足标准的也不少,😂")])])]),t._v(" "),s("h2",{attrs:{id:"goimports"}},[t._v("goimports")]),t._v(" "),s("p",[t._v("安装方法 "),s("code",[t._v("go get golang.org/x/tools/cmd/goimports")]),t._v(",一般的 IDE 都会内置这个工具,比如 goland")]),t._v(" "),s("ul",[s("li",[t._v("对于代码中使用了,但是没有 import 的包")]),t._v(" "),s("li",[t._v("对于代码中没有使用,但是 import 了的包")])]),t._v(" "),s("p",[t._v("这个工具都会一一管理,少了加上,多了取消掉")]),t._v(" "),s("h2",{attrs:{id:"go-build"}},[t._v("go build")]),t._v(" "),s("h2",{attrs:{id:"go-install"}},[t._v("go install")]),t._v(" "),s("h2",{attrs:{id:"go-get"}},[t._v("go get")]),t._v(" "),s("h2",{attrs:{id:"go-clean"}},[t._v("go clean")]),t._v(" "),s("h2",{attrs:{id:"go-doc-godoc"}},[t._v("go doc godoc")]),t._v(" "),s("h2",{attrs:{id:"go-run"}},[t._v("go run")]),t._v(" "),s("h2",{attrs:{id:"go-test"}},[t._v("go test")]),t._v(" "),s("h2",{attrs:{id:"go-list"}},[t._v("go list")]),t._v(" "),s("h2",{attrs:{id:"go-fix-go-tool-fix"}},[t._v("go fix go tool fix")]),t._v(" "),s("h2",{attrs:{id:"go-vet-go-tool-vet"}},[t._v("go vet / go tool vet")]),t._v(" "),s("h2",{attrs:{id:"go-tool-pprof"}},[t._v("go tool pprof")]),t._v(" "),s("h2",{attrs:{id:"go-tool-cgo"}},[t._v("go tool cgo")]),t._v(" "),s("h2",{attrs:{id:"go-env"}},[t._v("go env")])])}),[],!1,null,null,null);o.default=a.exports}}]); \ No newline at end of file diff --git a/assets/js/96.216ae8b8.js b/assets/js/96.216ae8b8.js new file mode 100644 index 000000000..847eb1cd6 --- /dev/null +++ b/assets/js/96.216ae8b8.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[96],{527:function(t,s,e){"use strict";e.r(s);var n=e(36),i=Object(n.a)({},(function(){var t=this.$createElement,s=this._self._c||t;return s("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}},[s("h1",{attrs:{id:"坑"}},[this._v("坑")]),this._v(" "),s("p",[this._v("这里记录 go 语言开发过程中,非常容易出现错误的地方。")])])}),[],!1,null,null,null);s.default=i.exports}}]); \ No newline at end of file diff --git a/assets/js/97.46e63812.js b/assets/js/97.46e63812.js new file mode 100644 index 000000000..41a38ec16 --- /dev/null +++ b/assets/js/97.46e63812.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[97],{526:function(t,s,e){"use strict";e.r(s);var n=e(36),r=Object(n.a)({},(function(){var t=this.$createElement,s=this._self._c||t;return s("ContentSlotsDistributor",{attrs:{"slot-key":this.$parent.slotKey}},[s("h1",{attrs:{id:"字符编码"}},[this._v("字符编码")])])}),[],!1,null,null,null);s.default=r.exports}}]); \ No newline at end of file diff --git a/assets/js/98.e08b5049.js b/assets/js/98.e08b5049.js new file mode 100644 index 000000000..2123d9ad1 --- /dev/null +++ b/assets/js/98.e08b5049.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[98],{528:function(t,i,v){"use strict";v.r(i);var _=v(36),s=Object(_.a)({},(function(){var t=this,i=t.$createElement,v=t._self._c||i;return v("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[v("h1",{attrs:{id:"go-中的测试"}},[t._v("go 中的测试")]),t._v(" "),v("p",[t._v("go 中的测试一共分为如下类型")]),t._v(" "),v("ul",[v("li",[t._v("测试的主要注意事项")])]),t._v(" "),v("h2",{attrs:{id:"testing-包"}},[t._v("testing 包")]),t._v(" "),v("ul",[v("li",[t._v("基础测试")]),t._v(" "),v("li",[v("RouterLink",{attrs:{to:"/工程/测试/基准测试.html"}},[t._v("基准 (性能) 测试")])],1),t._v(" "),v("li",[t._v("例子测试")]),t._v(" "),v("li",[t._v("子测试/子基准测试")]),t._v(" "),v("li",[t._v("main 函数测试")]),t._v(" "),v("li",[v("RouterLink",{attrs:{to:"/工程/测试/模糊测试.html"}},[t._v("模糊 (fuzzing) 测试")])],1),t._v(" "),v("li",[t._v("mock")])]),t._v(" "),v("h2",{attrs:{id:"testing-fstest-包"}},[t._v("testing/fstest 包")]),t._v(" "),v("ul",[v("li",[t._v("文件系统的测试")])]),t._v(" "),v("h2",{attrs:{id:"testing-iotest-包"}},[t._v("testing/iotest 包")]),t._v(" "),v("ul",[v("li",[t._v("io 系统的测试")])]),t._v(" "),v("h2",{attrs:{id:"testing-quick-包"}},[t._v("testing/quick 包")]),t._v(" "),v("ul",[v("li",[t._v("黑箱辅助测试")])]),t._v(" "),v("h2",{attrs:{id:"net-http-test"}},[t._v("net/http/test")]),t._v(" "),v("ul",[v("li",[t._v("http 客户端测试")]),t._v(" "),v("li",[t._v("http 服务端测试")])]),t._v(" "),v("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),v("ul",[v("li",[t._v("https://mp.weixin.qq.com/s/41ux7cA-GXYTfeS5juP3wA")]),t._v(" "),v("li",[t._v("https://github.com/golang/mock")])])])}),[],!1,null,null,null);i.default=s.exports}}]); \ No newline at end of file diff --git a/assets/js/99.ad21ff40.js b/assets/js/99.ad21ff40.js new file mode 100644 index 000000000..9f0cf04a6 --- /dev/null +++ b/assets/js/99.ad21ff40.js @@ -0,0 +1 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[99],{529:function(t,e,s){"use strict";s.r(e);var r=s(36),n=Object(r.a)({},(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("ContentSlotsDistributor",{attrs:{"slot-key":t.$parent.slotKey}},[s("h1",{attrs:{id:"基准测试"}},[t._v("基准测试")]),t._v(" "),s("h2",{attrs:{id:"hey"}},[t._v("hey")]),t._v(" "),s("h2",{attrs:{id:"ethr"}},[t._v("ethr")]),t._v(" "),s("h2",{attrs:{id:"参考资料"}},[t._v("参考资料")]),t._v(" "),s("ul",[s("li",[t._v("https://mp.weixin.qq.com/s/kKNSKdPoeAWUXbwC_tFINw")])])])}),[],!1,null,null,null);e.default=n.exports}}]); \ No newline at end of file diff --git a/assets/js/app.c50a697f.js b/assets/js/app.c50a697f.js new file mode 100644 index 000000000..dceb5fd0e --- /dev/null +++ b/assets/js/app.c50a697f.js @@ -0,0 +1,17 @@ +(window.webpackJsonp=window.webpackJsonp||[]).push([[0],[]]);!function(t){function e(e){for(var r,a,l=e[0],u=e[1],c=e[2],f=0,E=[];f=n.length?{value:void 0,done:!0}:(t=r(n,o),e.index+=t.length,{value:t,done:!1})}))},function(t,e,n){var r=n(7),o=n(12),i=n(41);t.exports=r?function(t,e,n){return o.f(t,e,i(1,n))}:function(t,e,n){return t[e]=n,t}},function(t,e){var n=Array.isArray;t.exports=n},function(t,e,n){var r=n(0),o=n(4),i=n(54),a=r.TypeError;t.exports=function(t){if(o(t))return t;throw a(i(t)+" is not a function")}},function(t,e,n){var r=n(0),o=n(169),i=n(170),a=n(140),l=n(20),u=n(5),c=u("iterator"),s=u("toStringTag"),f=a.values,E=function(t,e){if(t){if(t[c]!==f)try{l(t,c,f)}catch(e){t[c]=f}if(t[s]||l(t,s,e),o[e])for(var n in a)if(t[n]!==a[n])try{l(t,n,a[n])}catch(e){t[n]=a[n]}}};for(var p in o)E(r[p]&&r[p].prototype,p);E(i,"DOMTokenList")},function(t,e,n){var r=n(176),o="object"==typeof self&&self&&self.Object===Object&&self,i=r||o||Function("return this")();t.exports=i},function(t,e,n){var r=n(0).TypeError;t.exports=function(t){if(null==t)throw r("Can't call method on "+t);return t}},function(t,e,n){var r=n(2),o=r({}.toString),i=r("".slice);t.exports=function(t){return i(o(t),8,-1)}},function(t,e,n){var r,o=n(11),i=n(142),a=n(106),l=n(55),u=n(147),c=n(72),s=n(76),f=s("IE_PROTO"),E=function(){},p=function(t){return" + + diff --git a/runtime/gmp/index.html b/runtime/gmp/index.html new file mode 100644 index 000000000..1ed50a4ab --- /dev/null +++ b/runtime/gmp/index.html @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git a/runtime/index.html b/runtime/index.html new file mode 100644 index 000000000..3174b492a --- /dev/null +++ b/runtime/index.html @@ -0,0 +1,50 @@ + + + + + + runtime | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git a/runtime/netpool/index.html b/runtime/netpool/index.html new file mode 100644 index 000000000..a5ae1c2fe --- /dev/null +++ b/runtime/netpool/index.html @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/runtime/\344\270\211\350\211\262gc\347\256\227\346\263\225/index.html" "b/runtime/\344\270\211\350\211\262gc\347\256\227\346\263\225/index.html" new file mode 100644 index 000000000..f2dfeeb4b --- /dev/null +++ "b/runtime/\344\270\211\350\211\262gc\347\256\227\346\263\225/index.html" @@ -0,0 +1,50 @@ + + + + + + 参考资料 | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/runtime/\345\240\206\345\206\205\345\255\230\345\210\206\351\205\215/index.html" "b/runtime/\345\240\206\345\206\205\345\255\230\345\210\206\351\205\215/index.html" new file mode 100644 index 000000000..be04b5dde --- /dev/null +++ "b/runtime/\345\240\206\345\206\205\345\255\230\345\210\206\351\205\215/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/runtime/\345\256\232\346\227\266\345\231\250/index.html" "b/runtime/\345\256\232\346\227\266\345\231\250/index.html" new file mode 100644 index 000000000..6c1240216 --- /dev/null +++ "b/runtime/\345\256\232\346\227\266\345\231\250/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/runtime/\346\240\210\345\206\205\345\255\230\347\256\241\347\220\206/index.html" "b/runtime/\346\240\210\345\206\205\345\255\230\347\256\241\347\220\206/index.html" new file mode 100644 index 000000000..ce2c30060 --- /dev/null +++ "b/runtime/\346\240\210\345\206\205\345\255\230\347\256\241\347\220\206/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/runtime/\347\263\273\347\273\237\347\233\221\346\216\247/index.html" "b/runtime/\347\263\273\347\273\237\347\233\221\346\216\247/index.html" new file mode 100644 index 000000000..197da219d --- /dev/null +++ "b/runtime/\347\263\273\347\273\237\347\233\221\346\216\247/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\237\272\347\241\200/helloWorld/index.html" "b/\345\237\272\347\241\200/helloWorld/index.html" new file mode 100644 index 000000000..4eb1156cb --- /dev/null +++ "b/\345\237\272\347\241\200/helloWorld/index.html" @@ -0,0 +1,64 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

hello world

诞生背景

Go 语言是 Google 的 Robert Griesemer,Rob Pike 和 Ken Thompson 于 2007 年开始设计开发的一门编程语言。Go 语言吸收了 C 语言的表达能力,并添加了像动态语言中的并发性等特性,目的是让编程更简单高效。

语言哲学

Go 语言编程哲学如下:

  • 面向接口编程
  • 组合替换继承
  • 简单优于复杂
  • 内置并发支持

语言特点

Go 语言的主要特点有:

  • 语法简单,容易学习使用
  • 编译非常迅速,执行速度较快
  • 静态类型语言 - 变量和函数需要声明类型
  • 垃圾回收 - 自动的内存管理,不需要手动释放内存
  • 并发支持 - 原生支持 goroutine 和 channel
  • 简洁规范 - 语法简单一致,容易阅读理解
  • 代码风格统一 - 提供了标准的代码风格

安装 Go

要安装 Go 语言,可以去官网 https://go.dev 下载最新的安装包。

安装非常简单,一般直接使用默认设置即可完成安装。

安装完成后,可以使用 go version 命令检查安装是否成功。

hello world

package main
+
+import (
+	"fmt"
+)
+
+func main() {
+	ch := make(chan struct{})
+	go func() {
+		fmt.Println("Hello World")
+		ch <- struct{}{}
+	}()
+	<-ch
+}
+
+ + + diff --git "a/\345\237\272\347\241\200/index.html" "b/\345\237\272\347\241\200/index.html" new file mode 100644 index 000000000..e60dc04bf --- /dev/null +++ "b/\345\237\272\347\241\200/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\237\272\347\241\200/interface/index.html" "b/\345\237\272\347\241\200/interface/index.html" new file mode 100644 index 000000000..a490266b8 --- /dev/null +++ "b/\345\237\272\347\241\200/interface/index.html" @@ -0,0 +1,714 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

接口

有关泛型相关的约束内容均放置在泛型这一章节,这里不再赘述

重点内容前情提要

  • 方法集合决定接口的实现
  • 接口的嵌入
  • 类型断言
  • 接口类型的底层
  • 如何判断接口类型的相等
  • 论述 “nil error != nil” 的原因
  • 小接口的意义
  • 不可滥用空接口
  • 接口作为程序水平组合的连接点,提供程序的可扩展性
  • 接口提供程序的可测试性
  • 接口的严格对比函数的宽松

接口,go 语言提供的,用于抽象以及数据解耦的组件,在操作接口时,go 语言要求的严格程度远大于函数和方法。

在 go1.18+ 中接口的概念从包含抽象的方法变成了类型,但是我们在外部可以操作的接口仍然只能是经典接口,不过经典接口是可以当做约束在内部使用的,不过官方不推荐经典接口当做一般约束那种使用方式。

go 语言为了区分,将传统接口称之为接口,将扩展的接口称之为约束,实际上传统接口是约束概念的子集。扩展的约束并不能在函数或者方法以及类型之外使用。

这一章我们只介绍经典接口的基本概念和使用方法。

接口具有静态语言和动态语言的共同特点。

静态类型特点:

  • 接口可以作为类型:var e error
  • 支持在编译期进行类型检查:在编译器期间会对右边的变量进行类型的检查,检查是否实现了接口定义的方法类型中的所有方法

动态类型的特点:

  • 在运行时可以对接口进行动态赋值
  • 接口类型可以在运行时被赋予不同的动态类型变量,从而进行 “多态”

方法集合决定接口的实现

通常,我们使用下面这种形式去完成接口的实现

type Writer interface {
+	Write([]byte) (int, error)
+}
+type BufferWrite struct {
+	value bytes.Buffer
+}
+
+func(b *BufferWrite) Write(p []byte) (int, error){}
+

我们知道,go 语言规定,不可跨包去实现类型的方法,所以我们只讨论自定义的类型与接口之间的关系,首先抛出结论:松耦合关系。

go 语言使用一种叫做鸭子理论的方式去实现接口:只要实现了接口的方法 (go 泛型,go1.18+, 理论从方法改为了类型) 就算是实现了这个接口,这属于隐式接口实现。即:接口和它的实现者之间为正交关系。

通常来说,例如 es6,Java 等都是要显式的说明实现了哪个接口的,但是 go 不需要,它只需要实现方法即可,而且,它还可以实现多个接口,毕竟多实现几个方法就可以算是实现了这个接口。由此还可以推断出,go 可以多实现方法,而不会影响对接口的实现。

这里要说明一下,go 语言对于定义在指针类型上的变量有语法糖:

type Writer interface {
+	get()
+	set()
+}
+type Student struct{}
+func(s Student) get() {}
+func(s *Student) set() {}
+
+func main(){
+	var t Student
+	var tp = new(Student)
+	var w Writer 
+	// ❌
+	w = t
+	// ✅
+	w = tp
+
+}
+

我们可以看到,get 和 set 分别是一个值类型和一个指针类型上实现的,这里我们的结论是:当实现接口时,类型的指针变量在实现方法上可以包括定义在类型指针上的方法以及定义在值类型上的方法***,但是值类型变量只包含定义在值类型*上的方法

这里是提示信息:

cannot use t (variable of type Student) as Writer value in assignment: Student does not implement Writer (method set has pointer receiver)
+

通常来说,这不应该成为程序员的烦恼,所以想用谁就好好的定义在谁上面的方法即可,完全不会出错。

接口嵌入

在接口中嵌入接口类型

type Writer interface {
+	Write([]byte) (int, error)
+	error()
+}
+type Error1 interface {
+	error()
+}
+type WriterError interface {
+	Writer
+	Error1
+}
+

这样的组合就可以组合成一个新的接口,并且嵌入的接口还可以有方法上的交集,go 是不介意的 (go1.14+)。

在结构体中嵌入接口类型

在结构体中嵌入一个接口,就相当于实现了这个接口 (当然结构体的指针类型也实现了这个接口),拥有了它的方法,但是注意,拥有的是这种抽象方法,那么我们为什么要将一个接口类型嵌入到一个结构体中呢?

因为 go 语言规定,嵌入接口,结构体相当于实现了这些接口,这里注意,这里必须是直接嵌入,如果是接口类型作为变量类型的方式是不能拥有接口的方法的。

type A interface{
+	get()
+	set()
+}
+
+// ✅
+type A1 struct{
+	A
+}
+// ❌
+type A2 struct{
+	A A
+}
+// ❌
+type A3 struct{
+	a A
+}
+

当结构体本身也实现了方法时,优先调用结构体的方法。

这个场景是这样的,在某个函数中,它的参数是一个接口类型,并且这个函数调用的只是这个接口类型的某个,或几个方法,并不是全部,那么我们作为结构体,想实现这个接口,又不想多实现额外的方法,那么这种方法就很好用了。


+package main
+
+import "fmt"
+
+func main() {
+	var a = new(A)
+	D(a)
+}
+
+type A struct {
+	BI
+}
+
+type BI interface {
+	get()
+	set()
+}
+
+func (A) get() {
+	fmt.Println("hi")
+	// 如果想调用被嵌入的接口的方法可以这么用,
+	// 我们知道调用直接嵌入的对象时候,变量名称默等于后面的类型名称,
+	// 看下面演示
+	// var a A
+	// a.BI.get()
+}
+
+func D(b BI) {
+	b.get()
+
+}
+
+

注意上文提到了,接口中内嵌接口的时候,内嵌的接口方法可以重复,但是结构体中内嵌的接口,不允许出现方法重复的问题:

// ❌ : ambiguous selector a.get
+type A struct{
+	BI
+	BI1
+}
+type BI interface{
+	get()
+	set()
+}
+type BI1 interface{
+	get()
+	err()
+}
+

不过,要想解决这个问题,我们只需要让结构体实现这种重复的方法即可,这样,优先级就提升到了结构体,接口的方法就不会被调用了。比如:

package main
+
+import "fmt"
+
+func main() {
+	var a A
+	D(a)
+	D1(a)
+}
+
+type A struct {
+	BI
+	BI1
+}
+type BI interface {
+	get()
+	set()
+}
+type BI1 interface {
+	get()
+	err()
+}
+
+func (A) get() {
+	fmt.Println("hi")
+}
+
+func D(b BI) {
+	b.get()
+
+}
+
+func D1(b BI1) {
+	b.get()
+}
+
+

下面让我们看一下,这种用法在单元测试场景中的应用

让我们描述一个场景:

有一个函数,它接受一个接口类型作为参数,我们要对它进行单元测试,而且我们要伪造一些数据。

// 函数体
+func MaleCount(s stmt)(int,error){
+	result,err := s.Exec("SELECT count(*) FROM exployee_tab WHERE gender=?","1")
+	if err != nil {
+		return 0,err
+	} 
+	return result.Int(),nil
+}
+// 抽象接口
+type stmt interface {
+	Close error
+	NumInput()int
+	Exec(stmt string,args ...string)(Result,error)
+	Query(args []string)(Rows,error)
+}
+// 接口相关的一些数据
+type Result struct{
+	Count int
+}
+func(r Result) Int()int{return r.Count}
+
+type Rows []struct{}
+

我们可以看到,要想对这个 MaleCount 函数进行处理,那么一个实现了 stmt 接口的动态类型必不可少,但是我们并不需要所有的方法,仅仅需要 Exec 方法。

所以我们第一步就是设置一个 fake 类型,并且将接口内嵌来完成 “继承”。

type fakeStmtForMaleCount struct{
+	stmt
+}
+// 这里实际上只是简写,
+//真正的测试要对smt和arg进行测试的
+func(f fakeStmtForMaleCount)Exec(stmt string,args ...string)(Result,error){
+	return Result{1},nil
+}
+

当我们内嵌完成继承之后,我们相当于拥有了这些抽象方法,然后我们在这个接口体上自行实现 Exec,这样就可以将结构体的 Exec 优先级提前。

那么让我们开始使用虚假数据 Result{1} 开始测试 MaleCount 函数

func TestEmployeeMaleCount(t *testing.T) {
+	fs := fakeStmtForMaleCount{}
+	v,err := MaleCount(fs)
+	if err != nil {
+		t.Error("error is :",err)
+	}
+	if v != 1  {
+		t.Errorf("we want %d, actual is %d",1,v)
+	}
+}
+

匿名接口

与普通命名接口不同,匿名接口没有类型名,只通过方法集来定义接口

匿名接口的作用主要有:

  • 临时使用,不需要命名
  • 作为参数或返回值,减少接口命名
func doSomething(v interface{ Get() int }) {
+   // ...
+}
+
+func returnInterface() interface{ Foo() string } {
+   // ...
+   return x
+}
+

类型断言

当使用空接口作为类型参数的时候,空接口已经充盈了一个动态类型,如果我们要将这个空接口类型转化为原来的类型就需要断言。

常规的断言方式:

func main() {
+	var a any
+	a = 12
+	// 第二个参数 ok 变量可以省略
+	// v := a.(int)
+	// fmt.Println(v)
+	if v, ok := a.(int); ok {
+		fmt.Println(v)
+	}
+}
+

存在于 switch 中的断言方式:

func main() {
+	var f any
+	f = "12"
+	// (type)里面的type为固定用法,不能更改。
+	switch v := f.(type) {
+	case string:
+		fmt.Println(v)
+	default:
+		fmt.Println("NO")
+	}
+}
+

断言类型也可以是接口

断言中的类型不止是实际类型比如 int 比如一个具体的 struct,还能是接口类型,比如一个接口类型:


+type MyAget interface {
+	Age() int
+} 
+func IsMyAget(e error)bool{
+	if _ ,ok:=e.(MyAget);ok{
+		return true
+	}
+	return false
+}
+

这个函数的意义是这样的,接受一个 error 接口类型的对象,然后断言看它是否是 MyAget 接口类型。

type Age struct {
+	value string
+	e error
+}
+func (a *Age) Age() int {
+	return 10
+}
+func(a *Age)Error() string {
+	return a.value
+}
+func main(){
+	IsMyAget(&Age{"10",nil})
+}
+

接口类型的底层

这是接口的底层数据:

一般接口:

// interface
+type iface struct {
+	tab *itab
+	data unsafe.Pointer
+}
+

空接口:

// empty interface
+type eface struct {
+	_type *_type
+	data unsafe.Pointer
+}
+

data 表示的意思一样,值是动态类型的地址。我们比较 value 时,比较的是地址指向的数据是否相同而不是地址本身。

空接口并没有定义接口的方法,因此 _type 定义的均为动态类型的元数据

type _type struct {
+	size uintptr
+	ptrdata uintptr
+	hash uint32
+	tflag tflag
+	align uint8
+	fieldalign uint8
+	kind uint8
+	alg *typeAlg
+	gcdata *byte
+	str nameOff
+	ptrToThis typeOff
+
+}
+

一般接口因为本身定义了方法,因此它需要定义自己的方法,以及动态类型的数据,因此它除了 _type 外,还定义了 interfacetype 用来存储自己定义的方法元数据。

type itab struct {
+	// 非空接口本身的信息
+	inter *interfacetype
+	// 动态类型数据
+	_type *_type
+	// _type.hash的copy,用于 switch 判断类型
+	hash  uint32
+	_     [4]byte
+	// 动态类型已实现接口方法的调用地址数组
+	fun   [1]uintptr
+}
+

注意这里的 fun 数组,这里定义的 [1]uintptr 在实际使用时,可能不是 [1],这里的数据时可变的,如果是 2,就表示实现了两个方法。

原文的注释是这样的 // variable sized. fun[0]==0 means _type does not implement inter.

type interfacetype struct {
+	// 接口本身的类型信息
+	typ _type
+	// 接口所在的包路径
+	pkgpath name
+	// 接口方法集合
+	mhdr []imethod
+}
+
+

如何判断接口类型的相等

当接口类型未被赋予动态类型时,它的两个字段,即:动态类型字段和动态类型 value 字段均为 nil,那么这个未初始化的接口变量就恒等于 nil

当接口类型被赋予了动态类型,那么如果判断这时候的接口类型,必须为类型相同以及值相同,接下来我们看一个案例:

两个非空非 nil 接口变量比较:

func main() {
+    printNonEmptyInterface1()
+}
+
+type T struct {
+    name string
+}
+func (t T) Error() string {
+    return "bad error"
+}
+func printNonEmptyInterface1() {
+    var err1 error    // 非空接口类型
+    var err1ptr error // 非空接口类型
+    var err2 error    // 非空接口类型
+    var err2ptr error // 非空接口类型
+
+    err1 = T{"eden"}
+    err1ptr = &T{"eden"}
+
+    err2 = T{"eden"}
+    err2ptr = &T{"eden"}
+    println("err1:", err1)
+    println("err2:", err2)
+    println("err1 = err2:", err1 == err2)             // true
+    println("err1ptr:", err1ptr)
+    println("err2ptr:", err2ptr)
+    println("err1ptr = err2ptr:", err1ptr == err2ptr) // false
+}
+
err1: (0x104c959a8,0x1400004c748)
+err2: (0x104c959a8,0x1400004c728)
+err1 = err2: true
+err1ptr: (0x104c95988,0x1400004c738)
+err2ptr: (0x104c95988,0x1400004c758)
+err1ptr = err2ptr: false
+

println,预定义函数,在编译期间,会由编译器根据要输出的参数的类型,将 println 替换为特定的函数,这些预定义函数定义在 runtime/print.go (opens new window) 中,针对 eface 和 iface 的打印函数是: +go func printeface(e eface){ print(e._type, e.data) } go func printiface(i iface){ print(i.tab, i.data) }

如代码所示,我们要判断的是,非空接口,并且已经实现了动态类型的两组接口类型,答案已经写在代码里了,即:err1 == err2 err1ptr != err2ptr

现在就让我们从源码出发来探究一下原因。

首先,我们知道类型相同以及值相同才是真的相等,err1 和 err2 的动态类型均为 T,值也均为 T{"eden"},所以他们相等;err1ptr,和 err2ptr 的类型均为 *T,值均为 &T{"eden"},但是系统却判断他们不相等,从源码来看,二者的 tab *itab,因为动态类型的元数据相同,这个字段一致,所以类型一致,从第二个字段 data 来说,data 存储了 &T{"edent"} 的地址,这个地址指向的内容仍为地址,从内容上来说地址指向的地址值并不相同,所以这就可以解释为什么结果是 false 了。

接下来我们看一下两个空非 nil 接口类型的比较:

func main() {
+	var a any
+	var b any
+	a = &S{
+		"1",
+	}
+	b = &S{
+		"1",
+	}
+	//(0x102f8aaa0,0x1400004c758)
+  //(0x102f8aaa0,0x1400004c748)
+	println(a)
+	println(b)
+	// false
+	println(a == b)
+}
+
+type S struct {
+	name string
+}
+

根据源码所知,a 和 b 的 _type 是完全相同的,然而地址指向的地址不相同,所以结果是 false,下面让我们稍微改动一下:

func main() {
+	var a any
+	var b any
+	a = &S{
+		
+	}
+	b = &S{
+		
+	}
+// (0x104422aa0,0x1400004c767)
+// (0x104422aa0,0x1400004c767)
+	println(a)
+	println(b)
+	// true
+	println(a == b)
+}
+
+type S struct {
+	
+}
+

这个时候你惊奇的发现,结果竟然是 true,这是为什么呢?不是说,地址指向的地址应该是不同的吗?nonono,并不是所有的情况都是那样,如果结果是空接口,那么空接口的所有变量指向的都是同一个地址,所以从结果上来说,data 其实地址是相同的,指向的是同一个数据,所以答案是 true。

在 Go 中,空数据结构 (比如 struct {}) 不占用任何内存空间,因此在创建空数据结构时,它们实际上是指向同一个地址的。这是因为在 Go 中,每个变量都需要分配内存空间,以便可以存储它们的值。但是,由于空结构体没有任何字段,因此它们不需要分配任何内存空间。因此,在创建空结构体时,它们实际上是指向同一个已经分配的零大小内存块的指针。

一个非空接口类型和一个空接口类型一定不相等吗?

如果你根据源码来看,第一个字段本身就不一样,肯定不相等了,但是 go 在比较相等时,比较的是 _type 字段,并不是全部的 tab 数据,所以当两者字段中的 _type 相同就表示类型相同:

func main() {
+	var a any = S{6}
+	var b B  = S{6}
+	//(0x1040dfae0,0x1400004c760) 
+	//(0x1040e5a68,0x1400004c758)
+  //true
+	println(a,b)
+	println(a == b)
+}
+type S struct{
+	int
+}
+type B interface{
+	get()
+}
+func(S)get(){}
+

所以从结果来看,_type 字段相同均为 S,data 也是一致的,所以答案是 true

nil 接口类型:

func main() {
+	var e error
+	var a any
+	//(0x0,0x0)
+	println(a)
+	//(0x0,0x0)
+	println(e)
+	println(e == nil)
+	println(a == nil)
+	println(a == e)
+}
+

当一个接口是未给定动态类型的接口类型,它就是 nil 接口,那么它的类型和 data 值均为空,所以只要是 nil 接口,他们均相等,并且等于 nil。

最后说明一下,当接口类型获取动态类型的时候,绝大多数情况下,会将动态类型的值复制,并且放置在一个新的内存空间里,所以原始数据跟接口类型的数据再无瓜葛,指针类型除外,不过为了节省空间,有一种情况,go 编译器就会放弃这个动作,并不会每次都重新分配。

func main() {
+	var x any = 34
+	var y any = x
+	var z any = x
+// (0x1023343e0,0x10232c1b0)
+// (0x1023343e0,0x10232c1b0)
+// (0x1023343e0,0x10232c1b0)
+	println(x)
+	println(y)
+	println(z)
+}
+

可以看到,go 判断,x y z 三个空接口类型的动态类型,类型均相同都是 int,并且 data 指向同一块内存地址。

非空接口也是一样:

func main() {
+	var a1 a = b{}
+	var a2 a = a1
+// 	(0x102791a48,0x1400004c758)
+//  (0x102791a48,0x1400004c758)
+	println(a1)
+	println(a2)
+}
+
+type a interface {
+	get()
+}
+type b struct {
+	name string
+}
+
+func (b) get() {}
+

我们如果想获取关于接口的内部实现细节,可以看一下这个项目 (opens new window),可以输出内部的信息

小接口的意义

接口越小,抽象程度越高,使用范围也就越大

一群飞禽走兽,我们可以给他们的行为抽象为 “飞行”,一群能游泳的动物我们可以给他们的行为抽象为 “游泳”。那么飞行和游泳涵盖的内容就会非常的多,使用范围就会很大,比如我们现在有一个函数,要求对所有能飞行的动物做出打印动作,那么众多飞行的动物就都可以使用这个函数。

func main() {
+	e1 := e{"大鹅"}
+	y1 := y{"老鹰"}
+	// {大鹅} 他们的具体行为模式是: 大鹅慢慢的飞
+	PrintFlyer(e1)
+	// {老鹰} 他们的具体行为模式是: 老鹰迅速飞行
+	PrintFlyer(y1)
+}
+
+type Flyer interface {
+	Fly() (flyMod string)
+}
+
+func PrintFlyer(f Flyer) {
+	fmt.Println(f, "他们的具体行为模式是:", f.Fly())
+}
+
+// 大鹅
+type e struct {
+	name string
+}
+
+func (e) Fly() string {
+	return "大鹅慢慢的飞"
+}
+
+// 老鹰
+type y struct {
+	name string
+}
+
+func (y) Fly() string {
+	return "老鹰迅速飞行"
+}
+

易于实现和测试

当接口的方法较少时,动态类型实现的方法就少了,必然容易实现以及容易测试。

高內聚,易于复合组合

我们抽象程度很高的接口,接口做的事情就很单一,比如飞行类的接口方法就是飞行,游泳动物的接口方法就只有游泳,当有会游泳也会飞行的动物时,我们只需要成立一个新的接口,将飞机类和游泳类的接口嵌入到新接口中就形成了一个全新的会飞行也会游泳的接口了。

如果一个接口涵盖了各种方法,那么当组合接口的时候,势必某些方法是被弃用的,所以综上所述,设置单一的,高内聚的方法是好的设计方案。

如何设计小接口

  1. 先初步抽象出接口,这个时候可以有耦合,也可以不够高抽象,但是你得先定义出一个初步的接口出来,与此同时我们也得清楚,越是业务代码,抽象出一个高内聚的接口越难。

  2. 将大接口拆分为小接口,使用一段时间以后,我们会发现某些操作是可以单出被提取出来的,比如 io 包的 writer 和 reader,那么我们就可以把这个动作单独抽象出来。抽象的最高程度就是只有一个方法,这就非常的內聚了,可以说,这种程度的抽象在日常业务中还是相对比较难的,需要在长时间的使用中,慢慢摸索。

综上所述:现搞出一个能用的大接口再说,以后慢慢解耦,形成抽象程度更高的小接口。

不可滥用空接口

空接口和非空接口最大的差异性其实不止是底层数据的不同,我们知道他们一个是 eface 一个是 iface,最重要的差距是,非空接口在编译期是会对接口变量要赋值的动态类型做编译检查的,也就是说会对实参进行检查,来确定他真的实现了这个接口定义的方法,这就是一次安全的保护屏障

与此同时,空接口并没有提供任何的保护屏障,他没有给编译器提供要检查的参数,因此我们说不要滥用空接口,因为它让你的代码缺少编译期间的安全检查屏障。

因为空接口的不安全性,我们可以得出一下结论

  • 尽量不使用空接口
  • 仅仅在未知类型的时候使用空接口
  • 如果存在已知类型,并且类型较多的情况下,可以使用泛型编程
  • 尽可能的抽象出带有方法的接口,并使用非空接口去作为函数参数

接口作为程序水平组合的连接点,提供程序的可扩展性

在 go 语言中,一切皆组合,不过组合分为两种:

  1. 垂直组合,也就是类似接口的嵌入,结构体的嵌入,这种类型的组合被称之为垂直组合
  2. 水平组合,接口是水平组合的关键,当函数使用接口作为参数之后,实际参数可以无限制的水平扩展,只要我们传入的变量实现了这个接口

垂直组合的三种方法:

  • 往接口中嵌入接口实现新接口
  • 往结构体中嵌入接口,实现接口体实现接口这个操作
  • 往接口体中嵌入结构体,实现新的结构体这个操作

水平组合的几种形式:

基本形式:

函数或者方法的参数是接口类型,接口类型作为连接点,将多个包的数据连接在一起。这种方法满足了 “依赖抽象”,“里氏替换原则”,“接口隔离” 等代码设计原则。

依赖抽象原则 (Dependency Inversion Principle,DIP):高层模块不应该依赖于低层模块,两者都应该依赖于抽象。抽象不应该依赖于具体实现,具体实现应该依赖于抽象。(接口本身就是一种抽象,高层模块和底层模块都依赖接口这个抽象组件组合在一起) +里氏替换原则 (Liskov Substitution Principle,LSP):子类对象应该能够替换掉程序中的任何父类对象。也就是说,在任何需要父类对象的地方,都可以使用子类对象来替换,而不会影响程序的正确性。(接口动态类型将替换接口变量) +接口隔离原则 (Interface Segregation Principle,ISP):客户端不应该依赖于它不需要的接口。也就是说,一个类对另外一个类的依赖应该建立在最小的接口上。(go 推崇最小接口模式)

这些原则都是为了提高代码的可维护性、可扩展性和可重用性。它们可以帮助我们设计出更加灵活、健壮和易于维护的代码。

func Read(r io.Reader,cap int64)
+
+func Copy(r io.Writer,src io.Reader)
+

包裹函数:

函数或者方法,接受一个接口类型的参数,返回值也是这个接口类型。

func LimitReader(r Reader,n int)Reader{return &A}
+
+type A struct {
+	name string
+}
+func(*A)Reade(p []byte)(n int,err error){
+	//
+}
+

我们可以定义类似的内容,来实现链式调用。例如

CapReader(LimitReader(AReader(r)))
+

适配器函数类型:

将一个普通函数,转化为一个满足接口类型的动态类型:

http.ListenAndServe(":80",http.HandlerFunc(greeting))
+
+func greeting(w http.ResponseWriter,r *http.Request){
+//
+}
+
+type Handler interface{
+	ServeHTTP(ResponseWriter,*Request)
+}
+
+type HandlerFunc func(ResponseWriter,*Request)
+
+func(f HandlerFunc) ServeHTTP(w ResponseWriter,r *Request){
+	f(w,r)
+}
+
+

这就是适配器函数类型。http.HandlerFunc 就是这个适配器函数。

中间件:

func main(){
+	http.ListenAndServe(":80",aHandler(bHandler(http.HandlerFunc(greeting)))
+}
+

具体的实现过程可以参考这里

通常来说,web 编程中的中间件就是这么用的,使用 pipline 的方法,使用修饰器模式 (包裹函数) 然后链式调用,使用 http.HandlerFunc 进行适配器 (也就是类型的转换) 进而实现中间件的功能。

接口提供程序的可测试性

主要思想就是从引入一个结构体变成引入一个接口,这样就可以完美的解耦上下的数据。

下面有一个场景,使用接口来降低耦合达到可以测试函数的目的。

首先我们先看一下原版:

import(
+	"example.com/s/cache"
+)
+
+func AddA(name string, year int){
+	
+	cache.Add(name, year)
+}
+

我们发现,整个数据耦合在一起了,如果想替换掉 cache 包,那就是不可能的,下面我们使用接口将 AddA 改造一下。

type Ader interface{
+	Add(name string, year int)
+}
+
+func AddA(a Ader,name string,year int){
+	a.Add(name, year)
+}
+

下面我们实现一下这个接口

type ExampleAd struct {
+	name string
+	year int
+}
+func(d *ExampleAd)Add(name string, year int) {
+	d.name = name
+	d.year = year
+	cache.Add(d.name, d.year)
+}
+

最后的运行

a := new(ExampleAd)
+
+AddA(a,"liming",13)
+	
+

接口的严格和函数的宽松对比

接口的实现是严格的:在实现接口的时候函数需要显示转换 (适配器模式)

func main() {
+  http.ListenAndServe(":8080", http.HandlerFunc(hi))
+}
+
+func hi(w http.ResponseWriter, r *http.Request) {
+  fmt.Fprintf(w, "hi")
+}
+
+

这是正确的用法,不能因为 hi 跟 http.HandlerFunc 底层一样,就认为它俩相等,因为 http.HandlerFunc 实现了接口,并不代表 hi 实现了接口。

实际上是不等于的关系,需要显式的转换一下。

接下来让我们看一下刚才代码原理:

type b interface{
+  add(int,int)int
+}
+
+type a func(int,int)int
+
+func(a1 a)add(x,y int)int{
+  return a1(x,y)
+}
+
+func t (x,y int)int{
+  return x+y
+}
+
+func main() {
+  var bb b = a(t)
+  bb.add(1,2)
+}
+

函数的使用是宽松的。当直接使用函数,以及 return 函数的的时候,(其它引用类型也一样:slice,map,func,interface,chan) 是不需要显式转换的,只有非引用类型比如 int,bool string struct ... 需要。

// 不需要显示的转换
+// 或者也可以说:系统自动把这个匿名函数推导为了b类型。
+
+// 函数类型 return 
+type b func(string) int
+
+func get2() b {
+	return func(s string) int {
+    println(s)
+		return len(s)
+	}
+}
+
+// 函数类型 直接使用
+func main() {
+	var a = func(int)string{
+		return "hello"
+	} 
+	get(a)
+}
+
+type N func(int)string
+
+func get(n N){}
+
+// 当然你如果显式的转换一下也是没有问题的
+// 或者说,你人为的帮它推导出了类型是b类型
+func get3() b {
+	return b(func(s string) int {
+		return len(s)
+	})
+}
+

不过除了函数等引用类型之外的非引用类型还是必须显示的转换的

// 整数类型
+type a int
+
+func get1() int{
+	var a1 a
+	a1 = 12
+	return int(a1)
+}
+
+// struct 也需要显示的转换
+type b struct {
+  i int
+}
+
+type b1 b
+
+func get7()b1{
+  return b1{i:1}
+}
+
+// 或者是
+
+func get8() b1 {
+  return b1(b{i:1})
+}
+
+

issues

interface 如何判断 nil

只有类型是 nil + value 是 nil 的接口类型才是 nil,否则它不等于 nil

package main
+
+import (
+	"fmt"
+)
+
+func main() {
+
+	var a1 a
+	// true
+	fmt.Println(a1 == nil)
+	var b1 *b
+	a1 = b1
+	// false
+	fmt.Println(a1 == nil)
+
+}
+
+type a interface {
+	get()
+}
+
+type b struct{}
+
+func (*b) get() {}
+

论述 “nil error != nil” 的原因

nil error 通常可以用这种方法来输出:

func main() {
+	var a error = (*b)(nil)
+	//(0x102205988,0x0)
+	println(a)
+	//false,原因主要是动态类型赋予了这个指针类型具体的类型了,只是没有类型的值而已
+	println(a == nil)
+}
+
+type b struct {
+	error
+}
+
+

可以发现,nil error 的类型并不是 0x0,而 nil 接口变量是 0X0,0x0,所以这两者并不相同。

eface 和 iface 的区别

eface 和 iface 的第二个字段相同均存储的是动态类型的地址,然而 eface 的第一个字段保存的是动态类型的元数据,即:_type 字段,而 iface 的第一个字段不仅仅保存了动态类型的元数据 _type,还保存了自己的方法集合的相关数据,以及动态类型实现的方法地址等数据。

如何查找 interface 中的方法

除了查找文档,以及查看源码,还可以通过反射来查找 interface 中的方法。

type MyInterface interface {
+    Method1() 
+    Method2()
+}
+//这里就是将nil转化为*MyInterface类型,elem()是一个非常重要的方法, Elem返回接口 v 包含的值或指针 v 指向的值
+t := reflect.TypeOf((*MyInterface)(nil)).Elem()
+for i := 0; i < t.NumMethod(); i++ {
+    m := t.Method(i)
+    fmt.Println(m.Name) 
+}
+

使用反射,判断某个类型是否实现了某个接口:

package main
+
+import (
+	"fmt"
+	"io"
+	"os"
+	"reflect"
+)
+
+func main() {
+	// As interface types are only used for static typing, a
+	// common idiom to find the reflection Type for an interface
+	// type Foo is to use a *Foo value.
+	writerType := reflect.TypeOf((*io.Writer)(nil)).Elem()
+
+	fileType := reflect.TypeOf((*os.File)(nil))
+	fmt.Println(fileType.Implements(writerType))
+
+}
+

interface 设计的优缺点

优点:

  • 可扩展性:程序拥有横向扩展的能力
  • 松耦合:接口可以实现代码的松耦合,因为接口只是定义了一组方法,而不是实现,因此它们可以被许多不同的类型实现,这使得代码更加灵活。
  • 可测试性:使用接口编写的代码更容易进行单元测试,因为可以提供模拟对象来模拟接口所定义的方法。这有助于在代码库中提供更高的测试覆盖率。
  • 可读性:使用接口编写的代码更容易阅读和理解,因为可以通过接口名称和方法签名来查看类型所提供的功能。

缺点:

  • 过度设计:如果使用不当,接口可能会导致过度设计,这可能会令代码库变得复杂、难以理解和维护。因此,在设计接口时应该遵循简单性原则,仅定义必要的方法
  • 性能损失:使用接口可能会导致一些性能损失,因为在运行时需要进行类型断言和方法查找。虽然这种影响通常很小,但在高性能场景下可能会有所不同。
  • 难以理解:对于新手来说,理解接口的概念和使用可能会比较困难,这可能会导致一些代码可读性差的问题。

空接口类型和一般类型是从属关系吗?

func age(value any){}
+var a bool 
+// ❌
+age(a)
+

上面的代码就表示,空接口类型和一般的类型是平级关系,不能说 any 被所有类型实现了,就说 any 是所有类型的父类,这是错误的。所以当我们的 age 中 value 是 any 类型,那么我们传入的数据的类型也得是 any 类型,不然不就是类型错误了吗

那么我们学的,所有类型都实现了空接口这句话如何使用呢,很简单,让任何类型转化为 any 类型即可,任何类型转化为 any 类型之后,就会变成 any 类型,并不是之前它的类型。

所以这里的代码应该这么写:

func age(value any){}
+var b bool =  true
+var a any = b // 所以这里的 true 从 布尔类型 直接变成了any类型 
+// ✅
+age(a)
+

即便你直接传入字面量也可以:

func main() {
+	age(1) // 这里的 字面量 1 其实就是 any 类型  并不是所谓的int或者uint
+}
+
+func age(value any) {
+	fmt.Println(value)
+}
+

参考资料

  • https://book.douban.com/subject/35720728/ 246 页 - 286 页
  • https://mp.weixin.qq.com/s/6_ygmyd64LP7rlkrOh-kRQ
  • https://github.com/golang/go/blob/master/src/runtime/runtime2.go
  • https://github.com/golang/go/blob/master/src/runtime/type.go
+ + + diff --git "a/\345\237\272\347\241\200/map/index.html" "b/\345\237\272\347\241\200/map/index.html" new file mode 100644 index 000000000..c9cb9ec0b --- /dev/null +++ "b/\345\237\272\347\241\200/map/index.html" @@ -0,0 +1,149 @@ + + + + + + map | GOFamily - go 程序员宝典 + + + + + + + + +

map

导读:

  • map 的基本操作
  • map 基础知识
  • map 底层知识
  • iussues

map 的基本操作

因为 go 使用拉链法去构造 go 语言的 map,所以只要内存不被消耗光,map 中的元素是无限的。跟 slice 不同,map 不分 lenth 和 cap,在 make 中只有一个位置,这个位置表示的含义就是 cap

map 的基础操作

func main() {
+	// 声明一个新的map
+	var m map[string]int
+	// 给引用类型map分配底层数据结构
+	m = map[string]int{} // or m = make(map[string]int,100)
+	// 插入数据
+	m["hello"] = 1
+	if v, ok := m["hello"]; ok {
+		fmt.Println(v)
+	}
+	// 在初始化的时候无法初始化两个一样的key,这个检查是编译器就开始的
+	m = map[string]int{"a":1,"a":2} // error
+	// 在初始化的时候无法初始化两个一样的key,但是如果是一个变量的话是可以的,
+	// 因为编译器时无法获取变量的值的,所以这种方式通过。
+	m = map[string]int{a:1,a:2} // 这样是正确的。
+	// 遍历数据
+	for k, v := range m {
+		fmt.Println(k, ":", v)
+	}
+	// 删除数据,不存在这个数据不会panic
+	delete(m, "hello")
+	// 删除 map 所有条目
+	clear(m)
+
+}
+

map 基础知识

映射的第一步就是把键值转化为哈希值 (通常是一个很大的整数),然后根据哈希值的映射,把 key-value 本体,以及对应的哈希值存储在哈希筒中,go 使用链表法充当哈希筒,当寻找值的时候,通过哈希计算,以及取模映射,先查找到哈希筒,然后使用哈希值的方式去寻找有没有符合的哈希值,如果找到了,那么再使用 key 原来的值二次比较,这一步主要是为了规避哈希碰撞,即:俩 key 算出来的哈希值是一样的这种行为。

因为存 == 这种行为,所以 go 语言 map 的键值是有局限的,不可以是函数类型、字典类型和切片类型,如果是接口类型,传入的实际类型也不能是函数,字典和切片,例如

package main
+
+func main() {
+	_ = map[interface{}]int{
+		[]int{1, 2}: 2, // panic
+		"1": 1,
+	}
+
+}
+
+

**虽然,map 的 key 可以设置为接口,但是最好不要这么干,因为会在运行时引入风险,比如上述代码就是风险。同样的,如果 key 是数组类型,亦或者是 struct,参数值都不能是函数,切片和 map。**无论被埋藏的有多深,都不能出现切片,map,函数这几种类型,比如说 [10][2][]string,运行时都会看出来。

那么用什么类型的作为 key 值是比较推荐的呢?

这里有两个关键词:求哈希的速度,判断相等的速度,基本原理是越简单的类型速度越快,比如 bool,int8,就比 int64 快,因为 int8 单个值只占了一个字节,int64 是 8 个字节,所以越复杂的越慢,比如一个 struct,因为求一个 struct 的哈希值,需要对里面的字段都进行哈希计算,然后合并起来,大大影响了速度,所以优先选用数值类型和指针类型 (因为指针类型也就是一个 16 进制的正整数而已),如果选 string,最好短一些的比较好

在内存不爆炸的情况下,map 的 key 是无限量的,随意添加。

map 有一个最佳实践是使用形如 value,ok := map[key] 的 “comma OK” 的方法去获取 map 的值,OK 的意义就是为了获得 key 是否存在这个 map 里,因为就算不存在,map 也不会报错或者 Panic,返回的是这个值的零值,比如 int 就返回 0,那如果某一个 key 刚好结果是 0 就说不清了对吧,所以引入了这个 “comma ok” 机制,另外还存在一个不存在也不会 Panic 的操作,就是使用 delete(map,key) 的方法去删除 key 值

map 的遍历跟 slice 一样,使用 m := range map 的方法,但是输出的顺序是不固定的,如果想输出稳定的值,可以将 range 改成普通的 for 循环,然后 key 值使用一个切片存储,这样的话,读取 key 值的时候顺序就是固定的了。

map 底层知识

当 map 使用字面量的方式进行初始化的时候,数量小于25时,它会首先启用 make 关键字创建一个 map,然后使用 m["k"] = v 的方式进行初始化,当超过 25 的时候,其实也差不多,它会使用两个切片分别存储 key 和 value,然后使用 m[k] = v 的方式进行初始化,实际上 go 的复合类型使用字面量进行初始化基本上都是这种方式,基本上的流程就是使用关键字创建底层数据,然后使用最简单的方式进行 k-v 赋值,所以你也可以把字面量的赋值当作一种语法糖。

map 的语法在运行时会转化为另一套对应关系,这个转化是在编译器搞定的。

m := make(map[string]int, 10) -> m := runtime.makemap(maptype, 10, m)// maptype 下文有解释
+
+v := m["hello"] -> v := runtime.mapaccess1(maptype,m , "hello")  // 这里实际上引入的是 "hello"的 指针
+
+v,ok := m["hello"] -> v,ok := runtime.mapacess2(maptype,m , "hello")
+
+m["hello"] = 12 -> v := runtime.mapassign(maptype,m , "hello")
+
+delete(m,"hello") -> runtiem.mapdelete(maptype,m , "hello")
+
+

hmap 是一个 struct,这个结构体拥有众多字段,它用来描述这个 map 底层应用具有的所有字段。可以理解为它是描述 map 的头部文件,存储了所有的字段,但是并不存储真实的 body。

type hmap struct {
+	count     int 
+	flags     uint8
+	B         uint8  
+	noverflow uint16 
+	hash0     uint32 
+	buckets    unsafe.Pointer  // 重点
+	oldbuckets unsafe.Pointer 
+	nevacuate  uintptr        
+	extra *mapextra 
+}
+
+type mapextra struct {
+	overflow    *[]*bmap
+	oldoverflow *[]*bmap
+	nextOverflow *bmap
+}
+
+// 这是一个桶本身的数据结构,但是在运行时阶段会重塑这个结构体,添加更多的字段
+type bmap struct {
+	tophash [bucketCnt]uint8
+}
+// 这是添加字段以后的样子
+// 所以三个数组就完成了数据的存储,也可以避免padding的发生
+type bmap struct {
+    topbits  [8]uint8
+    keys     [8]keytype
+    values   [8]valuetype
+    pad      uintptr
+    overflow uintptr
+}
+
  • count:当前 map 的 value 个数,len 返回的就是这个值
  • flags:map 的状态标志 iterator oldterator hashwriting samesizegrow
  • B:2 ^ B = 桶数量
  • noverflow:指的是 overflow 的桶的数量
  • hash0:哈希函数的种子值,用作哈希函数的参数,引入随机性
  • buckets:指向桶数组的指针
  • oldbuckets:在 map 扩容阶段指向前一个桶的指针
  • nevacuate:map 扩容阶段充当扩容进度计数器,所有下标号小于 nevacuate 的桶都是已经完成了数据排空和迁移的操作的
  • extra:【此字段是可选字段】如果有 overflow 的桶出现,该字段保证 overflow 的桶不会被 gc,具体操作就是该字段存储所有指向 overflow 的桶的指针

当声明一个 map 的时候,运行时就会为这个 map 具体的变量生成一个实例,上门那个是 map 的描述符号,这里说的这个数据结构是定义这个 map 中所有元信息的。


+type maptype struct {
+	typ    _type
+	key    *_type
+	elem   *_type
+	bucket *_type // 表示hash bucket 内部的类型
+	// function for hashing keys (ptr to key, seed) -> hash
+	hasher     func(unsafe.Pointer, uintptr) uintptr
+	keysize    uint8  // size of key slot
+	elemsize   uint8  // size of elem slot
+	bucketsize uint16 // size of bucket
+	flags      uint32
+}
+

go 的 map 只有一套 method,只要使用这个 maptype 的具体不同实现就可以满足操作。比如 map[int]int 和 map[int]string,他们都使用 maptype 来定义自己的元信息,但是操作这这个元信息的函数是一个,只要取不同的 typ key 这种指针类型取操作就可以了。

存储数据的 body 本体是由一个 类似二维数组 + 链表 组成的,具体的图像如下:

tophash 区域就是为了寻找 key 和 value 使用空间换时间的原理做的索引,当我们把 hashcode 的高位值逐一比较的时候就可以确定 key 和 value 的位置。

key 和 value 都是连续的内存区域,key 和 value 在一个桶中只能存在 8 个,多余的在不满足扩容的情况下就会存储在溢出桶中,寻找 key 和 value 使用 tophash 的位置即可,go 使用分开存储 tophash,key,value 的方式,所以 go 就避免了数据 padding,满足了内存对齐,避免了内存的浪费。需要注意的是,当 key 和 value 大于 128 个字节的时候,存储的就是他们的指针

map 的扩容有一个具体的衡量指标,负载因子,当 hmap 中的 count > 负载因子 * 2^B (map 的负载因子为 6.5),或者溢出桶过多的时候,就会扩容。当因为溢出桶太多的时候,创建的新 map 的桶和现有的桶一样,当因为不满足负载因子导致的扩容时,会创建两倍于现有 bucket 的新 bucket,但是旧的桶 data 并不会立刻被迁移到新的 bucket 中,在 map 进行插入和删除的过程中旧的内容会被逐步迁移到新的桶中。原来旧的内容就会被 gc 掉。

普通 map 的扩容并不是原子性的,所以 map 的扩容过程会去检查是否已经处于扩容中。

当持续向 map 中写入数据,并且删除,然后继续写入删除,这个时候如果没有造成装载因子的超出,就会造成溢出桶过多的情况,这个时候就会造成内存的泄漏,所以会创建一个一样大的 map,存储没有被删除的数据,并且将那些被标记为 delete 的数据进行垃圾回收,没错,你进行 delete 的时候并没有真的 delete,只有 gc 以后才是真的 delete。

将旧的数据放在 runtime.hmap 中的 oldbuckets 字段上,然后将新的数据结构放在 buckets 上,溢出桶的设置也是一样的,因为 extra 指向的数据结构也是三个字段,老的,新的,正在使用的。

运行时会将一个旧桶的数据分流到两个新的桶子里,但是如果是内存泄漏的等量扩容时,就只会把一个旧桶的数据导入到一个新的桶子里,请注意这里的迁移指的是拷贝,在计数器计算数据已经被分流完全以后,旧的桶和旧的溢出桶的数据就会被 gc 掉

因为存在旧桶和新桶,所以在查找数据的时候,会先从旧桶查找数据,如果没有再去新的桶中查找,当我们写入和删除数据的时候,除了写入的新数据到新的桶中,也会把旧的桶中的一部分数据拷贝到新的桶中,当然了不会拷贝全部,这是为了效率考虑的,

issues

问题一: map 元素可以取地址吗?

不能,map 元素 (例如 ma [“12”]) 属于结果值,所以无法获取地址

问题二: map 可以并发读写吗?可以 recover 吗?

map 是线程不安全类型,读写得加互斥锁;被 recover 的 Panic 有几项是不能的:

  • 数据竞争 (比如:对 map 进行并发读写)

可以通过 go 的编译标记 race 对代码进行检测是否存在数据竞争

  • 内存不足出现的 Panic
  • 死锁出现的 Panic

问题三: sync.Map 适合的场景,和 map 加锁的区别

sync.Map 在读多写少性能比较好,否则并发性能很差

有关 sync.Map 的详细内容

map 不支持并发的读或者是写 (go1.6 以后就不支持并发的读和写了,之前的版本支持并发的读,但是不支持并发的写),所以 map+锁性能在读多写少和读少写多,读和写一样多的情况下是一样的。

最优解是使用多把锁即:分段锁的方式,并发读写,大幅提高性能

问题四: 在值为 nil 的字典上执行读操作会成功吗,那写操作呢?

答案是在值为 nil 的字典写会 Panic,读是没问题的。

package main
+
+import "fmt"
+
+func main() {
+	var a map[int]int
+	fmt.Println(a[1]) // 结果是int的一个初始值 0 
+  a[0] = 1 // panic
+}
+
+

问题五: 为什么不用向下寻址式?

我们知道解决哈希碰撞的问题有向下寻址法,链表法,这是因为哈希函数只能把数据尽可能分布的均匀,如果哈希函数的输出的范围大于输入的范围,这是不现实的,这就要求映射无穷多,这显然不可能,所以必然会有两个不同的 key 算出来的哈希值是相同的,那么如果很多 key 算出的哈希值都是一样的,这就出现了查找效率从 O(1) 下降到了 O(n) 这就是所谓的哈希冲突。

这里提到的哈希值一样,有可能是后几位一样,也就是部分一样,比如后 9 位相同

向下寻址法的意思是:依次向下一位去探究是否是要找的哈希对,当然插入的时候也是这,向没有数据的地方插入,所以这种方法必须要使用数组这种结构,而且遍历的时候还得使用循环数组的这种思想 index := hash("author") % array.len,开放寻址法有一个数据指标叫做装载因子,就是说元素的个数/数组长度,一旦装载因子大于 70 %乃至 90 % 基本上就倒退为了 O(n) 的时间复杂度,并且底层是数组的情况下,必须使用连续的内存地址,并且数组长度是有限的,并且大概率会发生内存 padding 的情况,因为 kv 要存储在一起,这就又造成了更大的浪费。

总结一下:不使用向下寻址使用拉链法的原因在于,1 可以利用碎片式的内存,2 不用内存 padding 造成浪费 3 原则上链表长度无限,可以无限增加。

问题六: map 如何操作真缩容?

可以使用 cap 重新生成一个 map,然后使用遍历的方式将老的 map 数据导入到新的小的 map 中,如果你知道数据不会再次增大的情况下是可以这么做的。

参考资料

  • https://github.com/golang/go/
  • https://blog.csdn.net/EDDYCJY/article/details/120465701
+ + + diff --git "a/\345\237\272\347\241\200/slice/index.html" "b/\345\237\272\347\241\200/slice/index.html" new file mode 100644 index 000000000..f34056a24 --- /dev/null +++ "b/\345\237\272\347\241\200/slice/index.html" @@ -0,0 +1,298 @@ + + + + + + 切片 | GOFamily - go 程序员宝典 + + + + + + + + +

切片

导读:

  • 切片的基本操作
  • 切片和数组的基本概念
  • 切片数组的底层数据结构
  • issues

基本操作

// 初始化一个切片,设置长度为5,容量为10
+a := make([]int,5,10)
+// 获取一个新的切片,遵循左闭右开的原则,取值时并且只看长度不能看容量
+// b切片,长度为1,容量为8(下文有讲,向右原则)
+b:= a[2:3]
+// 判断切片长度为0
+len(a) == 0
+// append 数据
+a = append(a, 1)
+// 将切片内容清空(element设置为初始值,比如int就是0,string就是"")
+clear(a)
+//
+// 将一个切片转为一个数组,注意,新数组不能超过切片的长度,且是数据的深度拷贝
+var arr =  [4]int(a) 
+

切片和数组的基本概念

数组是拥有一段连续内存的数据结构,切片是存储了这个数据结构地址,长度以及容量的 struct,这里俗称这种数据结构 (类似切片) 叫引用类型

生成数组有两种方式 [3]int[...]int 但是后者其实只是一种语法题,go 会自动推断出容量,因为推断是在编译期搞定的,所以并不会影响数组运行时的效率。

不同长度的相同数据类型数组不是一个类型,比如 [1]string{}[2]string{} 就是两个类型

t.Extra = &Array{Elem: elem, Bound: bound}
+

这段代码可以解释一下,它来自 go 的源代码,可以看出,生成数组的是一个 struct,那么显而易见了,里面的各项参数都必须一致的情况下 struct 才是一致的,所以,必须类型和容量都满足才行

但是切片没有这个烦恼,只要数据类型一致就是一种类型,因为它在编译期间的结构体中只有类型,并没有数量,数量需要在运行时才能确定

“切片的切片” 的容量是和 “切片的” 容量是不一致的 (比如这里的 a 和 b),我们来看一个例子:

package main
+
+import "fmt"
+
+
+func main() {
+	a := []int{1, 2, 3, 4, 5, 6}
+	b := a[2:5]
+	fmt.Println(len(a), cap(a), len(b), cap(b))
+}
+

output:6, 6, 3, 4

我猜你肯定以为 b 的容量也是 6,但是不是,go 规定,切片只能向右看,不能向左看,我们来说一下上面这个例子,a 就不用说了,a 的底层数据结构数组就是 6 个长度,所以 a 自然长度和容量都是 6,但是 b,它是切片的切片,遵从左闭右开的规则,它的长度是从 index 的 2 到 4,也就是说是 [3 4 5],自然它的长度就是 3,这毫无疑问,又因为切片遵从 “只能向右看容量” 的规则,它的容量从 index 2 开始往后算,也就是 6 - 2 = 4 所以它的长度是 3 容量是 4

切片,数组的底层数据结构

严格意义来说,go 的切片不存在扩容,如果切片想要的数据量大于底层数组的容量时,那么系统会做两件事,开辟新的数组,给这个数组生成新的切片,之前的数组和切片并没有任何的改变,而且如果没有被引用了,还会被 gc 掉

数组在数量小于等于 4 的时候,直接分配在栈内存里,如果大于四且没有逃逸到堆上时,变量就会在静态存储区初始化然后拷贝到栈上

静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。它主要存放静态数据、全局数据和常量。

栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。任何临时变量都是处于栈区的,包括在 main () 函数中定义的变量

堆区:亦称动态内存分配。程序在运行的时候用 malloc 或 new 申请任意大小的内存,程序员自己负责在适当的时候用 free 或 delete 释放内存。动态内存的生存期可以由我们决定,如果我们不释放内存,程序将在最后才释放掉动态内存。但是,良好的编程习惯是:如果某动态内存不再使用,需要将其释放掉,否则,我们认为发生了内存泄漏现象。

切片的底层数据结构。

type SliceHeader struct {
+	Data uintptr
+	Len  int
+	Cap  int
+}
+

实际上,切片就是一个 struct (struct 是 go 的基本组成单位,实际上很多东西都可以使用结构体来表达,比如接口底层也是结构体)

当我们使用字面量的方式去生成一个 slice 的时候,会在编译期间搞定,但是如果使用 make 去生成一个切片时,就会在运行时生成一个切片,在运行时生成切片,不仅仅会进行边界检查,而且还会看是否逃逸到堆上。

**slice 的扩容规律,**如果期望容量大于当前容量的两倍就会使用期望容量,如果当前切片的长度小于 256 (opens new window) 就会将容量 x2, +如果当前切片的长度大于 256 就会逐步 (按照这个公式 cap += (cap + 768) / 4) 从 2 倍的倍增速率调整到 1.25 的倍率,增加到新容量大于期望容量。不过这还不是全部

slice 扩容具体操作。扩容的时候并不是严格按照这个规律来的,这只是一个大致的规律,实际运行的时候会进行 padding (填充),也就是内存的对齐,将容量字节数尽量靠近 2 的次方,比如说期望的新容量是 5,这时期望分配的内存大小为 40 字节 (具体看一个位置多少个字节,这里是按照 8 举例子),运行时会向上取整内存的大小到 48 字节 (取整到 go 的推荐值 (opens new window)),所以新切片的容量是 48 / 8 = 6

我们知道当切片扩容的时候,重新分配底层数组是会牵涉到内存的复制的,因此尽量减少内存复制就是我们要追求的事情了,当我们往后面追加数据的时候,如果可以提前预估要使用的容量,那么就不牵涉到多次的内存复制了

func main() {
+	const sliceSize = 1000
+	m := make([]int, 0, sliceSize)
+	for i := 0; i < sliceSize; i++ {
+		m = append(m, i)
+	}
+}
+
+

slice 的浅拷贝 s := make([]int, 3,12)s1 := s[1:2],那么 s1 和 s 是浅层拷贝,他们拥有一个共同的底层数组,更改任意一个另一个的值也会发生改变。

**slice 的深拷贝。**copy 内置函数会将源内存 data 直接一次性拷贝,所以慎用,很消耗资源。

slice 的边界检查消除以及优化。go 在编译数组或者切片的时候都会在编译期间进行边界越界的检查,不过也只是普通的检查,比如直接使用整数或者常量访问数组,对于用变量去获取切片数组的情况根本就检查不了,这个时候 go 的运行时就会起作用,发出 Panic,那么边界检查有什么缺点呢?就是会降低运行时的效率,当然这里指的是运行时的检查,因为编译器的检查首先本身能力有限,其次只会降低编译速度而已,所以 go 从 1.17 开始,就开始支持了边界检查的消除,为了就是在证明不会越界的情况下,提高代码的运行效率,下面我们看一下取消运行时边界检查的例子:

package main
+
+func main() {
+	// 这种不能确定,所以还是会检查
+	a := []int{1,2,3}
+	for i := range a {
+		a[i]
+}
+
+// 这样,就可以消除边界检查,因为运行时确定不可能出现问题
+for i := len(a) - 1; i >= 0; i-- {
+		_ = a[i]
+	}
+}
+
+// 或者这样也可以
+
+func A(i []int, b []byte) {
+
+	if len(i) > 256 {
+		i = i[:256] // 这里就是给运行时一个暗示,表示i的最大index不会超过 256
+		for _, v := range b { // v的最大值也不会超过 256(byte最大值 255)
+			_ = i[v]
+		}
+	}
+}
+
+

append 追加数据的操作

前文我们知道 slice 会发生扩容情况,那么这种扩容一般是在 append 操作的时候发生的,通常来说操作是这样的:

a := make([]int,3,10)
+a = append(a,1) // 注意,append操作是追加的意思,所以这里不是 100,而是0001
+// 0001
+

当 slice 底层数组不够 append 的时候,就会发生扩容:

a := make([]int,3,4)
+a[0] = 1
+a[1]= 2
+a[2] = 3
+// 这里还是相同的数组,因为容量没有超过
+a = append(a,4) 
+
+// 这里的a 指向的就不同于之前的那个数组了。
+//底层就变成了一个新的数组  1 2 3 4 5 0 0 0 ;fmt.Println(cap(a)) : 8
+a = append(a,5)
+

同样底层数组的不同区域的切片 append 的时候经常会发生意想不到的结果:

func main() {
+	a := make([]int,3,5)
+	a1 := a[1:2]
+	fmt.Println(a,a1)
+	a1 = append(a1,2)
+	fmt.Println(a,a1)
+	a = append(a,4)
+	fmt.Println(a,a1)
+	a1 = append(a1,5)
+	fmt.Println(a,a1)
+
+}
+//[0 0 0] [0]
+// [0 0 2] [0 2]
+// [0 0 2 4] [0 2]
+// [0 0 2 5] [0 2 5]
+

从上面的案例可以说明,首先,a1 a 指向同一个底层数组,其次,a1 和 a 在 append 的时候,都是在各自切片的后面添加数据,他们会互相影响,在写代码的时候容易出现 bug

对 append 的优化

对一个未知大小的切片进行 append 操作的最佳选择是初始化一个 nil 切片:

var s []string
+s = append(s,"a")
+

s 在初始化的时候没有分配内存,在 append 的时候分配了一个底层数组,下面这种方式就会浪费一次内存分配

s := make([]string,0)
+s = append(s,"a")
+

这种方式,在 s 初始化的时候会给予它一个底层长度为 0 的数组,即便长度为 0,go 并未分配实际的内存空间,但是仍然浪费了执行片段,append 的时候还要再次分配底层数组。

不过使用 make 的方式比较适合已知容量的场景。

除此之外还有两种初始化的方式:

  • []int(nil)
  • []string("a")

我们知道 []int{}var s []int 是两种皆然不同的初始化方式,虽然都是 0 长度,但是前者不是 nil,后者是 nil,不过 append 的时候不会介意是否是 nil,这也提醒了我们判断是否是空切片的方式,使用 len == 0 才是正确的方法,“是否等于 nil” 是错误用法。

[]string(nil) 的用法非常少见,通常来说,使用场景就下面这么一个:

src := []string("hi","there","!")
+s := append([]string(nil), src...)
+

我们这里使用一个值为 nil 的切片主要是为了符合类型的需求。

切片的 copy

copy 是 go 语言的内置函数,全局使用,copy(a,b []Type),copy 是深度拷贝,它将后者的数据完全拷贝给前者。

要注意的是,将要被复制的元素能复制的量取决于前者的 length

比如下面这种情况,被复制的元素就是 0,但是并不会 panic

src := []int{1,2,3}
+var d []int
+// []
+copy(d,src)
+

一般来说,我们会使用相同的 length:

src := []int{1,2,3}
+d := make([]int,len(src))
+copy(d,src)
+

或者直接使用 append 也能做到 copy

src := []int{1,2,3}
+d := append([]int(nil),src...)
+

切片未被合理 gc

当切片完成自己的使命时,我们希望它可以正常的被 gc 掉,通常来说,我们可以手动使用 runtime.GC() 来强制系统进行垃圾回收,下面我们看一种 bug,这种 bug 出现以后,我们手动的垃圾回收将会无效

s := make([]int,10)
+// 此处原本的想法是只取两个数据
+// 但是造成了10个数据都不能垃圾回收,8个浪费
+b := s[8:]
+runtime.GC()
+runtime.KeepLive(b)
+

本来我们期望 s 底层数组可以被垃圾回收,但是 b 也指向这个相同的底层数组,那么这个垃圾回收就无法执行。

正确的方法是

copy(res,s[8:])
+return res
+

当然了,我们深究 gc 的本质就会发现,三色 gc 算法是看某个对象是否还被变量持有,是否可以通过变量的方式追踪到,来作为回收标准的,要么我们像前者一样,不再让变量持有值,要么将值变成 nil 即可:

type A struct {
+	v []byte
+}
+s := make([]A,10)
+// 此处原本的想法是只取两个数据
+// 但是造成了10个数据都不能垃圾回收,8个浪费
+for i:= 0;i<8;i++ {
+	// 这里是手动的将前八个数据丢弃掉
+	s[i].v = nil
+}
+b := s[8:]
+runtime.GC()
+runtime.KeepLive(b) 
+

这种多切片指向底层数组而造成的无法正常垃圾回收的行为很常见。在工作中还是应该检查好自己的代码,避免这种行为的发生。

切片中的 range 注意事项

range 时,我们直接修改返回的值是不会生效的,因为返回的值并不是原始的数据,而是数据的复制。

type Student struct {
+	year int
+}
+
+func main() {
+	result := []Student{
+		{12},
+		{13},
+	}
+	for _, student := range result {
+		student.year++
+	}
+	// 12 13
+	fmt.Println(result)
+}
+

也就是说,这里的 student := student 的改变不会影响 result 中的任何数据,除非这里的 []Student[]*Student

下面我们演示一下,正确的在 range 时的操作方式:

type Student struct {
+	year int
+}
+
+func main() {
+	result := []Student{
+		{12},
+		{13},
+	}
+	for i := range result {
+		result[i].year++
+	}
+	// 13 14
+	fmt.Println(result)
+}
+

正确的方式就是直接使用 result 本身进行操作,就可以真正的去改变 result 了。

并且,在 range 的时候,range 后面的数据其实也是复制品,也就是说,这里的 := range result result 也是复制品,原有的 result 如何变化都不会影响 range 结果。

// 这里的 result
+for i := range result {
+	// 跟这里面的result不是一个值,
+	//只有里面的result才是跟外面的result是一个值
+		result[i].year++
+	}
+

下面我们再看一个案例:

s := make([]int,3)
+for range s {
+	s = append(s,10)
+}
+// [0 0 0 10 10 10]
+

你猜结果是多少呢?是会一直 range 吗?因为数据在一直添加啊,nonono,只会 range 3 次而已,因为 range 后面的 s 是固定不变的,它本身只是原有 s 的复制品而已。

综上:

  • range 后面的数据是原有数据的复制品
  • range 前面的 k v 更是后面复制品输出数据的复制品
  • range 里面的数据才是跟外面的数据保持一致

第三点很关键,range 后面的数据跟 range 里面的数据并不是一个:

s := make([]int,3)
+// 这里的s
+for range s {
+	// 这里的s 跟外面保持一致
+	s = append(s,10)
+}
+

不能把 range 当做 function 来类比:


+func main(){
+	s := make([]int,1)
+	range(s)
+
+}
+func range(s any)(k,v any){
+	s[0]++
+}
+

如果是函数,函数体的变量 s 和函数内部的 s 就是同一个,显然,range 中,range 后面的 s 和 range 里面的 s 并不是同一个。

切片转化为数组

在 go 1.20 版本中,新添加了切片转为数组或者数组指针的操作,具体实现如下:

s:= make([]int,2,4)
+a := [1]int(s) 
+a1 := [2]int(s)
+a2 := (*[1]int)(s)
+a3 := (*[2]int)(s)
+

a[0] == s[0],也就是说,转化出来的就是底层的那个数组的复制,注意并不是底层数组本身,不过这里相同切片转化的数组指针是指向这个切片的底层数组的,所以 a2 和 a3,s 是公用一个数组的,如果更改了 a2[0],那么 a3,s 也是会发生改变的,a1 a 已经是数据的复制了,他们有了分别的生活了。

下面我们看一个具体的代码演示:

func main() {
+	s := make([]int, 2, 4)
+	a := [1]int(s)
+	a1 := [2]int(s)
+	a2 := (*[1]int)(s)
+	a3 := (*[2]int)(s)
+	fmt.Println(s, a, a1, a2, a3)
+	a[0] = 1
+	fmt.Println(s, a, a1, a2, a3)
+	a2[0] = 12
+	fmt.Println(s, a, a1, a2, a3)
+}
+//[0 0] [0] [0 0] &[0] &[0 0]
+//[0 0] [1] [0 0] &[0] &[0 0]
+//[12 0] [1] [0 0] &[12] &[12 0]
+
+// 可以证明确实是指向同样的底层数据
+fmt.Println(&a2[0], &a3[0], &s[0])
+// 0x1400012a020 0x1400012a020 0x1400012a020
+

当一个切片转化为一个数组的时候,数组的长度不能大于切片的长度,而不是容量

那么下面这种代码就会出现 bug

s:= make([]int,2,4)
+a := [3]int(s)
+a1 := (*[3]int)(s)
+// panic: cannot convert slice with length 2 to array or pointer to array with length 3
+

下面我们看一下转化后的特殊值

s:= make([]int,2,4)
+a := [0]int(s) //  []
+a1 := (*[0]int)(s)// &[]
+
+//将非空切片转为长度为 0 的数组,得到的指针不是 nil,比如 b2
+var j []int
+b := [1]int(j) // panic
+b1 := [0]int(j)//  [] 
+b2 := (*[0]int)(j)//  <nil>
+b3 := (*[1]int)(j)// panic
+
+//将 nil 切片转为长度为 0 的数组,得到的指针为 nil
+c := make([]int,0)
+u := (*[0]int)(c) //&[] 
+u1 :=[0]int(c) //  []
+

issues

问题一: 如果有多个切片指向了同一个底层数组,那么你认为应该注意些什么

一定要避免 a 切片的更改造成的底层数据的改变,对 b 切片的结果造成影响,因为它们指向同一个数据底层

a := []int{1,2,3}
+b := a[:]
+
+a[2] = 4
+
+fmt.Println(a,b)
+

[1 2 4] [1 2 4]

问题二: 怎样沿用扩容的思想对切片进行 “缩容

a:= []int{1,2,3}
+b := a[:2] // b = [1,2]
+
+// 如果确定a的数据多余的没有任何的用途了
+nb := make([]int,2)
+copy(nb,b)
+

所谓扩容的思想,就是创造一个新的底层数据

问题三: nil 切片和空切片 (比如 []int {}) 的区别

最大的区别就是指向的底层数组的地址不一样

  • nil 压根就没有地址
  • 空切片是有正儿八经的地址的,只不过这个地址指向的数组不占用空间,这个数组叫做 zero 数组,并且所有的空切片指向同一个数组就是这个 zero 数组,也可以说在 go 里,zero 数组是唯一的存在,它存在的目的就是为了空切片
a := [0]int{}
+	fmt.Println(a)
+	fmt.Println(unsafe.Sizeof(a)) // 0
+

空的数据是不占内存空间的,还有类似的,比如空的 struct 也是一样的

a := struct{}{}
+	fmt.Println(a)
+	fmt.Println(unsafe.Sizeof(a)) // 0
+

问题四: slice 和 array 的不同使用场景是什么

如果数据是固定的,可以用数组,否则还是切片更加灵活,实际上绝大多数情况下还是切片更好用

参考资料

  • https://www.jianshu.com/p/9ea2fba64f06
  • https://chai2010.cn/advanced-go-programming-book
  • https://blog.csdn.net/kevin_tech/article/details/122138489
  • https://blog.csdn.net/weixin_39927993/article/details/112099007
+ + + diff --git "a/\345\237\272\347\241\200/string/index.html" "b/\345\237\272\347\241\200/string/index.html" new file mode 100644 index 000000000..d8a68e7fa --- /dev/null +++ "b/\345\237\272\347\241\200/string/index.html" @@ -0,0 +1,256 @@ + + + + + + 字符串 | GOFamily - go 程序员宝典 + + + + + + + + +

字符串

  • 字符串的基础操作
  • 字符串的基础知识
  • 字符串的底层操作

字符串的基础操作

// 声明
+var s = "你好"// s := "你好"
+//读取
+fmt.Print(s)
+// 转化为unicode码点存储,单个字符, 例如'你',也是使用的rune来存储的,、
+// 不过string底层本身是[]byte的方式存储的
+_ = []rune(s)
+// 转化为字符存储
+_ = []byte(s)
+// 字符的简单拼接
+s1 := "你"
+s2 := "好"
+s := s1 + s2
+//多行字符
+s = `
+你好
+
+世界
+`
+// 多行字符也通常用在屏蔽 ""作用的地方
+s= `{"user": "shgopher", "links": ["https://github.com/shgopher"]}`
+

字符串的基础知识

  • 字符串的数据是只读数据不可更改
  • 字符串的零值是可用的,var s string 结果是 ""
  • 获取字符串长度的操作时间复杂度是 O(1) 因为它是不可变的只读数据,所以长度被保存在了字段中,直接读这个字段即可
  • 字符串可以通过 ++= 进行拼接
  • 字符串可以使用 > < >= <= == =! 运算符,比较的顺序是: +
    • 先比较长度
    • 再比较是否是指向一块内存地址
    • 如果都满足再比较具体数据
  • 字符串原生支持 unicode 字符集,并且 go 默认支持 utf-8 的编码算法 +
    • rune 存储 unicode 的一个码点
    • byte 存储真实的底层字符 (比如 utf-8,三个字符来保存一个中文字符,rune 就只显示一个字符,byte 会显示三个)
    package main
    +
    +import "fmt"
    +
    +func main() {
    +  var s = "中"
    +  fmt.Print([]byte(s), []rune(s))
    +}
    +// output: [228 184 173] [20013]
    +
  • 使用``原生支持多行字符

字符串的高效构造

字符串的构造有以下这么几种

  • 最常规的使用 ++=
  • fmt.Sprintf
  • strings.Join
  • strings.Builder
  • bytes.Buffer

让我们分别给出代码:

//最常规的使用`+`和 `+=`
+package main
+
+import "fmt"
+
+func main() {
+	s1 := "中"
+	s2 := "国"
+	fmt.Print(s1 + s2)
+}
+
//fmt.Sprintf
+package main
+
+import (
+	"fmt"
+)
+
+func main() {
+	s1 := "中"
+	s2 := "国"
+	fmt.Print(fmt.Sprintf("%s%s", s1, s2))
+}
+
//strings.Join
+package main
+
+import (
+	"fmt"
+	"strings"
+)
+
+func main() {
+	s1 := "中"
+	s2 := "国"
+	fmt.Print(strings.Join([]string{s1, s2}, ""))
+}
+
//strings.Builder
+package main
+
+import (
+	"fmt"
+	"strings"
+)
+
+func main() {
+	var b strings.Builder
+  b.Grow(2) // 给出猜测最终的string长度
+	s1 := "中"
+	s2 := "国"
+	b.WriteString(s1)
+	b.WriteString(s2)
+	fmt.Print(b.String())
+}
+
//bytes.Buffer
+package main
+
+import (
+	"bytes"
+	"fmt"
+)
+
+func main() {
+	var b bytes.Buffer
+	s1 := "中"
+	s2 := "国"
+	b.WriteString(s1)
+	b.WriteString(s2)
+	fmt.Print(b.String())
+}
+

根据 benchmark,得出以下结论:

  • 带有预估 string 长度的 strings.Builder 最快
  • 带有预估的 bytes.Buffer 和 strings.Join 性能第二档次
  • 没有预估长度的 strings.Builder 和 bytes.Buffer 以及 + += 第三档次
  • fmt.Sprintf 最差劲

那么:

  • 当能给出预估的情况下,优选使用 strings.Builder
  • strings.Joins 性能最稳,没有预估的情况下,使用这个稳定啊 (实际上这个 join 就是调用了 string.Builder,并且给出了预估长度)
func Join(elems []string, sep string) string {
+switch len(elems) {
+case 0:
+	return ""
+case 1:
+	return elems[0]
+}
+// 这里是搞定string长度的
+n := len(sep) * (len(elems) - 1)
+for i := 0; i < len(elems); i++ {
+	n += len(elems[i])
+}
+// 使用了builder
+var b Builder
+b.Grow(n)
+b.WriteString(elems[0])
+for _, s := range elems[1:] {
+	b.WriteString(sep)
+	b.WriteString(s)
+}
+return b.String()
+}
+
+
  • 操作符 + += 最直观,并且在字符短,以及编译器知道连接的字符串个数时,这种方式还能得到编译器的优化
  • fmt.Sprintf 用在多类型组成字符串的时候是最好的,虽然它效率很差,但是人家能力强啊

综上所诉:优先选 strings.Join

字符串的底层

数据结构

一个 string 的底层数据类似一个 slice,只不过这个 slice 是只读数据,它的底层不同于一般的 slice,是一个特别的 struct

type stringStruct struct{
+	str unsafe.Pointer
+	len int 
+	// 注意常规的slice这里是有一个cap的
+	//但是string因为是只读的关系只有length的含义
+}
+

runtime/string.go 中出现了这么一段代码

// rawstring allocates storage for a new string. The returned
+// string and byte slice both refer to the same storage.
+// The storage is not zeroed. Callers should use
+// b to set the string contents and then drop b.
+func rawstring(size int) (s string, b []byte) {
+	p := mallocgc(uintptr(size), nil, false)
+
+	stringStructOf(&s).str = p
+	stringStructOf(&s).len = size
+
+	*(*slice)(unsafe.Pointer(&b)) = slice{p, size, size}
+
+	return
+}
+

我们仔细看注释的这句话,当一个 string 导入数据的时候,运行时会给定一个辅助的 slice,用来辅助的导入数据,然后当数据导入完毕之后,这个 slice 的描述符,也就是这个代表了这个 slice 的 struct 就会被删除掉,所以说其实 string 不能跟 slice 划上等号,也不能简简单单的说它是一个只读的 slice,实际上它压根就不是 slice,slice 在生成 string 的过程中只是起到了辅助作用

类型转换

字符串进行的转化只能是 string 和 []rune or []byte 互相转换

package main
+
+import "fmt"
+
+func main() {
+	a := "【你好】"
+	b := []byte(a)
+	c := []rune(a)
+	// []byte 是可以直接使用fmt包直接输出为string的,但是[]rune需要显式进行转换。
+	fmt.Printf("现在我们打印出原始数据%s,打印出[]byte转化后的数据%v,打印出[]rune转化后的数据%v,打印出逆转的数据%s 和 %s,", a, b, c, b, string(c))
+}
+

上文我们提到的字符串的构造,例如删除一个字符,追加一个字符,都无一例外需要改变这个 string,那么很明显任何数据的处理都是拷贝的数据,原数据是不会有任何变化的,所以这就告诫我们字符串的处理要小心非常有可能浪费大量的内存。

我们看一下底层的转换代码:

const tmpStringBufSize = 32
+
+type tmpBuf [tmpStringBufSize]byte
+
+func stringtoslicebyte(buf *tmpBuf, s string) []byte {
+	var b []byte
+	if buf != nil && len(s) <= len(buf) {
+		*buf = tmpBuf{}
+		b = buf[:len(s)]
+	} else {
+		b = rawbyteslice(len(s))
+	}
+	// 这里就发生了拷贝
+	copy(b, s)
+	return b
+}
+
+

于此同时我们也能发现 string 的底层的真实存储是 []byte 不是 []rune

package main
+
+import "fmt"
+
+func main() {
+	a := "【你好】"
+	var b []byte
+	copy(b, a)
+	var c []rune
+	// invalid argument: arguments to copy c (variable of type []rune) 
+	// and a (variable of type string) have different element types rune and byte
+	copy(c, a)
+	fmt.Println(b, c)
+}
+
+

go 为某几种特别的情况优化了 string 和 slice 转换必须拷贝的情况,意思就是不需要拷贝就让这个 string 直接使用这个 slice 的底层,但是有个规定,只要是 slice 发生了改变,那么这个 string 立即失效

b = []int{1,2}

  • string(b) 用在 map 的 key 中 ma[string(b)]++
  • string(b) 在字符串的拼接句子中 “a” + string(b)
  • for-range 中的 string 到[]byte 的转换

for-range 字符串

因为对一个字符串使用 range 的时候,go 默认使用 utf8 的编码方式,但是 string 的底层是[]byte 的存储方式,所以直接 range 的时候,将这个时候的字符转化为字符就会发生乱码的情况

s := "你好"
+for k := range s {
+	// äå 
+	fmt.Printf("%c",s[k])
+}
+

解决方法有两种:

  1. 直接获取 value 值
func main() {
+	s := "你好"
+	// 这里的 v 就直接是 rune
+	for _, v := range s {
+		fmt.Printf("%c", v)
+	}
+}
+
  1. 将 s 转化为 []rune 来获取真正的 unicode 编码:
func main() {
+	s := "你好"
+	sr := []rune(s)
+	// 这里的 v 就直接是 rune
+	for i := range sr {
+		fmt.Printf("%c", sr[i])
+	}
+}
+
+ + + diff --git "a/\345\237\272\347\241\200/\344\275\234\347\224\250\345\237\237/index.html" "b/\345\237\272\347\241\200/\344\275\234\347\224\250\345\237\237/index.html" new file mode 100644 index 000000000..ca37d2bb6 --- /dev/null +++ "b/\345\237\272\347\241\200/\344\275\234\347\224\250\345\237\237/index.html" @@ -0,0 +1,145 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

作用域

先给出两个经典的案例:

if 级变量

func main() {
+  if a :=0;false{
+  
+  } else if b :=0;false{
+
+  }else if c :=0;false{
+
+  }else{
+    println(a,b,c)
+  }
+}
+

a b c 这三个变量的作用域属于 if 这整个结构,只要前面初始化过,那么后面就可以使用,所以你会发现 print 可以输出三个变量。

loop 级变量

func main() {
+  for i:=0;i<10;i++ {
+    go func(){
+      println(i)
+    }()
+  }
+  time.Sleep(time.Second)
+}
+

因为 i 属于 loop 级别,通常来说 for 执行过程是快于新开辟一个 goroutine 的,所以导致这是个 goroutine 输出的都是最后一个 i,即都输出 10

根据 go 1.20 的表述,以后这个 loop 级别的变量非常有可能会被修改成局部变量。如果没有被修改,我们可以开辟一个局部变量 (go 1.22 已经将这个 loop 级变量修改为局部变量!!!)

func main() {
+  for i:=0;i<10;i++ {
+    i := i
+    go func(){
+      println(i)
+    }()
+  }
+  time.Sleep(time.Second)
+}
+

代码块的类型

代码块是代码执行的基本单位,代码执行流总是从一个代码块流转到另一个代码块。

  • 显式代码块 +
    • 函数体
    • for 循环体
    • if 语句
  • 隐式代码块 +
    • 宇宙代码块:所有的 go 源码都属于这个代码块
    • 包代码块:每一个包都有一个代码块,这个代码块包括了整个包的源码
    • 文件代码块:每一个文件都有一个代码块,这个代码块包括了整个文件的源码

预定义的标识符,比如 make new cap len 作用域是宇宙代码块

包级变量,包级常量的作用域是包代码块

go 源代码中导入的包名称作用域是文件代码块

方法接收器,函数参数,返回值,对应的作用域是函数作用域

函数体内部声明的变量和常量作用域仅限于函数体内部的局部作用域


+func main(){
+  {
+    const A = 12 // A的作用域就仅限于这个大括号里面
+
+  }
+
+}
+

if

if 包含了一个隐式的代码块:

c := 12
+if a:=1;a<2 {
+  println(a)
+}else if b:=3;b < c {
+  println(b)
+}else {
+  println(a,b)
+}
+

其实等于:

  c:= 12
+{
+  a:=1
+  if a<2{
+    println(a)
+  }
+  b := 3
+  else if b < c {
+    println(b)
+  }else {
+    println(a,b)
+  }
+}
+

所以,在 if 中的大括号里,是可以输出 a 的值的。

for

for 循环的作用域可以使用下面的两种方式展现。for 循环中的变量都属于 loop 级,不属于每次的那个小循环的局部变量,所以在整个的 loop 过程中,变量是唯一的,就是那一个。只不过不同的时间点,这个变量的值有可能是不同的。

for i:=0;i<10;i++ {
+
+}
+
+// 等价于
+
+{
+  i:=0
+  for ;i<10;i++ {
+
+  }
+}
+

这里需要注意一下,一旦 go 在后续的版本中修改了 loop 级变量这个设定,这个等价就不成立了。上文有讲。

for k,v := range slice {
+
+}
+// 等价于
+{
+  k,v := 0,0
+  for k,v = range slice {
+
+  }
+}
+

switch && select

这两种结构的等价是相同的,都是 case 级。

switch expression:
+case list1:
+case list2:
+default:
+
+等价于
+
+switch expression:
+case list1:
+{}
+case list2:
+{}
+default:
+{}
+

select 稍微有点不同,因为 select 中的 case 是可以新建一个变量的,除了我们说的每一个 case 是一个作用域之外,case 上新建的这个变量也只是属于下面这个小的作用域。

+ + + diff --git "a/\345\237\272\347\241\200/\345\205\266\344\273\226\345\206\205\345\256\271/index.html" "b/\345\237\272\347\241\200/\345\205\266\344\273\226\345\206\205\345\256\271/index.html" new file mode 100644 index 000000000..4891e42fc --- /dev/null +++ "b/\345\237\272\347\241\200/\345\205\266\344\273\226\345\206\205\345\256\271/index.html" @@ -0,0 +1,155 @@ + + + + + + 其他内容 | GOFamily - go 程序员宝典 + + + + + + + + +

其他内容

指针

go 语言的指针类型是 unsafe.Pointer,uintptr 是指针的计算类型,也就是说,地址必须通过 unsafe.Pointer 提取以后,再转为 uintptr 才能进行计算,uintptr 的实质是一个整数类型,并且完全可以容纳所有的指针的数据。

go 语言使用 * 符号用来表示指针类型,以及取指针类型实际数据这个操作,使用 & 取变量的指针 (地址),我们看一下操作

func main(){
+	// 使用 内置函数 new() 去取结构体的地址
+	var p *People  = new(People)
+	*p = People{
+		"1"
+	}
+	// 使用 取值符号去取得 变量 p 的地址
+	fmt.Println(&p)
+}
+type People struct{
+	name string
+}
+
+

我们看一下如何直接计算指针类型


+func main() {
+	// a ,string 类型
+	var a string
+	
+	// b ,a变量的地址
+	var b *string = &a
+	fmt.Println("打印初始a变量的地址", b)
+	
+	// c,转为可计算的指针类型之后的变量
+	c := uintptr(unsafe.Pointer(b)) // uintptr(unsafe.Pointer())
+	fmt.Println("打印可计算指针类型c", c)
+	c++
+	fmt.Println("打印可计算指针类型c++", c)
+	
+	// 将c再转化为 a 的地址
+	b = (*string)(unsafe.Pointer(c)) // (*string)(unsafe.Pointer())
+	fmt.Println("打印转换后的a的指针", b)
+}
+
打印初始a的指针地址 0x14000096230
+打印可计算指针类型c 1374390149680
+打印可计算指针类型c++ 1374390149681
+打印转换后的a的指针 0x14000096231
+

go 的引用类型和非引用类型

引用类型 非引用类型
slice, interface, chan, map 其余

引用类型的实质其实就是 fat pointer 即:胖指针,整个类型使用 struct 作为底层数据,data 是一个指针地址,它指向的是要使用的数据。

全局变量和局部变量

全局变量,引用类型分配在堆上,值类型分配在栈上

局部变量,一般分配在栈上,当局部对象过大的时候分配在堆上,如果对局部变量做逃逸分析,发现它逃逸到了堆上,那么就将其分配到堆上

go init 函数的执行顺序

init 函数在一个包内的执行顺序:对同一个 go 文件的 init() 调用顺序是从上到下的,对同一个 package 中的不同文件,将文件名按字符串进行 “从小到大” 排序 (数字排在前面),之后顺序调用各文件中的 init() 函数

对于不同的包,如果不相互依赖的话,按照 main 包中 import 的顺序调用其包中的 init() 函数,如果包存在依赖,例如:导入顺序 main –> A –> B –> C,则执行顺序为 C –> B –> A –> main

go 会先执行全局变量再执行 init,当然多包全局变量的初始化跟 init 的执行顺序是一致的

go 可比较类型

https://go.dev/ref/spec#Comparison_operators

不可以的

  • 切片,map func (这几种都是因为自己本身变来变去,同样的变量,不同时间,内部值就变了,所以他们不可以参与 == 的比较) 变量无法参与一般的比较,但是他们可以和 nil 作对比

可以的:

  • 数字类型,bool,string 这种常见类型可比较 +
    • a := make(chan []int) 即使是这样的内部含有不可比较的通道变量本身也是可以比较的。
  • chan 当两个 chan 类型的变量进行比较时,只有它们都为 nil 或者指向同一个通道时才返回 true,否则返回 false。
  • 内部字段都必须是可比较类型的数组和结构体可以比较
  • 指针可以比较,指针的实质是一个整数类型
  • 接口类型是可比较类型

所以,接口变量是可以作为 map 的 key 值的,因为接口可以比较

package main
+
+import "fmt"
+
+func main() {
+	b := map[interface{}]int{}
+	var s Some
+	b[s] = 1
+	fmt.Println(b[nil])
+
+}
+
+type Some interface {
+	methods()
+}
+

go 可寻址类型

以下内容是不可寻址的量

字面量的解释 var a int = 12,12 就是字面量,也就是所谓的那个值本身;结果值的解释:就是这个结果这个 value 的值

  • 常量 const a = 12 &a 不可寻址

  • 基本类型值的字面量 a := 12 &12 不可寻址

  • 算术操作的结果值 a := 12 b := 12 &(b+a)

  • 对各种字面量的索引表达式和切片表达式的结果值。不过有一个例外,对切片字面量的索引结果值却是可寻址的

    a := map[int]int{1: 1}
    +	for k := range a {
    +		fmt.Println(&a[k]) // 无法寻址,这个数据属于临时的,可变的数据
    +	}
    +
    +	b := []int{1,2,3}
    +
    +	for k := range b {
    +
    +		// 这个可以寻址,每个切片值都会持有一个底层数组,
    +		// 而这个底层数组中的每个元素值都是有一个确切的内存地址
    +		fmt.Println(&b[k]) 
    +	}
    +
  • 切片字面量的切片结果值为什么却是不可寻址?a := []int{1,2}, &(a[:1]) 原因是切片表达式字面量的切片属于临时值跟字面量的结果值不一样,后者属于结果值有效值

  • 对字符串变量的索引表达式和切片表达式的结果值。a := "a" &("a") &a[0]

  • 对字典变量的索引表达式的结果值。a := map[int]int{1:1} &(a[1])

  • 函数字面量和方法字面量,以及对它们的调用表达式的结果值。&(func ())

  • 结构体字面量,见下面例子

  • 类型转换表达式的结果值

  • 类型断言表达式的结果值

  • 接收表达式的结果值

我们看一道面试题

这道题就是因为中间变量无法获取地址造成的 bug

package main
+
+func main() {
+	// 此处go会自动调用值的指针来运行 SetName 但是因为 return Dog{name} 是一个临时的值,所以无法获取到指针
+	New("nihao").SetName("monster")
+
+}
+
+func New(name string) Dog {
+	return Dog{name}
+}
+
+type Dog struct {
+	name string
+}
+
+func (d *Dog) SetName(n string) {
+	d.name = n
+}
+

我们可以这么改

package main
+
+func main() {
+	
+a := 	New("nihao")
+a. SetName("monster")
+
+}
+
+func New(name string) Dog {
+	return Dog{name}
+}
+
+type Dog struct {
+	name string
+}
+
+func (d *Dog) SetName(n string) {
+	d.name = n
+}
+
+

另外自增 ++ 自减 -- 左边的表达式都必须是可寻址的类型,否则也是无法操作的。

字典字面量和字典变量索引表达式的结果值是个例外例如 ma [“12”] ++

在赋值语句中,赋值操作符左边的表达式的结果值必须可寻址的,但是对字典的索引结果值也是可以的

总结一下:

  • 常量 + string 这种无法更改的数据无法寻址,函数通常来说也可以算作 “常量”,应该它就是一段代码,不可更改
  • 结果值/字面量 因为其无法更改所以寻址将没有意义
  • 中间值 或者 临时对象 比如说 &(a + b) 这类临时的变量的内存地址没有意义
  • 不安全的操作 比如 map 中的 k-v 经常要从一个哈希桶迁移到另一个桶,所以你获取地址,它经常会改变,外界还不得而知,所以获取到这个 key-value 的地址是不安全的
+ + + diff --git "a/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/0.html" "b/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/0.html" new file mode 100644 index 000000000..2e8904a84 --- /dev/null +++ "b/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/0.html" @@ -0,0 +1,112 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

反转控制

概念:控制逻辑与业务逻辑分享,让业务逻辑依赖控制逻辑。

大概的思路就是***将控制代码剥离,然后在逻辑代码中去调用控制代码即可***,具体实现的时候,就是在逻辑代码中内嵌控制代码的类型,这样即可。

首先需要明确的是 “控制逻辑是什么” 这个问题,我们举一个例子:有一个数据库,数据库的增删改查就是控制逻辑,使用这些基础的动作去将用户的名字存入数据库,删除用户的名字,这叫业务逻辑,我们所说的反转控制,就是将基础的控制剥离出来,下面我们看一个例子:

// 这是控制代码,我们把控制代码先揪出来。
+type Undo []func()
+
+func (undo *Undo) Add(function func()) {
+  *undo = append(*undo, function)
+}
+// 这段代码的作用:恢复
+func (undo *Undo) Undo() error {
+  functions := *undo
+  if len(functions) == 0 {
+    return errors.New("No functions to undo")
+  }
+  index := len(functions) - 1
+  if function := functions[index]; function != nil {
+    function()
+    // For garbage collection
+    functions[index] = nil 
+  }
+  // 这里是为了规避在调用delelte的时候,又调用了add的情况
+  // 因为 delete的时候会调用add,add的时候会再次调用delete
+  // 为了防止这种情况,我们认为的将这个切片减去一个即可。
+  *undo = functions[:index]
+  return nil
+}
+

我们将控制逻辑嵌入到业务逻辑中

type IntSet struct {
+    data map[int]bool
+    undo Undo
+}
+ 
+func NewIntSet() IntSet {
+    return IntSet{data: make(map[int]bool)}
+}
+
+// 直接继承
+func (set *IntSet) Undo() error {
+    return set.undo.Undo()
+}
+ 
+func (set *IntSet) Contains(x int) bool {
+    return set.data[x]
+}
+func (set *IntSet) Add(x int) {
+    if !set.Contains(x) {
+        // 增加一个数据
+        set.data[x] = true
+        // 如果是恢复的话,是需要删除一个数据的
+        // 所以这里的操作就是删除
+        set.undo.Add(func() { set.Delete(x) })
+    } else {
+        set.undo.Add(nil)
+    }
+}
+ 
+func (set *IntSet) Delete(x int) {
+    if set.Contains(x) {
+        delete(set.data, x)
+        set.undo.Add(func() { set.Add(x) })
+    } else {
+        set.undo.Add(nil)
+    }
+}
+
+
+ + + diff --git "a/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/1.html" "b/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/1.html" new file mode 100644 index 000000000..41a693db8 --- /dev/null +++ "b/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/1.html" @@ -0,0 +1,92 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

map-filter-reduce

map filter reduce 这三个操作经常用在数据的处理上,比如数据的统计业务。

下面看一下这三者基本的做法是什么

我们用做饭来进行类比:

  • map 30 个进,30 个出,类似于洗菜的过程
  • filter 30 个进,15 个出,类似于摘菜
  • reduce 15 个进,1 个出,类似于炒菜的过程

下面这个案例,我们要对于一组 string 进行数据处理。map 的过程,我们对这堆 string 进行全部的修饰,filer 过程我们挑选出合适的 string,reduce 过程我们最终输出要的成品

map 的举例

func MapStr(s []string,f func(string)string)[]string{
+  var newS []string
+  for _,v := range s{
+    newS = append(newS,f(v))
+  }
+  return newS
+}
+

filter 的举例

func FilterStr(s []string,f func(string)bool)[]string{
+  var newS []string
+  for _,v := range s{
+    if f(v){
+    newS = append(newS,v)
+    }
+  }
+  return newS
+}
+

reduce 的举例

func ReduceStr(s []string,f func(string)string)string{
+  var newS string
+  for _,v := range s{
+    newS += f(v)
+  }
+  return newS
+}
+

+func main() {
+	s := []string{"李明月", "李月", "百伯", "张兰", "武滴滴", "叶赫那拉", "迪丽热巴"}
+	ns := MapStr(s, func(v string) string {
+		return v + "同学"
+	})
+	fmt.Println(ns)
+	ns = FilterStr(ns, func(v string) bool {
+		return len([]rune(v)) > 4
+	})
+	fmt.Println(ns)
+	sum := ReduceStr(ns, func(v string) string {
+		return v + "-"
+	})
+	fmt.Println(sum)
+}
+

这段代码的意思就是首先,我们有一堆学生的名字,我们在名字后面先加上同学,然后我们要把名字本身大于两个字的同学挑出来,并且把他们组成一句话。

map :[李明月同学 李月同学 百伯同学 张兰同学 武滴滴同学 叶赫那拉同学 迪丽热巴同学]
+filter :[李明月同学 武滴滴同学 叶赫那拉同学 迪丽热巴同学]
+reduce :李明月同学-武滴滴同学-叶赫那拉同学-迪丽热巴同学-
+

通过这个案例我们发现,map-filter-reduce 本身只是控制逻辑,真正的业务逻辑其实是那个函数类型,所以说 map-filter reduce 也是标准的 控制代码-业务代码 分离设计

+ + + diff --git "a/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/2.html" "b/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/2.html" new file mode 100644 index 000000000..b4ee93250 --- /dev/null +++ "b/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/2.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/3.html" "b/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/3.html" new file mode 100644 index 000000000..b96a328cf --- /dev/null +++ "b/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/3.html" @@ -0,0 +1,98 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

修饰器

简单的描述:一个修饰器函数,参数是函数,返回一个运行了这个参数函数的跟参数函数保持相同的函数。

如果是接口,那么就是一个修饰器函数,参数是一个接口,返回值还是这个接口。

func D(f func(s string))func(s string){
+  return func(s string){
+    f(s)
+    fmt.Println("done")
+  }
+}
+
+func main(){
+  D(func(s string){
+    fmt.Println(s)
+  })("hello")
+}
+

具体案例

http 处理 cookie 和 header 的案例:

func main(){
+  http.HandleFunc("/",WithHeader(hello))
+  http.HandleFunc("/s",WithCookie(hello))
+  http.ListenAndServe(":8080",nil)
+}
+
+func hello(w http.ResponseWriter,r *http.Request){
+  fmt.Fprintln(w,"hello")
+}
+func WithHeader(h http.HandlerFunc)http.HandlerFunc{
+  return func(w http.ResponseWriter,r *http.Request){
+    w.Header().Set("Server", "HelloServer")
+    h(w,r)
+  }
+}
+  
+func WithCookie(h http.HandlerFunc) http.HandlerFunc {
+	return func(w http.ResponseWriter, r *http.Request) {
+		cookie := &http.Cookie{Name: "auth", Value: "123456", Path: "/"}
+		http.SetCookie(w, cookie)
+		h(w, r)
+	}
+}
+

多修饰器的 pipeline (串型调用)

type Hander func(http.HandlerFunc)http.HandlerFunc
+
+func DealWith(h http.HandlerFunc,handlers ...Hander)http.HandlerFunc{
+  for i := range handlers {
+    d := handlers[len(handlers) -i-1]
+    h = d(h) 
+  }
+  return h
+}
+func main(){
+  http.HandleFunc("/",DealWith(hello,WithCookie,WithHeader))
+  http.ListenAndServe(":8080",nil)
+}
+
+ + + diff --git "a/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/4.html" "b/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/4.html" new file mode 100644 index 000000000..e03b73034 --- /dev/null +++ "b/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/4.html" @@ -0,0 +1,120 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

pipeline

pipeline 模式,它将一系列的小命令组合成一个大的命令,它可以将***一个大命令拆分为一个一个的高内聚的小命令***,我们可以使用这一系列的小命令组合更多更复杂的命令。

下面给出一个需求,我们将这个需求写成 pipeline 的模式。

需求是:对一个 int 的切片做 “平方”,“过滤奇函数”,“求和” 的操作,并且使用 pipeline 的方式去调用这一系列的高内聚的小函数。

// 初始函数
+func echo(data []int) chan int {
+	out := make(chan int)
+	go func() {
+		for _, v := range data {
+			out <- v
+		}
+		close(out)
+	}()
+	return out
+}
+
+
// 平方
+func sq(in chan int) chan int {
+	out := make(chan int)
+	go func() {
+		for v := range in {
+			out <- v * v
+		}
+		close(out)
+	}()
+	return out
+}
+
+
// 过滤奇函数
+func odd(in chan int) chan int {
+	out := make(chan int)
+	go func() {
+		for v := range in {
+			if v%2 != 0 {
+				out <- v
+			}
+		}
+		close(out)
+	}()
+	return out
+}
+
// 求和
+
+func sum(in chan int) chan int {
+	out := make(chan int)
+	go func() {
+		sum := 0
+		for v := range in {
+			sum += v
+		}
+		out <- sum
+		close(out)
+	}()
+	return out
+}
+

+type EchoFunction func ([]int) (<- chan int) 
+type PipeFunction func (<- chan int) (<- chan int) 
+
+// pipeline 处理
+func Handle(a []int, e EchoFunction, ps ...PipeFunction) chan int {
+	ch := e(a)
+	for _, v := range ps {
+		ch = v(ch)
+	}
+	return ch
+}
+
+
// main函数
+func main() {
+	a := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
+	data := <-Handle(a, echo, sq, odd, sum)
+	fmt.Println(data)
+}
+
+ + + diff --git "a/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/5.html" "b/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/5.html" new file mode 100644 index 000000000..4e0dcea2d --- /dev/null +++ "b/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/5.html" @@ -0,0 +1,84 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

k8s visitor

这是一种将算法和数据结构分离的编程模式。

type Visitor func(shap Shap)
+
+type Shap interface{
+  Accept(Visitor)
+}
+

我们创造一个函数类型,它接受一个接口作为参数。

我们定义刚才那个接口,并且在接口中我们定义一个函数,这个函数中的参数是刚才的函数类型。

type result1 struct {
+  data int
+}
+func(r result1) Accept(v Visitor) {
+  v(r)
+}
+
+type result2 struct {
+  data int
+  name string
+}
+func(r result2) Accept(v Visitor) {
+  v(r)
+}
+

我们实现了两个 struct。

接下来,我们定义一些函数,这些函数是 visitor 函数。

func n1(shap Shap) {
+  fmt.Println(shap)
+}
+func n2(shap Shap) {
+  fmt.Println(shap)
+}
+

接下来我们在 mian 函数中调用这个 visitor 函数。

func main(){
+  r1 := result1{1}
+  r2 := result2{2, "shgopher"}
+  result := []Shap{r1, r2}
+  for _, v := range result {
+    v.Accept(n1)
+    v.Accept(n2)
+  }
+}
+

数据是 r1,和 r2,算法是 n1 和 n2,这样,我们就可以将算法和数据给分开,互相不会有耦合。

+ + + diff --git "a/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/6.html" "b/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/6.html" new file mode 100644 index 000000000..e5f225a6c --- /dev/null +++ "b/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/6.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/7.html" "b/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/7.html" new file mode 100644 index 000000000..06345d235 --- /dev/null +++ "b/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/7.html" @@ -0,0 +1,176 @@ + + + + + + 综合题 | GOFamily - go 程序员宝典 + + + + + + + + +

综合题

本内容选自 k8s (opens new window)

k8s visitor + 修饰器 + pipeline

这种写法,以 k8s 的 visitor 作为主要的实现方法,修饰器和 pipeline 作为辅助的实现手法

首先我们实现这段代码的目的是为了将数据结构和算法分离,并且,我们希望每段的算法处理数据结构的一部分。

// 行为
+type visitorFunc func(*info, error) error
+// 抽象层
+type Visitor interface {
+	Visit(visitorFunc) error
+}
+// 数据
+type info struct {
+	name string
+	year int
+	addr string
+}
+
+func (i *info) Visit(v visitorFunc) error {
+	return v(i, nil)
+}
+

我们基础部分已经写好了,这样就具备了基本的 visitor 的模式,接下来我们来写算法的实现

// 多态
+type OneDeal struct {
+	visitor Visitor
+}
+
+func (o OneDeal) Visit(v visitorFunc) error {
+	return o.visitor.Visit(func(i *info, err error) error {
+		e := v(i, err)
+		fmt.Println("one deal", i.name)
+		return e
+	})
+}
+
+type twoDeal struct {
+	visitor Visitor
+}
+
+func (o twoDeal) Visit(v visitorFunc) error {
+	return o.visitor.Visit(func(i *info, err error) error {
+		e := v(i, err)
+		fmt.Println("two deal", i.addr, i.year)
+		return e
+	})
+}
+

当算法搞定以后,我们在 main 调用一下:

func main() {
+	o := &info{}
+	var v Visitor = o
+	v = OneDeal{v}
+	v = twoDeal{v}
+	l := func(i *info, err error) error {
+		i.name = "liu"
+		i.year = 2020
+		i.addr = "beijing"
+		return nil
+	}
+	v.Visit(l)
+}
+
+

可以看到,visitor,pipline,以及修饰器的模式都运用了

  • visitor:上面一个 type func,下面的 interface,连在一起就是 visitor 模式
  • pipline:可以看到 visitor 的变量在不停的传递,然后调用的时候肯定会一直的调用不同的 visit func 函数,这就是 pipline
  • 修饰器:不同的 function,在不停的增加内容,并且输出的还是这个 function,这就是修饰器模式

可以看到,这个模式下,visitor 是主要的模式,其他的模式都是因这个模式而附带的效果,所以也可以说,visitor 自带 pipline 和修饰器模式,所以这种写法就单单的只是 visitor 而已。

接下来,我们将修饰器模式作为主要的模式,另外加上 visitor 和 pipline

修饰器 + k8s visitor + pipeline

同样的我们需要先构造一个 visitor 系统


+type VisitorFunc func(*Info, error) error
+
+type Visitor interface {
+	Visit(VisitorFunc) error
+}
+type Info struct {
+	name string
+	year int
+	addr string
+}
+
+func (i *Info) Visit(v VisitorFunc) error {
+	return v(i, nil)
+}
+

接下来我们创建一个修饰器系统

type VisitorBox struct {
+	visitor     Visitor // 行为
+	visitorFucs []VisitorFunc // 代表数据的桥梁
+}
+
+func NewVisitorBox(v Visitor, fn ...VisitorFunc) Visitor {
+	if len(fn) == 0 {
+		return v
+	}
+	return VisitorBox{visitorFucs: fn, visitor: v}
+}
+func (vi VisitorBox) Visit(v VisitorFunc) error {
+	return vi.visitor.Visit(func(i *Info, e error) error {
+		if e != nil {
+			return e
+		}
+
+		if err := v(i, nil); err != nil {
+			return err
+		}
+
+		for k := range vi.visitorFucs {
+			if err := vi.visitorFucs[k](i, nil); err != nil {
+				return err
+			}
+		}
+		return nil
+
+	})
+}
+
+

接下来让我们调用一下

func main() {
+
+	loadFile := func(info *Info, err error) error {
+		info.name = "li"
+		info.year = 10
+		info.addr = "bj"
+		return nil
+	}
+	func1 := func(info *Info, err error) error {
+		fmt.Println(info.name)
+		return nil
+	}
+	func2 := func(info *Info, err error) error {
+		fmt.Println(info.year)
+		return nil
+	}
+	func3 := func(info *Info, err error) error {
+		fmt.Println(info.addr)
+		return nil
+	}
+
+	info := &Info{}
+	var v Visitor = info
+	v = NewVisitorBox(v, func1, func2, func3)
+	v.Visit(loadFile)
+}
+

这种写法跟上面的写法改变之处在于创建了一个修饰器的 struct,并且装在了一个 pipline 的切片,然后我们就不用依次调用不同的算法结构体了,我们只需要在 new box 的时候将要执行的函数放入进去即可。

+ + + diff --git "a/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/index.html" "b/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/index.html" new file mode 100644 index 000000000..0a20d3dc3 --- /dev/null +++ "b/\345\237\272\347\241\200/\345\207\275\346\225\260\346\226\271\346\263\225/index.html" @@ -0,0 +1,1050 @@ + + + + + + 函数和方法 | GOFamily - go 程序员宝典 + + + + + + + + +

函数和方法

重点内容前情提要

  • 函数初始化的顺序
  • init 函数
  • defer 的运用
  • 变长参数
  • 类型嵌入来完成继承
  • define 类型的方法集合
  • 类型别名的方法集合
  • 函数式编程

go 语言中函数的基本使用方法如下:

// 函数的一般用法, go 拥有多个返回值
+func Get(i int)(int, error) {
+  return 0, nil
+}
+// 返回值具有变量的返回模式
+func Post(i int)(value int){
+  value = 1
+  return
+}
+
+// 函数作为value值赋值给一个变量变量
+var Get0 = func(i int) int {
+  return 0
+}
+

函数的初始化顺序

  • main 函数是所有 go 可执行函数的起始位置
  • 在 main 函数运行前,需要先运行导入子包的流程
  • 在导包的过程中,最先运行的是最深层的子包
  • 按照全局常量,全局变量,init 函数的顺序进行初始化的操作
  • 子包初始化之后,开始返回前一层的包中进行常量,全局变量,init 函数的初始化,图示流程非常清晰的画出了这个过程。
  • 多个相同的包只会导入一次,并且取相同包不同版本的可满足的最小版本导入,例如 v1.1.0 v1.2.0,只导入 v1.2.0,不会默认导入最新的版本。

init 函数

在一个包中,以及在一个包的某个文件中,可以存在多个 init 函数,一般来说,同一个包的不同 file,按照文件字符串比较的 “从小到大” 的顺序先被编译 file 中的 init 先执行,同一个 file 中的 init 函数按照先后顺序执行,但是 go 语言规范告诉我们,不要依赖 init 的执行顺序。

init 函数无法主动执行,它的执行是系统自动执行的,所以显式的去调用 init 函数会发生报错。

通常来说,init 的目的就是系统的初始化,因为它一定会被执行,下面我们介绍一下 init 函数的几个用途。

重置包级变量值。

// src/context/context.go
+
+var closedchan = make(chan struct{})
+func init(){
+  close(closedchan)
+}
+

我们的包级变量是一个 chan,并且需要它是一个已经关闭的 chan,我们使用 init 来确保它提前一定处于一个已关闭的状态。

对包级变量进行初始化,保证其后续可用。

var (
+  OutBox []int
+  InBox  chan int
+)
+
+func init(){
+  OutBox = make([]int, 10)
+  InBox = make(chan int, 10)
+}
+

init 函数中的注册模式。

这种模式在 go 中有两处经典的案例,其一是 database/sql 包的使用,其二是 image 包的使用。

// databsae/sql 包的使用
+
+import(
+  "database/sql"
+  _ "github.com/lib/pq" 
+)
+
+func main() {
+
+  db, err := sql.Open("postgres", "")
+}
+

这里使用 import _ 就是只有导包的过程,并没有任何除了 init 函数之外的函数调用。那么这么做的原因是什么呢?

工厂方法 + 导包的顺序

下面我们看一下这两个包的源码

// github.com/golnag/go/src/database/sql/sql.go
+var drivers = make(map[string]driver.Driver)
+
+func Open(driverName, dataSourceName string) (*DB, error) {
+	driveri, ok := drivers[driverName]
+}
+
+func Register(name string, driver driver.Driver) {
+	drivers[name] = driver
+}
+
  • sql 包有一个全局的 map,这里存放了所有的 driver,这个 map 的 key 是 driver 的名字,value 是 driver.Driver 的接口类型,通常来说存入的都是实现了这个接口的动态类型。
  • open 函数会直接判断是否拥有含有这个 key 的 value 值
  • register 函数,也就是注册函数,这个函数通常由实现了 sql.driver 的数据库第三方库的 init 函数去进行最终的注册。
// github.com/lib/pq
+
+func init() {
+	sql.Register("postgres", &Driver{})
+}
+
  • 这里就是实现了 sql.Driver 的 pq.Driver 结构体进行了名为 “postgress” 的注册。

下面我们进行整体的分析:

  • 在 pq 包实现的时候,它引入了 “database/sql”,“database/sql/driver” 这两个包,它将实现的数据库实例注册到 sql 的 map 中
  • 用户调用的时候,我们调用了 sql.Open 使用不同的 name 就可以使用不同的数据库,这里是工厂方法设计模式的运用

这种方法可以使 pq 这个包完全没有暴露到用户面前,仅需要使用 sql 包就可以间接的使用 pq 的代码,很好的做了隔绝。

下面看一下 image 包的运用。

package main
+
+import(
+  "image"
+  _ "image/png"
+  _ "image/gif"
+  _ "image/jpeg"
+  "os"
+)
+
+func main(){
+
+  width,height,error := image(os.Args[1])
+  if error != nil{
+    println(error)
+  }
+  println(width, height)
+
+}
+
+func image(f string)(int,int,error){
+  file,err := os.Open(f)
+  defer file.Close()
+
+  img, _, err := image.Decode(file)
+  if err != nil {
+    return 0,0,err
+  }
+
+  b := img.Bounds()
+  return b.Max.X,b.Max.Y,nil
+}
+

我们发现,image.Decode(f) 应该就如同上文提到的 Open 函数一样的功能,也就是工厂方法中的工厂。

下面我们看一下 image/gif 包中的 init 函数:

//https://github.com/golang/go/blob/master/src/image/gif/reader.go
+
+func init() {
+	image.RegisterFormat("gif", "GIF8?a", Decode, DecodeConfig)
+}
+

可以看到,这个 gif 包也是调用了 image 包,并且通过 init 函数,将动态类型注册到了 image 提供的注册器中。

init 函数中检查失败的处理方法。

使用 init 去检查某些数据的正确性,相当于做最后的质检工作,一旦发生了错误,直接使用 Panic,快速停掉程序。

defer 函数

func Get(){
+  f,err := os.Open("test.txt")
+  if err != nil{
+    panic(err)
+  }
+  // 此处就是 defer 函数
+  defer f.Close()  
+}
+

defer 函数有下面几个规则

  • defer 后面只能跟函数或者是方法
  • 一个函数中的 defer 函数,会使用 “栈” 的方式运行
  • defer 后面的函数和方法,返回值会被直接舍弃

下面看一下 defer 函数的几种用法

配合 recover 函数处理 panic

在 go 里,如果要处理函数内的 panic,有且仅有一种方法,那就是使用 recover 函数。而且 recover 函数必须运行在 defer 之中。那么让我们看一下具体的代码实现

func Get() {
+	defer func() {
+		if err := recover(); err != nil {
+				println("recoverd")
+		}
+	}()
+	panic("panic")
+}
+

修改函数的具名返回值

具名返回值,就是返回值带有变量的,比如:

func hi(a,b int)(x,y int){
+  defer func(){
+    x ++
+    y ++
+  }()
+  x = a+b
+  y = a-b
+  return  
+}
+// hi(1,3) 返回值为 (5,-1)
+

在分析这段代码的时候,我们要牢记一句话,在具名返回值的函数中,go 的 return 并不会立刻 return,它会等待 defer 执行完毕后再真的 return。也就是说,它 return 的是 x y 的最后时刻。

那么,让我们看一下非具名的函数:

func hi(a,b int)(int,int){ 
+	var x,y int
+  defer func(){
+    x ++
+    y ++
+		println("defer 会执行的")
+  }()
+  x = a+b
+  y = a-b
+  return  x, y
+}
+// hi(1,3) 
+
+// output:
+// defer 会执行的 5 -1
+// 4 -2
+

在非具名的函数中,return x,y 的时候就已经决定返回值的最终结果了,但是这个 defer 还是会执行,这个是真的,xy 的值确实也是在 defer 里改变了,但是 return 的是之前的中间量,defer 里面的 xy,并不会左右之前已经设定好的 x y 的复制值了。

所以输出的是 4 -2,而且 defer 里面的输出 “defer 会执行的 5 -1” 也执行了。

defer 函数虽然是先入后出,在所有的正式指令执行完成以后执行,但是它的变量初始化可是***顺序的***,这里强调一下,仅仅是变量的初始化是顺序执行,defer 函数是不会顺序执行的:

func hi(a, b int) (x, y int) {
+	j := 0
+  // j 顺序的初始化数据了,
+  // println却不会顺序执行!
+	defer func(j int) {
+		println(j)
+	}(j)
+	j++
+	return
+}
+// output: 0
+

这里的 defer 顺序执行完变量的初始化,所以它里面的 j 变量完成了值的复制,为 0,那么下文的 j++ 就不会对上面的复制品起任何作用了,即便 prinln(j) 确实发生在 j++ 之后,总结一句话,defer 函数执行是最后,但是初始化是正常的先后顺序。

如果想得到 1 的答案,可以这么改:

func hi(a, b int) (x, y int) {
+	j := 0
+	defer func() {
+		println(j)
+	}()
+	j++
+	return
+}
+

这里的 j 就会受到下面 j++的影响了。

还有一点要注意,defer 函数的作用域也是正常的作用域,也就是说,上文 hi 函数演示内容,j:= 0 必须声明在 defer 函数之前,虽然我们知道 defer 的内容最后执行,但是它也遵循正常的作用域。如果 j := 0 发生在 defer 之后,它就会无法找到这个变量。

看了上面那么多容易搞混的 defer 用法,这里要说明一下,正常的使用方法是这样的:

func hi(a,b int)(x,y int){
+  defer func(){
+    x ++
+    y ++
+  }()
+  x = a+b
+  y = a-b
+  return  
+}
+

也就是我们第一种使用的惯例,它的效果就是最后改变返回值,这也是这几种方式中最常用的方式。

看了上文关于 defer 函数的初始化 int 类型参数以后,我们还得注意切片在 defer 函数初始化参数的问题,因为它更容易出现误判。

func Get(){
+  s1 := []int{1,2,3}
+  defer func(v []int){
+    println(v)
+  }(s1)
+  s1 = []int{4,5,6}
+}
+
func Get(){
+  s1 := []int{1,2,3}
+  defer func(v *[]int){
+    println(v)
+  }(&s1)
+  s1 = []int{4,5,6}
+}
+
  • 前者输出 1 2 3
  • 后者输出 4 5 6

我们分析一下:

❗️ 这个地方很容易搞错,望周知。

首先我们确定的一件事就是:defer 函数的参数初始化是顺序执行的,它顺序执行之后,将数据保存在了一个专用的栈中。

所以,第一个函数中,v 就等于 []int {1,2,3},换言之,v 初始化的时候等于一个指向底层数据是 1 2 3 的数组,所以当 s1 重新指向一个新的数据 4 5 6 的时候,v 的数据并不会有任何的影响,它仍然执行的还是底层数据是 1 2 3 的数组,所以它的最终结果就是输出 1 2 3

第二个函数,在初始化阶段,v 正常的初始化,它的值是 s1 的地址,那么当下文中 s1 重新指向了一个新的底层数组为 4 5 6 的数据之后,v 的数据是一直跟着 s1 走的,因为它的本质是 s1 的地址,这把钥匙一直都没有变化,所以它的最终结果就是新的内容 4 5 6

最后提一嘴,defer 函数的性能消耗 (go 1.14+),跟不使用 defer 相比,几乎没有差距,所以放心大胆的使用 defer。

输出调试信息

func trace(s string)string{
+  println("prepare",s)
+  return s
+}
+func un(s string){
+  println("out",s)
+}
+func a(){
+  defer un(trace("a"))
+  println("in a")
+}
+func b(){
+  defer un(trace("b"))
+  println("in b")
+  a()
+}
+func main(){
+  b()
+}
+
+// prepare b
+// in b
+// prepare a
+// in a
+// out a
+// out b 
+

这是 go 文档提供的一个日志记录的例子,接下来我们分析一下

  • 首先 b 的执行,b 中的 defer 函数 un 开始初始化,它的初始化就是执行 trace (“b”),所以最先执行的是 prepare b
  • 然后开始执行 println (“in b”)
  • 接下来执行 a,跟 b 一样,先执行初始化的 un 中的 trace (“a”) 函数,然后执行 println (“in a”)
  • 然后开始执行 a 的 defer 函数,所以 a 的 out 先执行,(因为它后入,先出)
  • 接下来最后执行的是 b 中的 defer,那么就是 out b

还原变量的旧值

func init(){
+  oldfile := firstfile
+  defer func(){
+    firstfile = oldfile
+  }()
+  firstfile = "README.md"
+  //...
+}
+

我们知道,defer 后面只能放置函数,包括自匿名函数,或者是自定义的函数以及方法 (如果有返回值自动舍弃),那么 go 内置的函数是否可以放到 defer 后面执行呢?

答案是,部分内置函数可以直接放到 defer 后面,部分不能直接放到 defer 后面执行,需要放到一个匿名或者自定义函数中。

  • 可以直接放到 defer 后面的函数有:close copy delete print recover
  • 不能直接放到 defer 后面的函数有:append cap len make new

但是通常来说,我们使用 defer 的时候都是会跟一个匿名函数或者是自定义的函数,并不会直接跟一个内置函数。

比如直接跟匿名函数的:

func Get(){
+  defer func(){
+    if err := recover(); err != nil {
+      println(err)
+    }
+  }()
+  panic("error")
+}
+

跟一个自定义函数的:

func Get(){
+  f,err := os.Open("README.md")
+  
+  // defer 后面是一个方法,并且舍弃了这个方法的error参数
+  defer f.Close()
+}
+

defer 函数因为无法 return 造成的内存泄露 bug

func readFiles(ch <-chan string) error {
+	for path := range ch {
+		file, err := os.Open(path)
+		if err != nil {
+			return err
+		}
+		defer file.Close()
+		// Do something with file
+	}
+	return nil
+}
+

在这个案例中,defer 函数用于关闭一个文件的句柄,假设我们在读取文件的过程中发生了 bug,readFile 永远无法 return nil,那么这个 defer 函数就永远不会实行,就会造成内存的泄露。

改正的话,我们可以给 defer 函数 wrap 一层函数,在这个子函数中只要能 return,运行 defer 函数即可

func readFiles(ch <-chan string) error {
+	for path := range ch {
+		if err := readFile(path); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+// 我们设置了一个新的函数,只要在这个函数里可以正常return 即可
+func readFile(path string) error {
+	file, err := os.Open(path)
+	if err != nil {
+		return err
+	}
+	defer file.Close()
+	// Do something with file
+	return nil
+}
+

再次强调:有无返回值具体变量对于 defer 的影响

有返回变量

func main(){
+  // 13
+  // 12
+  fmt.Print(a())
+}
+func a() (i int) {
+
+	defer func() {
+		i++
+		fmt.Println(i + 1) //13
+	}()
+	i++
+	i += 10
+	return
+
+}
+

执行顺序是这样的: +1。i 初始化为原始数据 0 +2。i++ +3。i+= 10 +4。i++ +5。fmt.Println(i+1) // 13 +6。return 这个时候最终的 i // 12

所以当拥有返回值变量的时候,return 返回的是最终的 i,就连 defer 中的 i 的变量也算上。

无返回变量

func main(){
+  // 3
+  // 11
+  fmt.Print(a())
+}
+func a() int {
+  i:= 0
+	defer func() {
+		i++
+		fmt.Println(i + 1) //3
+	}()
+	i++
+	
+	return i + 10
+
+}
+

执行的顺序: +1。i 初始化为 0 +2。i++ +3。执行 i + 10 并把这个数据记录到 return 上去,但是并不会真的 return +4。i++ +5。fmt.Println(i+1)// 3 +6。defer 执行完毕以后,return 开始返回之前记录的那个值。

为什么在这个没有返回变量的时候,i 在 defer 中的变化不会影响返回值呢,因为返回值记录的那个值发生在 defer 之前,所以 defer 再将 i 变化也不会影响之前记录的那个值了,那个值是已经固定的了,它没有立即返回是因为要执行 defer,你也可以理解为

func a() int {
+  i:= 0
+	defer func() {
+		i++
+		fmt.Println(i + 1) //3
+	}()
+	i++
+	a := i+10
+	return a
+}
+

所以 a 的值是不会再受到 defer 中的 i 的变化的。

变长参数

举个变长参数函数的例子:

func Println(a ...any) (n int, err error) {
+	return Fprintln(os.Stdout, a...)
+}
+

我们平时使用的 fmt.Println(...any) 就是标准的变长参数的例子。

它的组织形式就是 ... + 类型,比如 ...int ...string

  1. 一个函数的参数中只能有一个变长参数,且必须为最后一位
  2. 变长参数在函数内部以 slice 的方式存在
  3. 变长参数只能接受两种形式的值,其一就是多个同样类型的值,例如 fmt.Println("hi","there",12),或者直接接受一个 slice...,例如 fmt.Println([]any{1, 2, "hi"}...),并且,这两种用法不能混用,比如 fmt.Println(1,2,[]any{1,2}...) 这种混写的方法错误。

any 类型和 string 类型是绝对的不同的两种类型,因为 any 是 interface {} 的别名 (type any = interface{}),string 类型虽然实现了空接口,但是它不是空接口类型,如果要转换成空接口,必须显式的转换:

func main() {
+	a := []int{12,3}
+	b := any(a)
+	fmt.Println(b)
+}
+

go 语言在 append 字符串到[]byte 的时候提供了一个语法糖

func append(slice []Type, elems ...Type) []Type

 func main() {
+	a := []byte{1, 3, 4}
+	var b string = "ee"
+	a = append(a, b...)
+}
+

这种语法糖只适用于 append 内置函数。底层应该是帮你把字符串转化为了 []byte()

func main() {
+    // 类似这种转换
+	a := []byte{1, 3, 4}
+	var b string = "ee"
+	c := []byte(b)
+	a = append(a, c...)
+}
+

如果自己实现一个的时候不进行转换就会报错。

func get(...byte){
+
+}
+func main(){
+
+  get("123"...)
+}
+// ❌
+//cannot use "123" (untyped string constant)
+// as []byte value in argument to get
+
+

使用变长函数去模拟重载函数

重载函数就是同一个作用域下,可以有相同名称的函数,只不过他们的参数不同,go 语言是不支持这种类型的函数的。

类似这种

func main(){}
+func get(){}
+func get(s string){}
+

如果真的想使用这种重载函数,我们可以使用这种方法来间接实现:

func Get(s string, args ...any) {
+	for _, v := range args {
+		switch v.(type) {
+		case int8, int, int16:
+			fmt.Println(vi)
+		case string:
+			
+		}
+	}
+}
+
+

使用变长函数去实现默认参数

func main() {
+	CheckIn("shgopher", 12, "男")
+  CheckIn("jackie",20,"男","上海")
+}
+
+type Contents struct {
+	Name    string
+	Age     int
+	Sex     string
+	Address string
+}
+
+func CheckIn(arges ...any) (*Contents, error) {
+	c := &Contents{
+		Address: "Beijing ",
+	}
+	for k, v := range arges {
+		switch k {
+		case 0:
+			name, ok := v.(string)
+			if !ok {
+				return nil, fmt.Errorf("name is not a string")
+			}
+			c.Name = name
+		case 1:
+			age, ok := v.(int)
+			if !ok {
+				return nil, fmt.Errorf("age is not a int")
+			}
+			c.Age = age
+		case 2:
+			sex, ok := v.(string)
+			if !ok {
+				return nil, fmt.Errorf("sex is not a string")
+			}
+			c.Sex = sex
+		case 3:
+			address, ok := v.(string)
+			if !ok {
+				return nil, fmt.Errorf("address is not a string")
+			}
+			c.Address = address
+		default:
+			return nil, fmt.Errorf("unknown argument")
+		}
+	}
+	return c, nil
+}
+

这段代码的意思就是先给定一个默认值,比如这里地址是默认值的,也即是它是可选的内容,鉴于这段代码标识的意义,这个可省略的一定是在最后一位的。

方法

接下来,我们介绍方法,所谓方法,其实只是函数的一种语法糖,它跟函数并没有本质的区别,我们看一个简单的例子:

type Student struct{
+  name string
+  year int
+  addr string
+}
+
+func(s *Student)GetName(name string){
+  s.name = name
+}
+

可以看到,这是一个定义在指针类型 *student 上的方法 GetName,实际上,它完全等于函数的这种形态:

func GetName(s *Student, name string) {
+  s.name = name
+}
+

go 语言本身对于方法的使用是很宽泛的:

  • 指针类型的变量可以使用值类型的方法
  • 值类型的变量可以使用指针类型的方法

这跟后文要讲的接口绝对是差距极大,因为接口的要求非常严格,这个可以看接口和函数的对比。

值类型的变量可以使用指针类型的方法

type Student struct{
+  name string
+  year int
+  addr string
+}
+
+func(s *Student)GetName(name string){
+  s.name = name
+}
+
+func main(){
+  var s Student
+  s.GetName("张三")
+}
+

当值类型来使用指针类型的方法时,系统默认会调用这个值的指针,因此这是系统给予的语法糖。

指针类型的变量可以使用值类型的方法

type Student struct{
+  name string
+  year int
+  addr string
+}
+
+func(s Student)GetName(name string){
+  s.name = name
+}
+
+func main(){
+  var s = new(Student)
+  s.GetName("张三")
+  fmt.Println("out:", s.name)//out:
+}
+

当指针类型来使用值类型的方法时,系统默认会调用这个指针指向的值,因此这是系统给予的语法糖。

这个例子其实是错误的行为,因为 s 的方法是定义在值类型的,使用一个 s 去调用它上面的 GetName,改变的只是方法中,s 的***复制品上***的值,外部调用的这个 s,实际上是不会有任何的改变的,我们如果从实质出发 func GetName(s Student, name string),就能看出来了。

跟函数以及全局变量,常量是一样的,方法也是首字母大写可以导出包,小写无法导出包。

这里有个细节要注意一下,不能跨越包去定义方法,go 语言不支持,比如,原生类型 (int,map,slice,bool 等) 是无法提供方法的,例如

// ❌
+func(i int)Get(){}
+

所以通常来说,我们就定义一个底层为 int 的新类型才可以

type A int
+func(a A)Get(){}
+

那么我们总结一下关于方法的几个小细节:

  • 方法首字母的大小写注定了是否可以导出包
  • 不能跨越包去定义方法
  • 每一个方法只能被一个类型去定义,不能俩类型去定义一个方法
  • 指针类型和接口类型不能作为方法的基底类型

最后一条,我们详细展开一下:

以下定义是错误的:

// ❌ 指针类型不能有自己的方法
+type A *int
+func(a A)Get(){}
+
+// ✅ 类型的指针可以有自己的方法
+type A int
+func(a *A)Get(){}
+
type A interface {
+	get()
+}
+// ❌ 接口类型不能有自己的方法
+func (A) get() {}
+
// ❌ 以接口为基底的类型不能有自己的方法
+type A xxInterface
+func(a A)Get()
+

类型嵌入来完成继承

go 语言使用类型的嵌入来实现继承母体的对象以及对象上的方法。

type People struct {
+  name string
+}
+func(p *People)Name(){
+  fmt.Println(p.name)
+}
+

现在我们将类型嵌入到一个新的类型来实现继承:

type Student struct {
+  People
+  address string
+}
+func(s *Student)Address(){
+  fmt.Println(s.address)
+}
+func main(){
+  var s Student
+  s.Name()
+  s.Address()
+}
+

我们还可以重写继承的方法来完成重载操作:

func(s *Student)Name(){
+  fmt.Println(s.People.Name()+"学生")
+
+}
+

当发生嵌入类型和本类型,字段重合的时候,优先调用本类型的字段,嵌入类型的只需要加上前缀就可以了。

type Student struct {
+  People
+  name string
+}
+type People struct {
+  name string
+}
+
+func main(){
+  var s Student
+  s.name = "1"
+  s.People.name = "2"
+}
+

如果你不想直接嵌入,也可以在前面加上变量名称:

type Student struct {
+  // 不嵌入也可以
+  people People
+  name string
+}
+type People struct {
+  name string
+}
+
+func main(){
+  var s Student
+  s.name = "1"
+  s.people.name = "2"
+}
+
+

不过,如果是不嵌入的方式,就无法直接调用方法了,需要加上前缀。

func main(){
+  var s Student
+  s.people.name()
+}
+

内置类型也可以作为字段直接嵌入到新类型中

type a struct{
+  int
+  string
+}
+

不过使用的时候比较非常规了:

func main(){
+  var a1 a
+  a1.int = 1
+  a1.string = "2"
+  fmt.Println(a1)
+}
+

指针类型也可以直接嵌入:


+type a struct {
+	int
+	string
+	*b
+}
+type b struct {
+}
+
+func main() {
+	var a1 a = a{
+		int:    0,
+		string: "",
+		b: &b{},
+	}
+
+	fmt.Println(a1)
+}
+

我们可以看到,直接嵌入的时候,其实是省略了写法。但是通常,int string,这种内置的类型我们都会指定一个变量给他们。例如常规写法:

type A struct {
+  People
+  name string
+  year int
+  b *b
+}
+

无论嵌入的是值类型还是指针类型,函数都可以直接调用他们身上的方法:

type People struct{
+  name int
+}
+
+func(p *People)Name(){
+  fmt.Println(p.name)
+}
+type Address struct{
+  value string
+}
+func(a *Address)Value(){
+  fmt.Println(a.value)
+}
+type Student struct{
+  People
+  *Address
+}
+func main(){
+  var s Student
+  //由于这里的address字段是指针,所以我们必须给address赋予实际的值的地址:
+  s = Student{
+    Address: &Address{
+      value: "1",
+    },
+  }
+  s.Name()
+  s.Value()
+}
+

s 是指针类型和值类型在调用实质上还是区别的,但是在实际使用中,并不会有什么区别,这主要还是因为要看方法是定义在值类型还是指针类型上。

值类型

func main(){
+  var s Student
+  s = Student{
+    Address: &Address{
+      value: "1",
+    },
+  }
+  s.Name()
+  s.Value()
+}
+
func main(){
+  var s *Student
+  //由于这里的address字段是指针,所以我们必须给address赋予实际的值的地址:
+  s = &Student{
+    Address: &Address{
+      value: "1",
+    },
+  }
+  s.Name()
+  s.Value()
+}
+

当 s 是值时,它调用的就是 People 的方法 + *Address 的方法,

但是如果 s 这里是指针类型的话,那么它调用的就是 *People 的方法 +*Address 的方法

不过即便 s 是值的时候,people 上本身是定义在指针上的方法,那么它在底层调用的时候也势必是 *People

总结一下:结构体变量是什么类型不重要,重要的是定义方法时使用的是什么类型。

你也可以不使用嵌入,使用 s.Address.Value() 来调用方法:


+type Student struct{
+  People People
+  Address *Address
+}
+func main(){
+  var s Student
+  //由于这里的address字段是指针,所以我们必须给address赋予实际的值的地址:
+  s = Student{
+    Address: &Address{
+      value: "1",
+    },
+  }
+  s.People.Name()
+  s.Address.Value()
+}
+

define 类型的方法集合

define 就是 type A int 的意思 (type A = int 是另一个意思表示 alias),其中新类型 A 叫做 define 类型,int 叫做 underlying 类型

define 类型的底层如果是接口,那么它完全可以 “继承” 底层数据的方法,比如底层接口拥有三个抽象函数,那么它也有三个一模一样的三个抽线函数

func main() {
+	fmt.Println("Hello, 世界")
+
+	var b B
+	var d D
+	b = d
+	b.get()
+
+}
+
+type A interface {
+	get()
+}
+
+type B A
+
+type D int
+
+func (D) get() {
+
+	println("hi")
+}
+
+

但是,如果不是接口类型,那么这个类型上就什么方法都没有。它跟它底层的 underlying 将没有任何的联系。

func main() {
+	fmt.Println("Hello, 世界")
+	var a1 a
+	a1.get()
+
+	var b1 b
+	b1.get()
+}
+
+type a struct{}
+
+func (a) get() {
+	println("hi")
+}
+
+type b a
+
+// error: b1.get undefined (type b has no field or method get)
+

类型别名的方法集合

接下来我们介绍一个类型的别名 alias

使用方法是这样的:

type rune = int32
+

可以看到跟

type rune int32
+

非常像,但是,我要强调一下,这两者是完全不同的东西。前者是类型别名,rune 就是 int32 的一个分身,它跟 int32 完全拥有相同的权利,后者,rune 和 int32 是完全两个类型,只是 rune 使用了 int32 作为自己的底层数据而已。

我们看一个例子:

package main
+
+import "fmt"
+
+func main() {
+	fmt.Println("Hello, 世界")
+
+	var h hiInterface
+	var hi hiStruct
+	h = hi
+
+	h.get()
+}
+
+type myInterface interface {
+	get()
+}
+
+type myStruct struct{}
+
+func (myStruct) get() {
+
+	println("hi")
+}
+
+type hiInterface = myInterface
+type hiStruct = myStruct
+
+

可以看到,别名和类型之间,完全相同,完全等价,不管是接口还是结构体,亦或者是其它的东西。

函数式编程

函数就是一个普通的类型,它跟 int,string,拥有相同的地位,所以你会发现函数式编程在 go 语言的代码里运用的很广泛。

比如:

package main
+
+func main() {
+	 a, _ := Get("hello", func(s string) int {
+		println(s)
+		return len(s)
+	})
+	println(a)
+}
+
+func Get(s string, f func(string) int) (int, error) {
+	return f(s), nil
+}
+

函数作为 go 语言中的一等公民,拥有以下特征:

  • 在源码的顶层正常的创建函数
  • 函数可以存在于函数内部
  • 函数可以作为类型
  • 函数可以赋值给一个变量
  • 函数可以作为参数
  • 函数可以作为返回值,并且拥有闭包
// 在源码的顶层正常的创建函数
+// main.go
+
+func main() {}
+
// 函数可以存在于函数内部
+
+fun Get(){
+  var a = func(s string){println(s)}
+}
+
// 函数可以作为类型
+type A func(s string) int
+
+
//  函数可以赋值给一个变量
+
+func main() {}
+var a = func(s string){println(s)}
+
// 函数可以作为参数
+
+func main() {
+	Get("hello", func(s string) int {
+		println(s)
+		return len(s)
+	})
+}
+
+func Get(s string, f1 func(string) int) int {
+	return f1(s)
+}
+
// 函数可以作为返回值,并且拥有闭包
+package main
+
+import "fmt"
+
+func main() {
+
+	g := Get()
+	fmt.Println(g("hello"))
+	fmt.Println(g("world"))
+	fmt.Println(g("--------------------------------"))
+	fmt.Println(g("hi"))
+	fmt.Println(g("你好"))
+}
+
+func Get() func(string) int {
+	i := 0
+	return func(s string) int {
+		println(s)
+		i++
+		return i
+	}
+}
+//out
+//hello
+// 1
+// world
+// 2
+// --------------------------------
+// 3
+// hi
+// 4
+// 你好
+// 5
+
+

函数式编程的实际应用

柯里化函数

概念:接受多个参数的函数,变成接受一个单一参数的函数,并且返回接受剩余参数以及返回值的新函数。

func sum(x, y, c int) int {
+	return x + y + c
+}
+
+func partialSum(x int) func(int, int) int {
+	return func(y, c int) int {
+		return sum(x, y, c)
+	}
+}
+
+func main() {
+	t1 := partialSum(1)
+	t2 := partialSum(2)
+	t3 := partialSum(3)
+	fmt.Println(t1(4, 5))
+	fmt.Println(t2(6, 7))
+	fmt.Println(t3(8, 9))
+}
+
+

函子

概念:functor (函子) 本身是一个容器 (slice map channel),容器类型实现一个方法,该方法接受一个函数类型参数,并且每一个容器参数都要被这个函数去改变,这里会得到一个新的 functor,原有的容器没有任何的影响。

package main
+
+import "fmt"
+
+type IntSliceFunctor interface {
+	Fmap(func(int) int) IntSliceFunctor
+}
+
+type IntSliceFunctorImpl struct {
+	ints []int
+}
+
+func (f IntSliceFunctorImpl) Fmap(f1 func(int) int) IntSliceFunctor {
+	newInts := make([]int, len(f.ints))
+	for i, v := range f.ints {
+		newInts[i] = f1(v)
+	}
+	return IntSliceFunctorImpl{newInts}
+}
+
+func NewIntSliceFunctorImpl(ints []int) IntSliceFunctor {
+	return IntSliceFunctorImpl{ints}
+}
+
+func main() {
+	i := NewIntSliceFunctorImpl([]int{1, 3, 4})
+	m1 := i.Fmap(func(i int) int {
+		return i * 2
+	})
+	m1p := i.Fmap(func(i int) int {
+		return i * 20
+	})
+	m2 := m1.Fmap(func(i int) int {
+		return i * 20
+	})
+	fmt.Println(m1, m1p, m2)
+}
+
+

配置选项问题

最基础的方法就是全部暴露出去

type Server struct {
+  Addr string
+  Port int
+  Protocol string
+}
+
+func NewDefalutServer(addr string,port int,protocol string) *Server {
+  return &Server{
+    addr,
+    port,
+    protocol,
+  }
+}
+
+func NewPortServer(addr string)*Server {
+  return &Server{
+    addr,
+    "8080",
+    "tcp",
+  }
+}
+

直接暴露这是一种最基础的方案,这种方法可扩展性很差。

如果想改进,完全可以把非固定的字段单独的封装在一个 struct 中,比如这种写法:

type Server struct {
+  Addr string
+  options *Options
+  
+}
+type Options struct {
+  Port string
+  Protocol string
+}
+
+func NewServer(addr string, options *Options) *Server {
+  return &Server{
+    addr,
+    options,
+  }
+}
+

还有一种场景是这样的,我们输出的 API 是一定的,但是我们的配置信息,因为是共同使用的,它可能会越来越多,这个时候改如何处理呢?

设置一个固定的 struct,以及一个可共用的 opintions struct

type Server struct {
+  Addr string
+  Port int
+  Protocol string
+}
+
+type Options struct{
+  Addr string
+  Port int
+  Protocol string
+
+}
+ // 这种写法,API内容不变,共同的options即便是变化了也无关紧要。
+func NewServer(options *Options) *Server {
+  var addr string
+  var port int
+  var protocol string
+
+  if options != nil {
+    addr = options.Addr
+    port = options.Port
+    protocol = options.Protocol
+  }
+
+  return &Server{
+    addr,
+    port,
+    protocol,
+  }
+}
+

我们还可以使用链式调用的方式去写这种参数

type Server struct {
+  Addr string
+  Port int
+  Protocol string
+}
+type ServerBulder struct {
+  Server
+}
+func(sb *ServerBulder) Build(addr string) *ServerBulder {
+  sb.Addr = addr
+  return sb
+}
+func(sb *ServerBulder) BuildWithPort(port int) *ServerBulder {
+  sb.Port = port
+  return sb
+}
+func(sb *ServerBulder) BuildWithProtocol(protocol string)*ServerBulder {
+  sb.Protocol = protocol
+  return sb
+}
+func(sb *ServerBulder) Run()Server{
+  return sb.Server
+}
+

functional Options --- 功能选项模式

使用场景:在一个配置中心,我们并不想把配置的 struct 暴漏出去,那么我们可以将这个 struct 定义为非导出类型,然后我们定义一个可导出的函数类型,将这个 struct 设置为函数的参数,使用这个可导出的函数来完成配置的操作。

首先是定义一个函数类型

type Option func(*server)
+
+type server struct {
+  addr string
+  port int
+  protocol string
+}
+

我们使用函数式的方式去定义一组函数


+func WithPort(port int) Options {
+  return func(s *server) {
+    s.port = port
+  }
+}
+
+func WithProtocol(protocol string) Options {
+  retrun func(s *server) {
+    s.protocol = protocol
+  }
+}
+
+func NewServer(addr string, options ...Option) *server {
+  serv := server{
+    addr,
+    "8080",
+    "tcp",
+  }
+  for _, opt := range options {
+    opt(&serv)
+  }
+  // 接下来的处理
+}
+
import(
+  "xx/example"
+)
+func main(){
+  example.NewServer("bj",example.WithPort("8080"),example.WithProtocol("tcp"))
+}
+

如你所见,使用了函数作为返回值,函数作为参数,变长函数以及闭包等知识,去完成了 “functional options” 这种函数式编程的模式。 +在这个场景下,我们扩展配置变得非常容易,并不需要更改现有的代码,并且也防止了配置 struct 的外漏。

这里还有关于函数式编程其它相关内容:

这里的主要内容来自酷壳 https://coolshell.cn/?s=GO+编程模式 (R.I.P 耗子叔)

issues

问题一: 关于方法的一道题:判断输出

func main() {
+	var a = []*Student{
+		{"一"},
+		{"二"},
+		{"三"},
+	}
+
+	for _, v := range a {
+		go v.pName()
+	}
+
+	var b = []Student{
+		{"四"},
+		{"五"},
+		{"六"},
+	}
+	for _, v := range b {
+		go v.pName()
+	}
+	time.Sleep(time.Second)
+}
+
+type Student struct {
+	name string
+}
+
+func (s *Student) pName() {
+	fmt.Println(s.name)
+}
+

答案是:

三
+六
+六
+一
+二
+六
+

回答:

可以看到,我们期望的一二三四五六并没有输出,这里不考虑顺序,那么四和五为什么没有输出呢?这个时候我们应该考虑方法的本质。

首先我们定了一个在指针类型上的方法 pName,所以第一个 for 循环中,实际上的运行是,每一个指针类型,然后定义在他们上面的方法,并且输出,但是第二个他们是值类型,go 的编译器自动给引出了指针类型,所以说按照指针的实质

func pName(s *Student) {
+  
+}
+

这里放置的就是 &v,还有个大的原因,就是整个 for 循环比开辟一个新的 goroutine 并且运行完毕远远的快,所以当三个 goroutine 开辟完成的时候,所引用的&v 就是同一个数据了。所以这个时候输出的就是同样的最后的数据值,也就是 “六”,不过这里如果 for 的每一次循环事件都很长,那么 goroutine 运行将会输出 “四五六”。

如果想正常的输出,可以把定义在指针上的方法,改成定义在值上的方法即可。

问题二: panic(nil) 时,defer 函数中的 recover() == nil 成立吗

package main
+
+import (
+	"fmt"
+	"reflect"
+)
+
+func main() {
+
+	defer func() {
+		a := recover()
+		fmt.Println(a == nil)
+
+	}()
+	panic(nil)
+}
+
+

答案是:

当 go1.21+ 的时候,不成立,在小于 1.21 的版本中是成立的。

在 1.21 以下,panic 中的 nil 是会传递给 recover() 函数的,所以必定是 true,但是在 1.21+的版本中,panic(nil) 在编译时,底层修改为了 panic(new(runtime.PanicNilError))

所以说,nil 是不等于 *runtime.PanicNilError 的,综上所述,小于 1.21 的版本成立,大于 1.21 的版本不成立。

参考资料

  • https://book.douban.com/subject/35720728/ 170 页 - 243 页
  • https://coolshell.cn/?s=GO+编程模式
  • https://github.com/golang/go/blob/06264b740e3bfe619f5e90359d8f0d521bd47806/src/database/sql/sql.go#L813
  • https://github.com/lib/pq/blob/922c00e176fb3960d912dc2c7f67ea2cf18d27b0/conn.go#L60
+ + + diff --git "a/\345\237\272\347\241\200/\345\217\230\351\207\217\345\243\260\346\230\216/index.html" "b/\345\237\272\347\241\200/\345\217\230\351\207\217\345\243\260\346\230\216/index.html" new file mode 100644 index 000000000..48a72cecd --- /dev/null +++ "b/\345\237\272\347\241\200/\345\217\230\351\207\217\345\243\260\346\230\216/index.html" @@ -0,0 +1,145 @@ + + + + + + 变量声明 | GOFamily - go 程序员宝典 + + + + + + + + +

变量声明

变量声明符号 var:=

在 go 语言中,我们使用 var 来表示声明一个变量

var a string = "hello world"
+

这就是一个标准的变量声明方式,var 符号在最前面,接着就是变量 a,变量后面紧跟着是变量的类型,这里是 string 类型,也就是字符串类型,= 后面是要赋值的具体值

我们也可以直接声明不赋予初始值,go 语言默认,声明即赋予初始值,那么这里的字符串类型初始值就是一个空字符串 ""

var a string
+

go 语言具有类型推断能力,所以我们可以省略类型,让 go 语言的编译器去推断类型

var a  = "hello world"
+

编译器会自动推断 a 为 string 类型

在函数体内部 (这是一个先决条件) 我们可以使用省略的写法,就是使用一个符号 := 来充当 var 的角色,也就是初始化的工作,比如说

func main(){
+  a := "hello world"
+}
+

可以看到,整个的用法是 [变量] [:=] [初始值] 这三者缺一不可,而且还不能多,不能在 a 后面带有类型,不能省略初始值,且仅限于函数/方法内部使用

go 语言支持多变量同时赋值

var a, b string
+
+var c, d string = "1", "2"
+
+a, b := "hello world",12
+

其中,使用 var 进行声明的时候,如果是多个变量同时声明,必须是相同类型;使用 := 进行多变量赋值时,多个变量可以不同类型,因为全靠编译器推断

常见的变量声明方式

从广义上来说,go 语言只有两种变量,包一级的变量和函数一级的变量

var DefaultValue int
+
+func NewMethod(n int){
+
+}
+

其中 DefaultValue 就是包级变量,n 就是函数级变量 (也是形式变量)

下面我列举一些常见的声明方式

var (
+ a string
+ b int32 = 1
+)
+
+var c map[int]string //包级变量
+
+func hi(){
+  d := 12 //仅限函数内部使用,变量后面不能有类型
+  var c map[int]string // 函数级变量
+  var e = "hello world" //自动推断变量类型
+}
+
+

可以说声明的方式很多,不过呢,在一个项目中应该尽量保证声明方式的一致性,因为可以加强代码的统一性,减少理解代码的难度。

go 语言可导出变量

go 语言跟一般的语言不同,它使用变量字母的大小写来区分变量的可导出性质,大写 (如果使用中文作为变量名称,默认是可导出的) 代表可导出,小写代表仅限包内部使用 (包这一级,多个文件只要是同一个包就可以使用)

package Example
+
+var OutPutName = 0o77
+var inName = 0x99
+

其中 OutPutName 是一个可导出的变量,inName 是不可导出变量

go 语言还拥有比如函数,方法,结构体,接口,等等各类型的组件,这里你可以先不懂到底是什么,你先有一个印象,但凡首字母是大写的都是可以导出的,小写就是包内使用,没错,go 语言就是这么简单

包级变量

第一种声明形式声明的同时,显式初始化

  • var a = method("t") go 编译器根据右侧的返回值自动确定左侧变量的类型
  • var a = 3.14 在没有具体返回值,没有具体类型的情况下,go 赋予它默认类型,比如 float 的默认类型就是 float64,int 类型的默认类型就是 int
  • 如果要显式的赋予类型,并且保证命名的一致性 var a int = 12 的行为应该避免,应该写成 var a = int(12) 用来保证一致性
    var(
    +  a = 3.14
    +  b = int(12)
    +  e = errors.New("EOF")
    +)
    +

第二种声明形式声明但是延迟初始化

只有 var a int64 这一种方式,不过呢,go 语言的声明是直接赋予零值的,比如说这里的 a 默认就是 0

go 语言变量声明的聚集和就近原则:将同一类型的放在一个 var() 内部;或者另一种分类方法:将有初始值的放在一个 var 里,将延迟初始化的放在另一个 var 里。

分类一

var(
+a int
+b int
+c int
+)
+var(
+d string
+e string
+f string
+)
+

分类二

var(
+  a = 12
+  b = "string"
+  c = "string"
+)
+var(
+  d int
+  c string
+  f bool
+)
+

接下来谈一谈就近原则,变量的声明和变量的使用尽量的近,不要都声明在头部,如果一个变量被全局大量使用,那么可以放在头部,如果就是仅仅使用少量的次数,还是应该在使用的前面就进进行声明

函数级变量

第一种声明形式延迟初始化

在函数体内使用 var

func Method(){
+  var a int
+}
+

如果变量特别多,也可以使用 var() 的方法在函数内部使用

第二种声明形式声明且显式初始化的局部变量

func method1(){
+  a := 12
+  b := int32(20) // 改变默认类型
+}
+

小心 shadow 的变量

我们知道,当有两个两个以上的变量在赋值时,如果其中有一个未被提前声明,那么就需要使用 :=,这个时候系统会自动判断有哪些未提前声明,然而有一种场景下系统会发生误判,准确的来说这是一种歧义,系统的判断会跟程序员的心理不一致,出现了变量 shadow 的行为,让我们看一下代码:

func WithName(){
+  var a string
+  // tracing 为 bool 类型
+  if tracing{
+    a,err := example.Method()
+  } else {
+    a,err := example.Method1()
+  } 
+}
+

在外层,a 已经提前声明,但是在 if 这个作用域中,由于 err 并未提前声明,所以使用了 :=,由于系统无法获知这里的 a 是否需要再次声明,所以 go 语言默认 a 是一个新的变量,这样外层的 a 就无法得到新的值,外层 a 也就被内层的 a 给 shadow 了。

如果想改变这种 bug,我们可以将 err 也提前声明:

func WithName(){
+  var a string
+  var err error
+  // tracing 为 bool 类型
+  if tracing{
+    a,err = example.Method()
+  } else {
+    a,err = example.Method1()
+  } 
+}
+

或者也可以改变内部的变量名称,来改变这种 shadow:

func WithName(){
+  var a string
+  // tracing 为 bool 类型
+  if tracing{
+    ai,err := example.Method()
+    a = ai
+  } else {
+    ai,err := example.Method1()
+    a = ai
+  } 
+}
+

参考资料

  • https://juejin.cn/post/7241452578125824061
+ + + diff --git "a/\345\237\272\347\241\200/\345\244\215\345\220\210\345\255\227\351\235\242\351\207\217/index.html" "b/\345\237\272\347\241\200/\345\244\215\345\220\210\345\255\227\351\235\242\351\207\217/index.html" new file mode 100644 index 000000000..5f23e49aa --- /dev/null +++ "b/\345\237\272\347\241\200/\345\244\215\345\220\210\345\255\227\351\235\242\351\207\217/index.html" @@ -0,0 +1,94 @@ + + + + + + 复合字面量作为构造器 | GOFamily - go 程序员宝典 + + + + + + + + +

复合字面量作为构造器

go 的复杂类型经常采用复合字面量的方式进行初始化,例如 struct,数组,slice map,比如:

a := Some{
+  name:"hi there",
+  year:12,
+  }
+arr := [...]int{1,2,3,4}
+
+sl := []int{1,2,3}
+
+m  := map[int]int{
+  1:1,
+  2:2,
+}
+

当 struct 需要使用 {key :value} 来构建时,即使可以按照顺序省略掉 key,也千万不要这么做,加上 key 就可以将结构体的使用和设计解耦,可以乱序进行 struct 的赋值,即使某些字段没有赋值,那么系统也会自动的赋予它的初始值。

当获取 struct 的指针时,最好是在字面量上取 &,比取变量的指针地址更加符合具体的含义,即:获取结构体的指针类型并且复制给一个变量。比如 a := &Some{} 就比 a := Some{} , &a 更好,另外当我们要获取一个结构体的初始值的时候使用 a := Some{}a = new(Some) 更加常用。

数组和切片的复合体跟 struct 不同,它使用下标来作为 key,它们几乎不会使用 key:value 的形式,不过下面两种情况还是会出现的:

  • 为了省略中间元素
  • 为了显著的体现下标
//为了省略中间元素
+func main() {
+	a := [...]int{12, 4: 3}
+  b := [...]int{'a':0,'b':1}// 这里 '' 代表了 rune类型
+	fmt.Println(a,b)
+}
+
//为了显著的体现下标
+var data = []int{ 0:-10, 1:-5, 2:0, 3:1, 4:2, 5:3 }
+

当 slice 和 array 含有复合类型的时候,可以直接省略复合类型的类型名称,直接用 {} 即可

package main
+
+import "fmt"
+
+func main() {
+	a := []Some{
+		{
+			name: "12", // 这里尽量不要省略字段名称,方便解除跟结构体设计时的耦合
+			year: 12,
+		},
+		{
+			name: "12",
+			year: 12,
+		},
+	}
+	fmt.Println(a)
+
+}
+
+type Some struct {
+	name string
+	year int
+	p    bool
+}
+

map 字面量作为初始值的时候,非常自然的就会使用 key-value 的方式,需要注意两点

  • 当 value 或者 key 是复合类型的时候 (key 必须是可比较的) 可以省略复合类型的类型名称
  • 如果是指针类型,那么连指针也可以省略
+ + + diff --git "a/\345\237\272\347\241\200/\345\270\270\351\207\217\345\243\260\346\230\216/index.html" "b/\345\237\272\347\241\200/\345\270\270\351\207\217\345\243\260\346\230\216/index.html" new file mode 100644 index 000000000..33dd6608e --- /dev/null +++ "b/\345\237\272\347\241\200/\345\270\270\351\207\217\345\243\260\346\230\216/index.html" @@ -0,0 +1,146 @@ + + + + + + go 语言常量 | GOFamily - go 程序员宝典 + + + + + + + + +

go 语言常量

go 使用 const 来声明常量,常量表达式的运算在编译期就可以完成了,并且 go 常量是类型安全的,编译器优化友好的元素。

常量中的数据类型只可以是布尔、数字 (整数、浮点和复数) 以及字符串型。

const(
+  startOne = -2
+  startTwo = 1.0
+  startThree = 3.14
+  isTrue = true
+  hi = "hi"
+)
+

go 推崇无类型常量,这主要是因为 go 不支持隐式的类型转化,只能显式转换,所以一旦常量给定类型,那么它跟变量之间或许就要将常量转化类型才能进行运算。

package main
+
+import "fmt"
+
+const (
+	PI = 3.14
+)
+
+func main() {	
+	var a float32 = PI
+	var b float64 = PI
+}
+
+

可以看到,如果是没有类型的常量,可以非常灵活的赋予 float32 或者 float64 都可以。

局部常量

在 Go 中,局部常量在编译期常量折叠 (compile-time constant folding) 时会被编译器处理成字面值,因此局部常量在编译时会被编译。

例如,下面的代码定义了一个局部常量 x,并使用它来初始化一个变量 y:

func main() {
+    const x = 42
+    var y = x * 2
+    fmt.Println(y)
+}
+

在编译时,x 将被处理成字面值 42,因此编译器将 y 初始化为 84,如下所示:

func main() {
+    var y = 84
+    fmt.Println(y)
+}
+

可见,局部常量在编译期已经被折叠成了字面值,因此在运行时不会再进行计算了。

常量不可包含计算

在 Go 中,常量必须在编译时就可以确定其值。因此,常量的值必须是一个编译时的常量表达式,不能包含运行时的计算。

不过,常量表达式可以包含一些简单的运算,例如加、减、乘、除、取模等算术运算,以及位运算、逻辑运算等。例如:

const (
+    x = 2 + 3            // 常量表达式,结果为 5
+    y = (x * 2) % 10     // 常量表达式,结果为 0
+    z = x == 5 || y == 0 // 常量表达式,结果为 true
+)
+

常量表达式还可以使用一些内置函数,例如 lencapmakenew 等,以及可以在编译期计算的一些标准库函数,例如 math.Sinmath.Pi 等。

总之,常量必须在编译期就可以确定其值,因此不能包含运行时的计算,但可以包含一些简单的算术、位运算、逻辑运算等。

枚举常量 iota

go 语言提供隐式重复前一个非空表达式的机制

package main
+
+import "fmt"
+
+const (
+	PI = 3.14
+	a
+	b
+	c
+	d
+	e
+	f
+)
+
+func main() {
+	fmt.Println(PI, a, b, c, d, e, f)
+}
+

这段代码将会全部输出 3.14

iota 是 go 语言的一个预定义标识符,它表示 const 声明块中,每一个常量所处位置的偏移量,它本身也是一个无类型的常量,它的初始值是 0,意思是说此处的 iota 跟第一行比偏移了 0 个位置

const(
+  a = 1 << iota
+  b
+  c
+  d
+  e
+  f
+)
+

同一行的常量 iota 值是一样的,可以理解为偏移量相同

const(
+  a,b = iota
+  c,d
+)
+

如果要让 iota 的初始值是 1,那么可以这么做

const(
+  _ = iota
+  a
+  b
+  c
+  d
+)
+

iota 只需要在一个 const 群中出现一次即可,出现多次跟出现一次的效果一样

const(
+  a = iota
+  b = iota
+  c = iota
+)
+// 一样
+const(
+  a = iota
+  b
+  c
+)
+

当 iota 群中出现异类该如何处理

const(
+  a = iota
+  b = 12
+  c = iota
+  d
+  e
+  f
+  g
+)
+

答案就是异类输出自己的,其它的常量不受影响,比如这里的输出就是 0 12 2 3 4 5 6,只要记住 iota 是偏移位置就可以理解为什么是这么输出的了。

如果不考虑常量的灵活性,极致追求安全性,那么也可以给 iota 常量加上类型

const(
+  a int = iota
+  b
+  c
+  d
+)
+

这种就要求变量类型必须是 int 才能被这些枚举类型接纳,但是一般常量还是用无类型的较为常见。

+ + + diff --git "a/\345\237\272\347\241\200/\346\225\260\345\255\227\347\261\273\345\236\213/index.html" "b/\345\237\272\347\241\200/\346\225\260\345\255\227\347\261\273\345\236\213/index.html" new file mode 100644 index 000000000..9bee658c2 --- /dev/null +++ "b/\345\237\272\347\241\200/\346\225\260\345\255\227\347\261\273\345\236\213/index.html" @@ -0,0 +1,119 @@ + + + + + + go 数字类型 | GOFamily - go 程序员宝典 + + + + + + + + +

go 数字类型

导读:

  • int
  • float
  • complex
  • issues

int

int 是 go 语言的整数类型,一共分为下面这么几类

类型 描述
int 有符号的整数类型,具体占几个字节要看操作系统的分配,不过至少分配给32位
uint 非负整数类型,具体占几个字节要看操作系统的分配,不过至少分配给32位
int8 有符号的整数类型,占8位bit,1个字节。范围从负的2的7次方到正的2的7次方减1
int16 有符号的整数类型,占16位bit,2个字节。范围从负的2的15次方到正的2的15次方减1
int32 有符号的整数类型,占32位bit,4个字节。范围从负的2的31次方到正的2的31次方减1
int64 有符号的整数类型,占64位bit,8个字节。范围从负的2的63次方到正的2的63次方减1
uint8 无符号的正整数类型,占8位,从0到2的8次方减1.也就是0到255
uint16 无符号的正整数类型,占16位,从0到2的16次方减1
uint32 无符号的正整数类型,占32位,从0到2的32次方减1
uint64 无符号的正整数类型,占64位,从0到2的64次方减1
uintptr 无符号整数类型。它大到足以容纳任何指针
rune int32的别名,代表一个 UTF-8 字符
byte uint8别名,代表了ASCII 码的一个字符

go 使用 '\x12' 或者使用 0x12 来表示 16 进制

go 使用 '\012' 或者使用 012 来表示 8 进制

go 不能直接显示 2 进制,使用 fmt.Printf("%b",12) 1000 来输出一个二进制

float

类型 描述
float32 浮点型,包括正负小数,IEEE-754 32位的集合,提供大约 6 个十进制数的精度,math.MaxFloat32 表示 float32 能取到的最大数值,math.SmallestNonzeroFloat32表示最小值
float64 浮点型,包括正负小数,IEEE-754 64位的集合,提供约 15 个十进制数的精度,math.MaxFloat64 表示 float64 能取到的最大数值,math.SmallestNonzeroFloat64表示最小值

浮点数使用 fmt.Printf("%.4f\n", math.Pi)%.nf 来控制保留几位小数

我们知道根据 IEEE 754 标准,使用有限个位置的二进制数字去代表无限个数字,那么必然是不精确的,是近似的。

由于浮点数计算的时候并不精确,容易发生较大误差,所以我们可以使用现有的高精度第三方库进行替换:https://github.com/shopspring/decimal

基础浮点数,math/big 包,decimal 三方包的对比

让我们看一下著名的 0.1 + 0.2 != 0.3 这个问题

package main
+
+import "fmt"
+
+func main() {
+	var a = 0.1
+	var b = 0.2
+	
+	// false
+	fmt.Println(a+b == 0.3)
+}
+

基础浮点数 IEEE 754 详细介绍

这是一个 IEEE 754 的内部存储示意图,这个标准下,数字使用二进制的方式进行存储:

IEEE 754

sign exponent fraction
代表正负的符号位 指数位 尾数
  • sign:(-1)^sign,所以当 sign 等于 0 时,就是正数,当 sign 等于 1 时就是负数

  • exponent:指数,也就是 2 的指数,在计算的时候,我们要减去一个偏移量 (127 或者 1023,这取决于 32 位还是 64 位),在 32 位的情况下,0 - 255,然后减去偏移量 127,结果是:[-126,127] (正常情况下,不算上 00000000 和 11111111 这两种情况)

  • fraction:尾数,表示为 1.fraction 或者 0。fraction 的样子,后者通常表示的极小浮点数

例如,图示的 0.15625,使用 IEEE 754 来表示:

(-1)^0 * 2^1111100 * 1.01
+
+= 2^(124 - 127) * 1.01
+
+2^-3 * 1.01
+
+= 0.00101(二进制)
+= 0.15625 (十进制)
+

可以看到,一个十进制的 0.15625 要转化为一个 2 进制的信息进行保存,那么问题来了,为什么浮点数的 0.1 不等于整数的 0.1 呢?

10 进制小数转化为 2 进制:使用十进制的小数部分乘以 2,然后取整数部分,然后再把这个数的小数部分再次乘以 2... 直到小数部分为零结束;十进制的整数部分就按照普通的除法,每次都除以 2,然后取余数,直到商为 0:

举一个例子:十进制 8.35;

整数部分是 8,8 /2 = 4 余 0,4/2 = 2 余 0,2/2 = 1 余 0,1/2=0 余 1,那么根据倒序排列来得出结果就是 1000;

小数部分是 0.35,0.35 2 = 0.7 整数部分是 0,0.7 2 = 1.4 整数部分是 1,0.4 2 = 0.8 整数部分是 0,0.8 2 = 1.6 整数部分是 1,0.6 2 = 1.2 整数部分是 1,0.2 2 = 0.4 整数部分是 0,0.4 2 = 0.8 整数部分是 0,0.82 = 1.6 ... 那么小数部分就是 0.01011001... 那么在满足某个精度的情况下,它的值用二进制表示就是 1000.01011001;

将它还原也很简单,整数部分按照 2 的指数,小数部分按照 2 的负指数:1*2^3 + 2^-2 + 2^-4 + 2^-5 = 8.34375

第一步,我们需要将 0.1 转化为二进制,那么等于多少呢?

答案是:0.0001100110011...

看到了吧,除不尽啊,那么我们取它的尾数 1.1001100110011001100 * 2^-4

如果填入他们的图就是

  • sign 为 0
  • 指数的十进制为 -4 + 127 = 123,也就是 1111011 填入上面的内容就是 01111011
  • 尾数是 100110011001100

所以你会发现从这里的存储再转化为 10 进制的结果就是

2^-4 * 1.1001100110011
+
+= 0.00011001100110011
+
+= 0.0999984741210937
+

总结:由于某些数字,从 10 进制转化为 2 进制的时候,无法得到精确值,除不尽,所以,浮点数存储数字,有些数字不能精确存储

math/big 包详细介绍

big 拥有下面三个类型

  • Int
  • Float
  • Rat (有理数)

big 包提供的 big.Int 底层数据是一个切片,所以说它可以保存超过 uint64 的数据;big.Rat 的底层数据是,分子和分母分别都是一个 big.Int 类型,所以说它也可以保存超过 uint64 的数据。

big.Float 类型是 Go 语言中提供的一个高精度的浮点数计算库,它使用了任意精度的浮点数表示,可以处理大数和小数,并提供了一系列的计算方法

虽然它是高精度的浮点数计算,但是仍然不够精确,如果是绝对的精确的浮点数计算,通常要使用有理数,或者使用整数去取代真正的小数,下面这个第三方包就是使用整数去取代浮点数的方式来获取精确的浮点数运行结果。

decimal

这个第三方包底层使用 big.Int 进行处理数据,也就是说,它使用整数存储,计算然后得出结论的时候再把之前的指数还原即可,所以它可以进行精确的浮点数运算

https://github.com/shopspring/decimal

这个包中,0.1 + 0.2 就等于 0.3 了

func main() {
+	// 创建一个新的Float类型
+	a := decimal.NewFromFloat(0.1)
+	b := decimal.NewFromFloat(0.2)
+	c := decimal.NewFromFloat(0.3)
+	value := a.Add(b)
+	// 0
+	fmt.Println(value.Cmp(c))
+}
+

complex 复数

类型 描述
complex64 实部和虚部是 float32
complex128 实部和虚部都是 float64

c := complex(3, 4) 创建一个复数,实部为 3,虚部为 4

或者还可以这样:c := 3 + 4i

使用 go 语言内置的函数 real()imag() 来分别获取到复数的实部和虚部

issues

***问题一:***go 语言数字之间的类型转化是如何进行的

go 语言的类型转换是显性转化的,数字类型之间可以使用这种方法

var i int
+f := float64(i)
+

***问题二:***能说说 uintptrunsafe.Pointer 的区别吗

  • unsafe.Pointer 是通用指针类型,它不能参与计算
  • uintptr 是指针运算的工具,但是它不能持有指针对象 (意思就是它跟指针对象不能互相转换)
  • unsafe.Pointeruintptr 可以相互转换,unsafe.Pointer 和指针对象可以相互转换,但是 uintptr 和指针对象不能相互转换
  • unsafe.Pointer 是指针对象进行运算 (也就是 uintptr) 的桥梁
package main
+
+import (
+	"fmt"
+	"unsafe"
+)
+
+func main() {
+	// 指针对象
+	v := new(int)
+	// 将指针对象转化为通用指针类型
+	vp := unsafe.Pointer(v)
+	// 将通用指针类型转换为指针对象
+	vo := (*int)(vp)
+
+	//将通用指针对象转化为uintptr
+	uv := uintptr(vp)
+	//将uintptr转换为通用指针对象
+	vpp := unsafe.Pointer(uv)
+	fmt.Println(v, vp, vo, uv, vpp)
+
+	// 对指针对象的地址进行计算
+	t := new(string)
+	// 首先先将t转化为unsafe.Pointer类型
+	pt := unsafe.Pointer(t)
+	// 然后将pointer再转化为 uintptr
+	rt := uintptr(pt)
+	//进行计算
+	npt := rt + uintptr(1)
+  // 计算完毕后,再将uintptr转化为unsafe.Pointer再转换为*string类型
+	nt := (*string)(unsafe.Pointer(npt))
+	fmt.Println(t, pt, rt, npt, nt)
+}
+
+

***问题三:***rune 和 byte 的区别

rune 是 int32,byte 是 uint8,相比 byte 来说,rune 可以容纳的字符个数要多很多,所以 utf8 编码的字符使用 rune,而 ascii 使用 byte,例如 ‘中’ byte 无法承受,“中” 转为 []byte 的时候是 [228 184 173],“中” 转换为 []rune 则是等于 [20013] 因为 rune 可承受的数值更大,并且一个 utf8 的字符就等于一个 rune 的数值,如果是使用 ‘中’ 那默认就是 rune 类型

unicode 是一种字符编码,让每个字符和一个数字对应起来,仅此而已,至于这个数字如何存储它就不管了。utf8 就是定义了如何具体存储这个编码数字的一种方法

参考资料

  • https://zhuanlan.zhihu.com/p/145220416
  • http://www.manongjc.com/article/50416.html
  • http://c.biancheng.net/view/18.html
  • https://zhuanlan.zhihu.com/p/353013671
  • https://developer.aliyun.com/article/673258
+ + + diff --git "a/\345\237\272\347\241\200/\346\261\202\345\200\274\351\241\272\345\272\217/index.html" "b/\345\237\272\347\241\200/\346\261\202\345\200\274\351\241\272\345\272\217/index.html" new file mode 100644 index 000000000..13733aabc --- /dev/null +++ "b/\345\237\272\347\241\200/\346\261\202\345\200\274\351\241\272\345\272\217/index.html" @@ -0,0 +1,74 @@ + + + + + + 求值顺序 | GOFamily - go 程序员宝典 + + + + + + + + +

求值顺序

包级变量声明语句中的表达式求值顺序

  • 按照变量的声明顺序,从上到下,从左到右,进行求值
  • 如果 a 求值时有依赖项 b,或者是间接的依赖项 b,那么先求值 b
  • 在求值的过程中会一直对于变量是否拥有依赖项进行查找,直到查询到没有依赖项的变量将其求值,然后重复这个查找,最后全部求值。
  • 同一个包不同文件的处理,原则上如果 a 文件在 b 文件前面,那么 a 文件中的所有变量求值比 b 文件中的更早,除非 a 文件变量依赖了 b 中的变量。
var (
+  a = c + b
+  b = f()
+  _ = f()
+  c = f()
+  d = 3
+)
+func f() int {
+  d ++ 
+  return d
+}
+
  1. 查找没有依赖项的变量将其求值,这一轮中是 d,此时只有 [d=3]
  2. 查找没有依赖项的变量进行求值,这一轮中 a 还是不符合,但是 b 符合了,所以现在是 [b=4,d=4]
  3. 查找没有依赖项的变量进行求值,这一轮中是 _,此时 [b=4,d=5]
  4. 查找没有依赖项的变量进行求值,这一轮中是 c,此时 [b=4,c=6,d=6]
  5. 查找没有依赖项的变量进行求值,这一轮中是 a,此时 [a=10,b=4,c=6,d=6]

输出 10 4 6 6

普通求值顺序

这包括了,函数,方法,channel 中的求值顺序。

  • 规定是从左到右

y[f()],ok = g(h(),i()+x[j()], <-c),k()

这个语句的求值顺序就是 f() h() i() j() x[] <-c g() k()

普通值求值顺序和包级变量求值依赖顺序一起使用的时候,包级变量优先级更高,并且它在导入包的时候就已经求值了,而普通的求值顺序只有调用的时候才会求值。

赋值语句中的求值顺序

赋值语句求值有两个阶段 --- “先算后赋”

  • 对等号左边的下标表达式,指针解引用表达式,以及等号右边的表达式,从左到右的依次求值
  • 按照从左到右的顺序将变量进行赋值

例如

n0 := 1
+n1 := 2
+n0,n1 = n0+n1,n0
+
  1. 左边没有要处理的表达式,右侧有,那么第一个位置就是 n0+n1,因为 n0 和 n1 已经存在了初始化的内容了,所以这里直接就是 3,no = 1,这时 n0 和 n1 还是 1 2,因为还没有赋值呢。
  2. 开始赋值:右侧一个是 3 一个是 1,那么最新的 no 和 n1 就是 3 1

switch select 中的表达式的求值顺序

这里主要想说一下惰性求值:就是只有需求的时候才会进行求值

func f(n int)int{
+  return n
+}
+
+func main(){
+  switch f(2) {
+    case f(1),f(2),f(3):
+      fmt.Println("--")
+  }
+}
+

首先先求值的是 switch 后面的 f (2)

然后对 f(1) f(2) 求值;f(3) 不会求值,因为 f (2) 已经满足了要求。

在 select case 中,如果 case 中存在表达式,最开始会依次计算所有的表达式,只有一种除外,收 case 中,位于左侧的表达式,它会在接受数据之前才会计算,然后赋值。

参考资料

  • https://go.dev/ref/spec#Order_of_evaluation
  • https://book.douban.com/subject/35720728/ 132 页 - 142 页
+ + + diff --git "a/\345\237\272\347\241\200/\346\263\233\345\236\213/index.html" "b/\345\237\272\347\241\200/\346\263\233\345\236\213/index.html" new file mode 100644 index 000000000..825dc32e3 --- /dev/null +++ "b/\345\237\272\347\241\200/\346\263\233\345\236\213/index.html" @@ -0,0 +1,453 @@ + + + + + + 泛型 | GOFamily - go 程序员宝典 + + + + + + + + +

泛型

导读:

  • 约束
  • 使用方法
  • 实现原理
  • 跟其它语言的泛型进行对比
  • 用例子学泛型
  • issues

泛型需满足 go1.18+

约束

go 使用 interface 作为约束,约束的意思是约束了这个泛型都具有哪些实际类型。所以可以理解为,go 将 interface 的职责给扩展了,让接口不仅仅作为接口 --- 解耦的,抽象化的结构体,还具有了约束,对于类型的约束作用。

go 可以将所有的接口 (包括经典接口,和泛型以后的接口) 都用作约束,但是可以不代表应该,要有选择的去使用约束,但是约束并不是都可以作为传统的接口来使用的,例如传统的接口只能存在方法,并不能存在类型,只要存在类型就自动归纳为约束

综上所述,第一,约束的概念大于接口,只有传统接口可以作为抽象类型去使用,约束只能存在于函数方法或者类型之中,不能单独使用。

func main() {
+	var a1 a
+}
+
+type a interface{
+	int|float64
+	get()
+}
+
# 不能在类型约束之外使用类型 a:接口包含类型约束
+cannot use type a outside a type constraint: interface contains type constraints
+
+

第二,不要把传统接口用在约束的地方,这种用法是不合适的,通常来说传统接口的模式就用作抽象类型的解耦即可 (用了也不错,只是不建议)。

// 不建议
+type Writer interface {
+	hi()
+}
+
+func Get[T Writer](v T) {
+	v.hi()
+}
+

泛型是类型,约束是类型的约束

请认识到,泛型是一种类型,约束是一种类型的约束,请不要把约束当作泛型。

type st interface {
+  int | string
+}
+

这里 st 约束拥有 int 和 string,请注意这里的 st 是约束,不是泛型类型

go 内置了很多约束,比如说 any 和 comparable,意思是任何类型和可以比较的类型。以后***应该***会有一个新的内置约束的包叫做 package constraints 例如 any comparable,Ordered 等等约束都会内置到标准库中

约束不仅仅可以单独写出来,还可以内置于函数内部。

func Age[T int| string, B float64| string](i T,j B){}
+

这种形式下,T 和 B 的约束就是仅限此函数使用

下面我们看一种形式,这种情况下约束的不仅仅是 string 和 int,而是包含了底层是他们的所有数据,比如说 type DD int 也符合这个约束,请记住只能使用在底层类型上,如果使用 ~DD 是不被允许的

type st interface{
+	~string | ~int
+}
+

约束的嵌套

约束跟接口是一样的也是可以嵌套的

type ComparableHasher interface {
+	comparable
+	Hash() uintptr
+}
+
+// or
+
+type ImpossibleConstraint interface {
+	comparable
+	[]int
+}
+

这里的意义就是 and 的意思就是说这个约束是可以比较的还是必须得支持 hash()uintptr

下面这种方式也是可以的

type NumericAbs[T any] interface {
+	~int | ~int8 | ~int16 | ~int32 | ~int64 |
+		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
+		~float32 | ~float64 |
+		~complex64 | ~complex128
+	Abs() T
+}
+

上面的类型意思是满足数字类型,下面的意思是满足这个方法,所以最终实现这个约束的对象就是一个数字类型,并且实现了这个接口的 Abs()T 方法。

泛型

当结构体中使用泛型的时候,泛型不能直接作为嵌入使用

type Lockable[T any] struct {
+	T // 正确的方法应该是 t T ; 将 T 作为类型参数,不可直接嵌入
+	mu sync.Mutex
+}
+
+

错误提示:embedded field type cannot be a (pointer to a) type parameter嵌入式字段类型不能是(指向)类型参数

我们再看一下当泛型结构体嵌入到其它结构体中如何使用

type A[T any] struct {
+	T T
+}
+
+type B[T any] struct {
+	// 这里使用 A[T] 输入了实际类型,
+	A[T]
+	T T
+}
+

可以看出关键点,泛型结构体被嵌入其它结构体的时候,泛型要给实际的类型才可以

结构体泛型和方法中的泛型做对比:

type A[T any] struct{}
+
+func(a A[T])Get(){}
+

方法里的泛型是可以直接从 struct 定义的地方继承这个泛型 T 的,当这个结构体使用时,指定实际的类型即可。

func main() {
+	var d1 d
+	var a  = A[d]{
+		d1,
+	}
+	a.Get()
+}
+// 只要没有类型就不是约束,是接口,在go语言中,接口可以充当一个一般类型
+type d interface {
+	get()
+}
+
+type A[T any] struct {
+	T T
+}
+
+func (a A[T]) Get() {
+	fmt.Println(a)
+}
+

约束里的泛型同样不能直接嵌入使用

// ❌
+type B[T any] interface {
+	T
+}
+

错误提示:cannot embed a type parameter

泛型只能充当类型:

type EmbeddedParameter[T any] interface {
+	~int | ~uint 
+	me() T 
+

使用约束中的泛型还是需要注意一下的,稍微有些复杂:

func Abs[T EmbeddedParameter[T]](t T)T{}
+

解释一下,中括号里面泛型的两个 T 表达的意思是不一样的,后面的 T 表达的是约束里的泛型,表示 any,前面的 T 表示的是满足后面的这个约束的类型 T,但是这里注意,后面 T 虽然之前定义的时候是 any 但是这里被赋予为 T 之后,改变了,改变为了必须满足约束 EmbeddedParameter 的类型,如果说的通俗点,从 any 变成了,满足 int | uint and 实现 me()T方法 后文会有代码进行解释。

当然了,后面的 T 没有也行,如果没有后面的 T 就是相当于不改变后面的 T 的约束类型了

type Differ[T1 any] interface {
+	Diff(T1) int
+}
+
+func IsClose[T2 Differ](a, b T2) bool {
+	return a.Diff(b) < 2
+}
+
+

当使用了泛型之后,是无法使用断言的,这是非法的,那么如果一定要在运行时的时候去判断类型怎么办呢?答案就是转变成 any (type any = interface {}) 即可,因为我们知道任何对象都已经实现了空接口,那么就可以被空接口去转化

func GeneralAbsDifference[T Numeric](a, b T) T {
+	switch any(a).(type) {
+	case int, int8, int16, int32, int64,
+		uint, uint8, uint16, uint32, uint64, uintptr,
+		float32, float64:
+		return OrderedAbsDifference(a, b) 
+	case complex64, complex128:
+		return ComplexAbsDifference(a, b) 
+	}
+}
+

下面看一下别名的真实类型是泛型的情况

type A[T any] []T
+
+type AliasA = A // 错误 ❌
+
+type AliasA = A[int] // 正确
+

其中错误的问题是别名不能直接使用泛型类型 cannot use generic type A[T any] without instantiation,它需要泛型实际赋值

使用方法

下面展示一下 go 泛型的基本使用方法

package main
+
+import "fmt"
+
+func main() {
+	fmt.Printf("%v",Age[int](12))
+}
+
+func Age[T any](t T) T{
+	return t
+}
+

这是函数使用泛型的写法,当函数使用泛型的时候,需要在变量前使用中括号标注泛型的具体约束,然后后面才能使用这个泛型类型,使用泛型函数的时候,中括号是可以省略的 Age(12) 系统会自动推算泛型的具体实现。顺便说一下,泛型类型使用 %v 作为占位符,也就是默认的类型,泛型类型无法进行断言。

当然了,我么也可以不用 any,自定义一个约束

package main
+
+import "fmt"
+
+func main() {
+	Age[int](12)
+}
+type st interface{
+  int | string
+}
+func Age[T st](t T) {
+	fmt.Println((t))
+}
+

看完了在函数内的泛型,我们在看一下在方法中如何使用泛型

package main
+
+import "fmt"
+
+func main() {
+	new(Age[int]).Post(12)
+
+	var dd DD[int]
+	dd.TT(12)
+}
+
+type Age[T any] struct {
+	I T
+}
+
+func (a *Age[T]) Post(t T) {
+	fmt.Println(a.I, t)
+}
+
+type DD[T any] []T
+
+func(dd *DD[T])TT(t T){
+	fmt.Println(t,len(*dd))
+}
+

在 age 结构体声明的时候,声明了一个泛型 T,在 struct 体内就可以使用这个 T,方法内部仅可以使用定义在这个结构体对象上的泛型

下面是一个错误案例

// ❌
+func (a *Age[T])Post[B any](t T,b B) {
+	fmt.Println(a.I, t)
+} 
+

syntax error: method must have no type parameters

接下来我们看一下,如何使用有类型也有方法的泛型

package main
+
+import "fmt"
+
+func main() {
+	var d DDD
+	var i DDD
+	d = 1
+	i = 2
+	io := AbsDifference[DDD](d, i)
+	fmt.Println(io)
+}
+
+type DDD int
+
+func (ddd DDD) Abs() DDD {
+	return ddd + ddd
+}
+
+type NumericAbs[T any] interface {
+	~int | ~int8 | ~int16 | ~int32 | ~int64 |
+		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
+		~float32 | ~float64 |
+		~complex64 | ~complex128
+	Abs() T
+}
+
+// AbsDifference computes the absolute value of the difference of
+// a and b, where the absolute value is determined by the Abs method.
+func AbsDifference[T NumericAbs[T]](a, b T) T {
+	d := a - b
+	return d.Abs()
+}
+

实现原理

泛型的第一种方法是在编译这个泛型时,使用一个字典,里面包含了这个泛型函数的全部类型信息,然后当运行时,使用函数实例化的时候从这个字典中取出信息进行实例化即可,这种方法会导致执行性能下降,一个实例化类型 int, x=y 可能通过寄存器复制就可以了,但是泛型必须通过内存了 (因为需要字典进行运行时赋值),不过好处就是不浪费空间

还有一种方法就是把这个泛型的所有类型全部提前生成,这种方法也有一个巨大的缺点就是代码量直线上升,如果是一个包的情况下还能根据具体的函数调用去实现该实现的类型,如果是包输出的的情况下,那么就得不得不生成所有的类型。

所以将两者结合在一起或许是最好的选择。

这种方法是这样的,如果类型的内存分配器/垃圾回收器呈现的方式一致的情况下,只给它生成一份代码,然后给它一个字典来区分不同的具体行为,可以最大限度的平衡速度和体积

跟其它语言的泛型进行对比

  • c 语言:本身不具有泛型,需要程序员去实现一个泛型,实现复杂,但是不增加语言的复杂度 (换言之只增加了程序员的)
  • c++和 rust:跟 go 基本保持一种方式,就是增加编译器的工作量
  • Java:将泛型装箱为 object,在装箱和拆箱擦除类型的过程中,程序执行效率会变低

为什么 Java 编译器不在编译期就完成泛型的装箱操作呢?

主要有以下几个原因:

  1. 编译期装箱需要编译器对代码细节有完整信息。但是泛型类型信息可能来自其他模块或第三方库,编译器难以完整获取。

  2. 即使编译器可以提前装箱,也需要代码里有大量反射样板代码来还原原始泛型类型,违背泛型设计初衷。

  3. 编译期装箱也无法很好处理运行期检查和转换的各种边界情况。

用例子学泛型

理论学习完了,不使用例子进行复习的话会忘的很快的。跟着我看几个例子吧

函数泛型 map-filter-reduce

package main
+
+import (
+	"fmt"
+)
+
+func main() {
+	vM := Map[int]([]int{1, 2, 3, 4, 5}, func(i int) int {
+
+		return i + i
+	})
+	fmt.Printf("map的结果是:%v", vM)
+	vF := Filter[int]([]int{1, 2, 3, 4, 5}, func(t int) bool {
+		if t > 2 {
+			return true
+		}
+		return false
+	})
+	fmt.Printf("filter的结果是:%v", vF)
+	vR := Reduce[Value, *Result]([]Value{
+		{name: "tt", year: 1},
+		{name: "bb", year: 2},
+		{name: "7i", year: 3},
+		{name: "8i", year: 4},
+		{name: "u4i", year: 5},
+		{name: "uei", year: 6},
+		{name: "uwi", year: 7},
+		{name: "uti", year: 8},
+	}, &Result{}, func(r *Result, v Value) *Result {
+		r.value = r.value + v.year
+		return r
+	})
+	fmt.Println("reduce的结果是:", vR.value)
+
+}
+
+// Map:类似于洗菜,进去的菜和出来的菜不一样了所以需要两种种类
+func Map[T1, T2 any](arr []T1, f func(T1) T2) []T2 {
+	result := make([]T2, len(arr))
+	for k, v := range arr {
+		result[k] = f(v)
+	}
+	return result
+}
+
+// Filter:类似于摘菜,进去的菜和出来的菜是一种,不过量减少了
+func Filter[T any](arr []T, f func(T) bool) []T {
+	var result []T
+	for _, v := range arr {
+		if f(v) {
+			result = append(result, v)
+		}
+	}
+	return result
+}
+
+// Reduce:类似于做菜,将菜做成一道料理,所以需要两种类型
+func Reduce[T1, T2 any](arr []T1, zero T2, f func(T2, T1) T2) T2 {
+	result := zero
+	for _, v := range arr {
+		result = f(result, v)
+	}
+	return result
+}
+
+type Value struct {
+	name string
+	year int
+}
+type Result struct {
+	value int
+}
+

map的结果是:[2 4 6 8 10] filter的结果是:[3 4 5] reduce的结果是: 36

例子二:方法上的泛型 sets

package main
+
+import (
+	"fmt"
+)
+
+func main() {
+
+	// 这里 Sets的具体类型和Make的具体类型都是int,所以可以正常赋值
+	var s Sets[int] = Make[int]()
+	//
+	s.Add(1)
+	s.Add(2)
+	fmt.Println(s)
+	fmt.Println(s.Contains(3))
+	fmt.Println(s.Len())
+	s.Iterate(func(i int) {
+		fmt.Println(i)
+	})
+	fmt.Println(s)
+	s.Delete(2)
+	fmt.Println(s)
+}
+
+// Sets 一个key  存储对象
+type Sets[T comparable] map[T]struct{}
+
+// Make 实例化一个map
+func Make[D comparable]() Sets[D] {
+	// 泛型就像一个管道一样,只要实例化的时候管子里的东西一致,那么就是一根管子
+	return make(Sets[D])
+}
+
+// Add 向这个sets添加内容
+func (s Sets[T]) Add(t T) {
+	s[t] = struct{}{}
+}
+
+// delete ,从这个sets中删除内容
+func (s Sets[T]) Delete(t T) {
+	delete(s, t)
+}
+
+//  Contains 播报t是否属于这个sets
+func (s Sets[T]) Contains(t T) bool {
+	_, ok := s[t]
+	return ok
+}
+
+//Len sets拥有的长度
+
+func (s Sets[T]) Len() int {
+	return len(s)
+}
+
+// Iterate 迭代器,并且给予每个元素功能
+
+func (s Sets[T]) Iterate(f func(T)) {
+	for k := range s {
+		f(k)
+	}
+}
+

map[1:{} 2:{}] false 2 1 2 map[1:{} 2:{}] map[1:{}]

例子三:外部定义的约束 实现一个sort接口类型

package main
+
+import "fmt"
+
+func main() {
+	fmt.Println("Hello, 世界")
+}
+// ~ 代表只要底层满足这些类型也可以算满足约束
+type Ordered interface {
+	~int | ~int8 | ~int16 | ~int32 | ~int64 | ~uintptr |
+		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |
+		~float32 | ~float64 | ~string
+}
+type orderedSlice[T Ordered] []T
+
+func (s orderedSlice[T]) Len() int           { return len(s) }
+func (s orderedSlice[T]) Less(i, j int) bool { return s[i] < s[j] }
+func (s orderedSlice[T]) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
+func OrderedSlice[T Ordered](s []T) {
+	sort.Sort(orderedSlice[T](s))
+}
+

issues

关于泛型中的零值

在 go 里面对泛型的零值并没有一个所谓的泛型零值可以使用,需要根据不同的实践去实现,比如

package main
+
+import "fmt"
+
+func main() {
+	
+}
+
+type Aget[T any] struct {
+	t *T
+}
+// 根据实际判断,如果a的t不等于nil再返回,如果是nil就返回一个T类型的nil(意思就是只声明)
+func (a *Aget[T]) Approach() T {
+	if a.t != nil { 
+		return *a.t
+	}
+	var r T
+	return r
+}
+
+

实际上目前,还没一个确切的泛型的零值,那么我们要做的只能是按照实际来具体分析,按照提案,以后有可能使用 return ... return _ return return nil return T{} 这些都是可能的结果,我个人比较喜欢 return T{} 来表示泛型的零值,拭目以待吧。

无法识别使用了底层数据的其它类型

type Float interface {
+	~float32 | ~float64
+}
+
+func NewtonSqrt[T Float](v T) T {
+	var iterations int
+	switch (interface{})(v).(type) {
+	case float32:
+		iterations = 4
+	case float64:
+		iterations = 5
+	default:
+		panic(fmt.Sprintf("unexpected type %T", v))
+	}
+	// Code omitted.
+}
+
+type MyFloat float32
+
+var G = NewtonSqrt(MyFloat(64))
+

这里约束 Float 拥有的约束类型是 ~float32float64 当在 switch 中定义了 float32 和 flaot64 时,无法识别下面的新类型 MyFloat 即使它的底层时 float32,go 的提议是以后在 switch 中使用 case ~float32: 来解决这个问题,目前尚未解决这个问题

即便约束一致,类型也是不同的

func Copy[T1, T2 any](dst []T1, src []T2) int {
+	for i, x := range src {
+		if i > len(dst) {
+			return i
+		}
+		dst[i] = T1(x) // x 是 T2类型 不能直接转化为 T1类型
+	}
+	return len(src)
+}
+
+

T1,和 T2 虽然都是 any 的约束,但是啊,它不是一个类型啊!

Copy[int,string]() // 这种情况下,你能说可以直接转化吗???
+

这种代码可以更改一下

dst[i]= (interface{})(x).(T1)
+

确认是一种类型以后才能转化

参考资料

  • https://coolshell.cn/articles/21615.html
  • https://go.dev/doc/tutorial/generics
  • https://colobu.com/2021/08/30/how-is-go-generic-implemented/
  • https://go.googlesource.com/proposal/+/refs/heads/master/design/43651-type-parameters.md
  • https://quii.gitbook.io/learn-go-with-tests/go-fundamentals/generics
  • https://mp.weixin.qq.com/s/zKnh_iPm8skxWv3rxaOscw
  • https://mp.weixin.qq.com/s/BFsoQPvrog_sMKMTEofZyQ
+ + + diff --git "a/\345\237\272\347\241\200/\347\273\223\346\236\204\344\275\223/index.html" "b/\345\237\272\347\241\200/\347\273\223\346\236\204\344\275\223/index.html" new file mode 100644 index 000000000..d106514a7 --- /dev/null +++ "b/\345\237\272\347\241\200/\347\273\223\346\236\204\344\275\223/index.html" @@ -0,0 +1,319 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

结构体

简单介绍 struct

type People struct {
+  Addr string
+  name string
+  year int
+}
+
  • Peopel 首字母大写,根据我们所学的首字母大写可导出的知识,它是包级可导出结构
  • Addr 首字母大写,所以它是可导出字段,name 和 year 都是小写,所以他们俩不可导出

下面看一下结构体的使用

// 初始化
+func main(){
+  var p People
+  p.Addr = "北京"
+  p.name = "小明"
+  p.year = 2000
+
+  var p1 = People{
+    Addr: "北京",
+    name: "小明",
+    year: 2000,
+  }
+
+  var p2 = &People{
+    Addr: "北京",
+    name: "小明",
+    year: 2000,
+  }
+}
+type People struct {
+  Addr string
+  name string
+  year int
+}
+
+
// 结构体的调用
+
+func main(){
+  var p  = &People{
+    Addr: "北京",
+    name: "小明",
+    year: 2000,
+  }
+  // 这里是语法糖,p虽然是people的指针,
+  // 但是它却可以直接调用 Addr
+  // 实际上就是 (*p).Addr 的省略
+  p.Addr = "上海"
+  p.name = "小红"
+  p.year = 2001
+  fmt.Println(p.Addr, p.name, p.year)
+
+  // 我们还可以使用new来代替&
+  // 这种写法跟上文中的 var p = &People{} 
+  // 一个意思
+  var p1 = new(People)
+  fmt.Println(p1)
+}
+type People struct {
+  Addr string
+  name string
+  year int
+}
+

解耦结构体声明和调用

当我们实现结构体的时候,如果显示写出结构体的字段变量名称,就可以不按照顺序,以及可以不完全实现全部字段,这样的话,结构体的声明和实现就可以完全解耦,当然可以隐藏实现的结构体变量,那么你不得不要按照顺序,以及实现全部字段,满足这两者才可以。

// 显式实现
+type People struct {
+  Addr string
+  name string
+  year func(int)int
+}
+func main(){
+  var p = People{
+    Addr: "北京",
+    year: func(a int)int{
+      return 2000 + a
+    }
+  }
+ 
+}
+

上述代码就是显示的写出了字段的变量名称,你看,name 并没有被写上,这种情况下,name 就会被命名为一个初始值,即 “”

这样,即使结构体本身有什么增加字段的行为,实现结构体的逻辑代码也不用改变了。

如果是隐式的话,那么必须按照顺序,以及数量进行实现,建议在字段不变以及字段数量非常少的时候使用。

匿名 struct

我们使用匿名 struct,可以完全将另一个结构体嵌入到这个结构体中。

type Student struct{
+  People
+  score int
+}
+type People struct {
+  name string
+  year string
+  addr string
+}
+

使用匿名函数,等于将这个匿名函数的字段完全给予了这个新的结构体,这跟使用这个结构体当作一个字段的类型是不同的,我们看一个例子

type Student struct{
+  People People
+  score int
+}
+type People struct {
+  name string
+  year string
+  addr string
+}
+

这段代码跟上文看起来很像,但是一个是将 people 直接嵌入,一个是当成了它的一个字段类型。让我们看一下这两者使用起来的区别

type Student1 struct{
+  People People
+  score int
+}
+type Student struct{
+  People
+  score int
+}
+type People struct {
+  name string
+  year string
+  addr string
+}
+
+func main(){
+  var s = new(Student)
+  var s1 = new(Student1)
+  s.name = "小明"
+  s.year = "2000"
+  s.addr = "北京"
+  s.score = 100
+  fmt.Println(s.name, s.year, s.addr, s.score)
+  s1.people.name = "小红"
+  s1.people.year = "2001"
+  s1.people.addr = "上海"
+  s1.score = 200
+  fmt.Println(s1.people.name, s1.people.year, s1.people.addr, s1.score)
+}
+

我们发现匿名函数的字段直接赋予了新的结构体,它不再需要显式的调用 s1.People.name

实际上,不止一个 struct 可以当作匿名函数,内置类型也是可以的

type Student struct{
+  string
+  People
+  int
+  score int
+}
+type People struct {
+  name string
+  year string
+  addr string
+}
+
+func main(){
+  var s = new(Student)
+  s.People = People{
+    name: "小明",
+    year: "2000",
+    addr: "北京",
+  }
+  s.int = 12
+  s.string = "12"
+  s.score = 100
+  fmt.Println(s)
+}
+

函数也可以将指针类型当作类型以及直接嵌入,与非指针相比,我们不能直接调用嵌入的结构体的字段,因为它目前还是 nil,这个时候我们必须先将结构体初始化,然后再进行操作,下文的代码有演示。

type Student struct{
+  Name *Name
+  *People
+  int
+  string
+  score int
+}
+type Name string{
+  fist string
+
+  last string
+}
+type People struct {
+  name string
+  year string
+  addr string
+}
+
+func main(){
+  var s = new(Student)
+
+  s.Name = new(Name)
+  s.People = new(People)
+
+  s.Name.first = "小明"
+  s.Name.last = "小红"
+  s.name= "小明"
+  s.year = "2000"
+  s.addr = "北京"
+  s.int = 12
+  s.string = "12"
+  s.score = 100
+
+  fmt.Println(s)
+}
+
+

通过这个演示我们发现,其实嵌入更像是一种语法糖,它就跟将结构体当作类型,前面的结构体字段变量跟这个类型保持一致这种操作是同样的作用,只不过直接嵌入可以将嵌入的字段当作自己的字段,省略了中间的变量罢了,它有下面的等于关系

type Student struct{
+  //People *People
+  // 等于 
+  // *People
+
+
+  // People People
+  // 等于 
+  // People
+  
+
+}
+type People struct {
+  name string
+  year string
+  addr string
+}
+

当一个结构体拥有了其它嵌入的时,也一起拥有了在他们身上的方法,不过,非嵌入的那种,还是需要带上中间的字段变量名称比如:

package main
+
+type Student struct {
+	Name Name
+	*People
+	int
+	string
+	score int
+}
+
+type Name struct {
+	first string
+	last  string
+}
+
+type People struct {
+	name string
+	year string
+	addr string
+}
+
+func (s *Name)S() {
+	println("stuend-s")
+}
+
+// 如果这里不是定义在指针上的方法,这段代码将报错
+// 原因是 people嵌入的时候还是nil,如果这里不是指针上的方法,是值上的方法
+// 底层是需要 *People (取值)  操作的,但是这时候是nil,所以就会报错。
+func (s *People)SetS() {
+	println("people-setS")
+}
+
+func main() {
+	var s = new(Student)
+  // 这里注意,因为不是嵌入的操作,所以它必须带上中间的 Name
+	s.Name.S()
+	s.SetS()
+}
+
+

空结构体

通常,当我们需要一个临时的变量时,我们可以会想到设置一个 bool 类型,因为我们潜意识中感觉一个 bool 类型是比较小的,但是一个空的变量才是最小的,下面让我们看一个例子,这个例子发生在使用 channel 传递信息这个场景。

func main() {
+  sig := make(chan bool)
+  go func(){
+    time.Sleep(time.Second)
+    sig <- true
+  }()
+  <- sig
+  fmt.Println("任务已经完成")
+}
+

没错,这段代码中,使用一个 bool 类型的 channel 是没什么问题的,你甚至也可以使用 int string 都可以,因为只是传递一个信息,信息的内容不重要,但是当我们站在优化的角度来考虑,这里的 bool 就不完美了,我们改成空的结构体即可:

func main() {
+  sig := make(chan struct{})
+  go func(){
+    time.Sleep(time.Second)
+    sig <- struct{}{}
+  }()
+  <- sig
+  fmt.Println("任务已经完成")
+}
+

需要注意的是,一个空的结构体,表示它类型的方式是 struct{},而使用这个空结构体的方式就是 struct{}{},前面的大括号是跟 struct 一起的整体表示空结构体,后面的大括号表示一个空结构体类型的结构体调用

直接嵌套还是作为字段

type Pool struct {
+  wg sync.WaitGroup
+  JobQueue chan Job
+  dispatcher *dispatcher
+}
+// or
+type Pool struct {
+  sync.WaitGroup
+  
+  JobQueue chan Job
+  dispatcher *dispatcher
+}
+
+

结构体嵌套可能带来的问题:

  • 名称冲突 +如果 Pool 结构体中还定义了 Add/Done/Wait 方法,和嵌套的 WaitGroup 中的方法就会产生冲突。

  • 不必要的方法 +嵌套整个 WaitGroup 会让 Pool 结构体拥有 Add/Done/Wait 等方法,但 Pool 可能只需要 Wait 就够了。

  • 使结构体臃肿 +嵌套整个 WaitGroup 会让 Pool 结构体看起来很臃肿,包含许多其实用不到的方法。

  • 继承关系不清晰 +Pool 并不是 WaitGroup 的一种,嵌套整个 WaitGroup 让人可能误以为它是在继承 WaitGroup。

所以,作为一个字段,可以避免上述问题,又可以保持必要的功能。

总结一下,嵌套要慎用,只有当:

  • 被嵌套的类型方法不会与现有方法冲突
  • 被嵌套的所有方法都会被用到
  • 两者之间有逻辑上的继承关系

时,才更适合使用嵌套 (组合) 的方式。

否则,使用字段的方式可以获得必要的功能,而不引入嵌套的潜在问题。

+ + + diff --git "a/\345\237\272\347\241\200/\351\200\273\350\276\221\345\222\214\345\210\244\346\226\255\350\257\255\345\217\245/index.html" "b/\345\237\272\347\241\200/\351\200\273\350\276\221\345\222\214\345\210\244\346\226\255\350\257\255\345\217\245/index.html" new file mode 100644 index 000000000..ad90c6809 --- /dev/null +++ "b/\345\237\272\347\241\200/\351\200\273\350\276\221\345\222\214\345\210\244\346\226\255\350\257\255\345\217\245/index.html" @@ -0,0 +1,199 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

逻辑和判断语句

go 语言基本上继承了 c 语言的逻辑判断语句,但是仍有些不同,比如:

  • 在循环语句中,仅保留了 for 语句,do-while,while 均不支持,这也是满足了 go 小而美的原则 -- 单一功能仅保留一种方式。
  • break continue 后面添加 label 功能
  • switch 中 case 语句不会自动执行下一个 case,需要显示的使用 fallthrough,当然了在 select 中的 case,并没有 fallthrough 功能
  • switch 中 case 支持表达式列表
  • switch 增加 type 模式,让类型也可以作为选择的条件
  • 跟 c 语言最大的不同,增加了 select - case 功能。

if

go 语言中的控制语句基础语法:

if conditon {
+
+} else if condition{
+
+} else {
+  
+}
+// 比如:
+
+if a > 0 {
+  println("a > 0")
+}else if a < 12 {
+  println("a > 0 并且 < 12")
+}else {
+  println("a > 12 或者是非正数")
+}
+

go 语言推崇的控制语句是 “快乐路径”:

  • 当出现错误时,直接返回
  • 成功的逻辑不要放在 if 语句中
  • 返回值通常在此函数的最后一行
err, value := handle()
+
+if err != nil {
+  return err
+}
+
+return value
+

for

for 循环的基础语言和 c 类似,for range 语句中,range 后面接数组,指向数组的指针,切片,字符串,和拥有读权限的 channel

for i:= 0;i< 10;i++ {}
+
+for k,v := range sliceValue{}
+
+

其中普通循环语句中,i 值属于整个 loop 所有,这一点需要好好注意,我们在作用域那一章节中也提到过。

for-range 语句中,有两点需要注意,首先跟普通循环一样,变量属于整个 loop 所有。

其次,k,v 变量是切片或者 map 的 index 和 value 复制体,当遇到比如 slice 更改数据的时候,切勿直接更改 k v,可使用 arr[k] = v+1 这种方式直接改变切片本身,另外,当使用 for-range 语句时,如果第二个变量没有使用的价值,可以不写,并且无需使用占位符 _


+for k := range sliceValue{}
+
+// 如果是省略第一个,那么还是需要占位符的
+
+
+for _,v := range sliceValue{}
+

切片在运行时,len 是会变化的,因为确定切片的 len 是 runtime 的责任,而数组的 len 是在编译期确定的,for - range 后面的数组或者切片,真正处理的其实是这个数据的复制,下面看一个 bug


+	a := []int{1, 2, 3, 4, 5}
+	var number int
+	for i := range a {
+		number++
+		if i == 0 {
+			a = append(a, 6, 7)
+		}
+	}
+	println(number) // 5
+

这里 number 输出的是 5,因为我们 append 添加的长度是原切片的长度,但是循环体中存储的 len,还是最初的长度 5,所以只能循环 5 次。

for-range 中还有一个容易出 bug 的事情,比如 range 后面跟一个数组,因为 range 的时候实际上是数组的复制品,看下面这段代码:

package main
+
+import "fmt"
+
+func main() {
+	var a = [5]int{1, 2, 3, 4, 5}
+	var r [5]int
+	fmt.Println(a)
+
+	for i, v := range a {
+		if i == 0 {
+			a[1] = 12
+			a[2] = 13
+		}
+		r[i] = v
+	}
+
+	fmt.Println(r, a)
+
+}
+

本来期望的 r 值是 1 12 13 4 5,因为在 i == 0 时就更改了数组 a 的值,但是最终输出的却是 1 2 3 4 5,原因很简单,因为 v 读取的是 a 这个数组的复制品,也就是说,实际上这个代码的隐藏含义是 range a' 这里的 a' 就是 a 的复制品,所以更改了 a,a 的复制品也不会被改变,解决方法就是取这个数组的切片就可以了,这样即便是复制了,也只是复制了一份儿指针而已。

string

如果 for-range 中后面接的是 string,每次迭代不是按照 byte 来进行的,而是按照 rune 来进行,比如 "你好" 每次的迭代输出的就是你和好,而不是 byte

map

for range 后面是 map 时,无法保证 map 的输出顺序,但是 map 和 slice 一样都是胖指针,所以时可以对 map 进行直接操作的。如果在循环体中新创建一个 map 项,那么这个项目在 range 时有可能会被输出,不能保证一定。

channel

channel 的本质也是一个胖指针,所以它也可以直接被操作本体,channel 在 range 时是阻塞式的读取,如果不关闭 channel,这个 range 会一直阻塞。

var c chan int
+
+for v := range c {
+
+}
+

这段代码就会造成一直阻塞,进而触发系统的 Panic

switch && select

在 go 语言的 switch 和 select 的 case 中,我们经常会使用 break 来跳出循环,默认跳出的就是最小的那个循环单位,比如下面这个例子

func a(){
+  for {
+    select {
+      case <- time.After(time.Second):
+      break
+    }
+  }
+}
+

这段代码是有 bug 的,因为它无法跳出 for 这层循环,只能跳出 select 这里。那么我们该怎么做呢?这个时候应该使用 label 了,所谓 label 就是标签的意思,意思就是指定好了 break 的位置,它的作用就是这个。

func a(){
+  Now:
+  for {
+    select {
+      case <- time.After(time.Second):
+      break Now
+    }
+  }
+}
+

这个时候就可以 break 到 Now 指定的层级了。

我们应避免使用 fallthrough 来执行多条件表达式,比如这样的代码

switch v.(type){
+  case int: fallthrough
+  case int8: fallthrough
+    println("yes")
+}
+

这种代码也很烂,我们可以直接使用多个 case 并列的方式:

package main
+
+func main() {
+	var i int16 = 12
+	a(i)
+}
+
+func a(v any) {
+	switch v.(type) {
+	case int, int8:
+		println("yes")
+	default:
+		println("no")
+	}
+}
+

这段代码使用的就是判断 type 的断言模式,固定用法就是 value.(type) 前面是要判断的 value,后面是固定用法 type,必须是这个单词才行。

我再带你回忆一下普通的断言,a.(int) 除了在 switch 中的断言方式,普通的断言就跟这段代码是一致的,一个 any 类型 (interface {}) 后面跟具体的类型。

经过上面的初步介绍,接下来,我们深入看一下 for range 的一些底层原理

汇总一下 for 和 for range 中最容易迷惑的几段代码

第一段代码:

func main() {
+  arr := []int {1,2,3}
+  for _,v :=  range arr {
+    arr = append(arr,v)
+  }
+  fmt.Println(arr) 
+}
+

这段代码 for 循环不会一直循环,原因是,arr 会在 range 一个复制一份儿,这个复制体的 len 在最初的 range 中的开头已经确定是 3,后面继续追加的 arr,并不会改变这个最初读取的 len == 3 这个结果。

不过,如果你使用的是传统的循环,那么这种写法就会出现 bug:

package main
+
+import "fmt"
+
+func main() {
+	n := 12
+	for i := 0; i < n; i++ {
+		n++
+		fmt.Println("i")
+	}
+}
+

使用这种传统的 for 循环,因为 n 在循环体和循环内部都是同一个,所以循环不会结束

因此你应该将这种代码改写为 for - range 模式:

go 1.22 增加了对于整数的 for range,之前只有 chan slice map,不过整数的 for range 前面只有一个变量,并且跟其他 for range 一致,n 为复制值,并且 for i := range n,i 也是复制值。这跟其他的 for range 保持一致

package main
+
+import "fmt"
+
+func main() {
+	n := 12
+	for range n {
+		n++
+		fmt.Println("i")
+	}
+}
+

第二段代码:

arr := []int{1,2,3}
+result := [] *int{}
+for ,v := range arr {
+result = append(result, &v)
+}
+

这段代码是存在 bug 的,&v,首先,根据作用域可知道,这个 v 是 loop 级作用域,那么这个 result 中存在的&v 就是同一个值,所以这个代码是错误的。

改正的方式就是放入正确的切片中数据的指针即可:

arr := []int{1,2,3}
+result := [] *int{}
+for k := range arr {
+result = append(result, &arr[k])
+}
+

第三段代码:

for i,_ := range result {
+
+  result[i] = 0
+}
+

这是一段对 int 切片进行归零的方法,很多人会觉得这要循环一次会非常浪费时间,其实不会,因为在编译器中,它不会真的循环,编译器会优化这个操作,直接给它内存清零。

+ + + diff --git "a/\345\237\272\347\241\200/\351\224\231\350\257\257\345\244\204\347\220\206/index.html" "b/\345\237\272\347\241\200/\351\224\231\350\257\257\345\244\204\347\220\206/index.html" new file mode 100644 index 000000000..56a87eedd --- /dev/null +++ "b/\345\237\272\347\241\200/\351\224\231\350\257\257\345\244\204\347\220\206/index.html" @@ -0,0 +1,562 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

错误处理

错误处理的基本认识

在 go 语言中,没有传统编程语言的 try - catch 操作,go 语言中一切错误都需要显式处理:

if err := readFile("./x"); err != nil {
+	return err
+}
+

通常,我们规定函数返回的最后一个数据是错误接口:

func age(v int)(int, error){
+	if v > 10 {
+		return 10, nil
+	}
+	return -1, fmt.Errorf("错误x")
+}
+

我们直接返回一个 err 是一种简单的做法,如果错误比较初级也可以这么做,但是如果想要带有更精确的提示信息,可以在返回的时候 wrap 一层信息:

就以上文的读取数据为例

if err := readFile("./"); err != nil {
+	return fmt.Errorf("在读取数据的时候发生了错误,错误信息是:%w", err)
+}
+

wrap 一层信息,对于错误的定位更加高效

Error 的本质是什么?

错误处理的核心就是下面这一个 error 的接口

type error interface{
+	Error()string
+}
+

所以只要我们的自建类型实现了这个接口,就可以使用很多的错误处理的方法。

自定义 error

我们使用 errors.New() 的时候其实就是返回了一个 go 自建的,叫做 errorString 的实现了 error 接口的结构体。 +这就是自建 error 的方法

func main() {
+	e := errors.New("a")
+	println(e)
+}
+// (0x47fd48,0xc00003e730)
+

为了防止在比较错误的时候发生错误一致的情况,所以自建 error,返回的实际上是一个指针。

下文会提用什么方法进行比较 err,实际上就是 “两个接口类型是否相等 --- 类型一致,值一致”,如果返回的值是指针,那么值肯定就不可能一样了。

// go 源代码
+
+func New(s string) error {
+	return &errorString{s}
+}
+

当我们使用 fmt.Errorf() 的时候,其实也是使用的上述方法。

不过,如果我们使用了占位符 %w 时,将不会使用上述方法,而是使用了 wrapError:

type wrapError struct {
+	msg string
+	err error
+}
+func(e *wrapError) Error()string{
+	return e.msg
+}
+func(e *wrapError) Unwrap() error{
+	return e.err
+}
+

使用这种方式主要是为了错误链,那就让我们看一下如何使用错误链的相关操作。

errors.Is()

上文我们说到,错误可以使用 wrap 的方式进行封装,那么如果我们想判断封装的这么多层的错误中,有没有哪一层错误等于我们要的值,可以使用这个函数进行判断:

func main(){
+	err := errors.New("err is now")
+	err1 := fmt.Errorf("err1:%w",err)
+	err2 := fmt.Errorf("err1:%w",err1)
+	if errors.Is(err2,err) {
+		fmt.Println("是一个错误")
+	}
+}
+

errors.As()

这个方法跟上文的 Is() 类似,只不过它判断的是类型是否一致。

type errS struct {
+	a string
+}
+
+func (t *errS) Error() string {
+return t.a
+}
+
+err := &errS{"typical error"}
+err1 := fmt.Errorf("wrap err: %w", err)
+err2 := fmt.Errorf("wrap err1: %w", err1)
+
+var e *errS
+// 这里的 target 必须使用指针
+if !errors.As(err2, &e) {
+	panic("TypicalErr is not on the chain of err2")
+}
+	println("TypicalErr is on the chain of err2")
+	println(err == e)
+

errors.Join()

这个方法是将几个错误结合在一起的方法:

	err1 := fmt.Errorf("err1:%w",err)
+	err2 := fmt.Errorf("err1:%w",err1)
+	err := errors.Join(err1,err2)
+

当错误处理遇到了 defer

func age() (int, error){
+	if xx {
+		return 0 ,err
+	}
+	defer f.close
+}
+

这段伪代码的意思是说,当有条件之后,返回一个错误,但是 defer 的内容发生在这个 err 被固定之后,所以 defer 中如果再有错误将不会被处理。

那么我们该怎么更改呢?

我想你一定想到了前文我们说过,使用带有变量的返回值是可以将 defer 的值进行返回的:

func age() ( i int, e error){
+	if xx {
+			e = xx
+			i = xx
+		return 
+	}
+	defer f.close
+}
+

那么这种写法,defer 中如果发生了错误就会覆盖掉了程序执行中的 err,所以这种方法也是不行的,即使它能照顾到了 defer 中的错误处理。

我们可以将错误处理都放在 defer 中处理就可以了

func age() ( i int, e error){
+	if xx {
+		i = xx
+		e = xx
+		return 
+	}
+	defer func(){
+		e1 := f.close()
+		if e != nil {
+				if  e1 != nil {
+					log(e1) 
+				}
+				return 
+		}
+		err = e1
+	}()
+}
+

这样,两种错误都能处理到了

错误处理实战的五种方式

经典的错误处理方式

每一个步骤分别直接处理错误

type age interface{
+	getAge()error
+	putAge()error
+	allAge()error
+}
+func D(ag age)error{
+	if err != ag.getAge();err != nil {
+		return fmt.Errorf("%w",err)
+	}
+	if err != ag.putAge();err != nil {
+		return fmt.Errorf("%w",err)
+	}
+	if err != ag.allAge();err != nil {
+	  return fmt.Errorf("%w",err)
+	}
+}
+

屏蔽过程中的错误处理

将错误放置在对象的内部进行处理:

type FileCopier struct{
+	w *os.File
+	r *os.File
+	err error
+}
+func (f *FileCopier)open(path string)( *os.File,error){
+	if f.err != nil {
+		return nil, f.err
+	}
+
+	h, err := os.Open(path)
+	if err!= nil {
+		f.err = err
+		return nil, err
+	}
+	return h,nil
+}
+
+func(f *FileCopier)OpenSrc(path string) {
+	if f.err != nil {
+		return
+	}
+	f.r,f.err = os.Open(path)
+	return 
+}
+func(f *FileCopier) CopyFile(src, dst string) error {
+	if f.err != nil {
+		return f.err
+	}
+	defer func(){
+		if f.r != nil {
+			f.r.Close()
+		}
+		if f.w!= nil {
+			f.w.Close()
+		}
+		if f.err != nil {
+			if f.w != nil {
+				os.Remove(dst)
+			}
+		}
+	
+	f.opensrc(src)
+	f.createDst(dst)
+	return f.err
+		}
+	}()
+}
+

这段代码并不是特别完整,但是从中我们还是可以理解这种将错误放在对象中的写法的技巧。

首先,错误直接放置在对象自身,在方法中首先去调用这个字段来看是否拥有错误,如果有,直接退出即可

如果没有错误继续往下走,如果本次方法发生错误就继续将这个错误赋值给这个字段,

当最后处理的方法时,这里也就是 copyfile 方法,我们在 defer 中要对于各个子方法进行判断,到底是哪个方法有错误,然后逐一进行判定。相当于处理错误的逻辑集中放置到了最后一个函数进行执行了。

也就是说,将错误放置在对象本身的时候,通常应该为顺序调用的方法,一旦前者出现错误,后者即可退出

如果不是顺序的执行过程,那么有些的错误就可能被湮没,导致错误无法被感知。

分层架构中的错误处理方法

常见的分层架构

  • controller 控制层
  • service 服务层
  • dao 数据访问层

dao 层生产错误

if err != nil {
+	return fmt.Errorf("%w",err)
+}
+

service 追加错误

err := a.Dao.getName()
+	if err != nil {
+	return fmt.Errorf("getname err: %w",err)
+	}
+}
+

controller 打印错误

if err!= nil {
+	log(err)
+}
+

pkg/errors

如果感觉标准库提供的错误处理不够丰富,也可以使用 github.com/pkg/errors 来处理错误

此包常用的方法有

// 生成新的错误,同样会附加堆栈信息
+func New()error
+// 只附加新的消息
+func WithMessage(err error,message string) error
+// 只附加堆栈信息
+func WithStack(err error)error
+// 附加信息 + 堆栈信息(就是一大堆的各种文件的堆栈调用过程的详细信息)
+func Wrapf(err error,format string,args...interface{}) error
+// 获取最根本的错误(错误链的最底层)
+func Cause(err error) error
+

例如:

func main(){
+	err := age()
+	if err != nil {
+		// %+v 是 pkg/errors 包提供的格式化输出格式,输出错误堆栈
+		fmt.Printf("%+v",err)
+	}
+}
+
+func age()error {
+	return errors.Wrap(err,"open error")
+}
+

在使用这个 pkg/errors 包的时候要注意一件事,因为 wrap 可以包装堆栈向上输出,如果你调用的第三方库使用了 wrap,你再次使用 wrap,那么就会出现两堆相同的堆栈信息,这造成了极大的冗余。

所以,

  • 在提供多人使用的三方库的时候不要使用 wrap,只有逻辑代码的时候使用 wrap 的功能
  • 遇到一个错误不打算处理,那么要带上足够多的信息再向上抛出
  • 一旦错误处理完成之后就没有错误了,不再需要把错误继续网上抛,返回 nil 即可

所以我们使用 pkg/errors 包将上面的分层写法做一个更完善的改进:

dao 层生产错误

// a
+func getName()error {
+	if err != nil {
+		// 返回此处的错误堆栈
+		return errors.Wrap(err,"error:")
+	}
+	return nil
+}
+
+

service 追加错误

err := a.getName()
+	if err != nil {
+		// 不返回堆栈了,仅仅添加错误
+		return errors.WithMessage(err,"getName error")
+	}
+}
+

controller 打印错误

if err!= nil {
+	// 添加日志
+	log(err)
+}
+

errgroup 的使用技巧

errgroup 的使用方法是 golang.org/x/sync/errgroup

package main
+
+import (
+	"context"
+	"fmt"
+	"golang.org/x/sync/errgroup"
+)
+
+
+func main() {
+	g, ctx := errgroup.WithContext(context.Background())
+	// 启动一个 goroutine去处理错误
+	g.Go(func() error {
+		return fmt.Errorf("error1")
+	})
+	g.Go(func() error {
+		return fmt.Errorf("error2")
+	})
+	// 类似 waitgroup 的 wait 方法
+	if err := g.Wait(); err != nil {
+		fmt.Println(err)
+	}
+}
+

错误处理相关技巧

这里会介绍在实战过程中用到的诸多技巧

使用 errors.New() 时要写清楚包名

package age
+
+ErrMyAge := errors.New("age: ErrMyAge is error")
+ErrMyAddress := errors.New("age: ErrMyAddress is error")
+

使用 error 处理一般错误,使用 panic 处理严重错误 (异常)

使用这种模型就避免了类似 Java 那种所有错误都一样的行为,Java 的使用 try-catch 的方式导致任何错误都是一个方式去处理,非常有可能让程序员忽略错误的处理

然而 go 不同,错误使用 error,异常使用 panic 的方式去处理。

  • 错误:error
  • 异常:panic

假设我们在代码中使用了 panic,通常来说,为了代码的健壮性还是会使用 defer 函数去运行一个 recover() 的,程序的存活比啥都重要。

基础库,应该避免使用 error types

因为这种写法容易造成代码的耦合,尤其是在我们写的基础库中,非常容易造成改动代码来引入的不健壮性。

使用自定义的 error type

package main
+
+import (
+	"fmt"
+)
+
+// 为了额外增加更多的错误信息,字段需大写
+type ErrMyAge struct {
+	EV string
+	MErr int
+}
+
+func (e *ErrMyAge) Error() string {
+	return fmt.Sprintf("age: %s", e.EV)
+}
+
+func main() {
+	err := &ErrMyAge{"err age is hi"}
+	fmt.Println(err)
+}
+

使用 errors.New() 哨兵错误模式:

// 我方代码
+ErrAge := errors.New("age: ErrAge is error")
+ErrAddress := errors.New("age: ErrAddress is error")
+
+---------
+// 使用者
+
+import(
+	"github.com/shgopher/age"
+)
+
+func age(){
+		// 带来了耦合
+		if errors.Is(err, age.ErrAge){
+		// 处理
+		}
+	}
+}
+

实际上他们都是 error types,只不过前者比后者增加了很多额外的信息,但是相同点是,他们都造成了耦合

如果别人使用了这个基础库,那么势必这些错误就会跟使用者的代码耦合,我们改动了代码,第三方的代码就会因此受到影响。

因此,在对外暴露的基础包中,我们应尽量减少定义哨兵错误 (上述定义方法被称之为哨兵模式的错误)

上述提供的哨兵模式是透明的错误导出机制,所以容易造成耦合

我们可以提供不透明的机制,不导出透明的错误类型,仅仅让用户判断是否等于 nil,就可以防止耦合的存在

import ("github.com/shgopher/age")
+
+func age(){
+
+	if err:= age.Bar(); err == nil {
+		return
+	}
+	
+}
+

这也是大多数程序应该提供的模式,不对外暴露,避免了耦合

那么这种方法的缺陷也很明显了,就是无法获取更多的错误信息,理论上来说,我们也没必要获取那么多错误信息,但是如果真的要获取错误信息了,该如何去做呢?

我们可以向外暴露一些动作,只暴露行为:

type mage interface {
+	age() bool
+}
+
+// 通过一个对外暴露的函数可以对外输出行为
+// 并且还不用暴露出具体的对象
+func IsMage(err error)bool {
+	 a, ok := err.(mage)
+	 return ok && a.age()
+}
+

优化不必要的代码让程序变得更简洁

方法一将大函数变小函数,通过封装函数的方法从视觉上降低 if err 的影响。

// 改造之前
+
+func OpenFile(src ,dst string) error {
+	if r,err := os.Open(src); err != nil {
+		return err
+	}
+	if w,err := os.Create(dst); err != nil {
+		r.Cloase()
+		return err
+	}
+	if _,err := io.Copy(w,r); err!= nil {
+
+		return err
+	}
+	return nil
+}
+
+// 将前面两个操作封装成一个函数
+
+func OpenD(src dst string) (*os.File,*os.File,error) {
+	var r ,w *os.File 
+	var err error
+
+	if r,err = os.Open(src); err!= nil {
+		return nil,nil,err
+	}
+	if w,err = os.Create(dst); err!= nil {
+		r.Close()
+		return nil,nil,err
+	}
+
+	return r,w,err
+}
+// 主函数就只有一个 if err 了
+func OpenFile(src,dst string)error {
+	var err error
+	var r, w *os.File
+	if r,w,err = OpenD(src,dst); err!= nil {
+		return err
+	}
+	defer func(){
+		r.Close()
+		w.Close()
+	}()
+	if _,err = io.Copy(w,r); err!= nil {
+		return err
+	}
+	return nil
+}
+

方法二将代码中不必要的成分删除,来保证代码的简洁

// 改造前
+func AuthticateRequest(r *Request)error{
+	err := authticate(r.User)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+// 实际上 authticate 只返回一个 error 类型的接口,根本不需要判断
+func AuthticateRequest(r *Request)error{
+	return authticate(r.User)
+}
+

方法三使用更加合适的方法

// 我们要逐行去读取数据
+
+// 改造前
+func Countlines(r io.Reader)(int,error){
+	var(
+		br = bufio.NewReader(r)
+		line int
+		err error
+	)
+
+	for {
+		_,err = br.readline('\n')
+		line ++ 
+		if err != nil {
+			break
+		}
+	}
+		if err != io.EOF {
+			return 0,err
+		}
+		return lines,nil
+		
+}
+
+// 代码看起来也是很合理的样子,也很简洁,但是,我们其实用的函数不是特别的合适
+
+//其实这里使用 scan 更加合适,代码量更加精简,并且结构异常舒服
+func Countlines(r io.Reader) (int,error){	
+	sc := bufio.NewScanner(r)
+	lines := 0
+
+	for sc.Scan() {
+		lines++
+	}
+	
+	return lines, sc.Err()
+}
+
+

错误应该只处理一次

我们有日志系统,有些时候我们发现一个错误,然后打了一个日志,然后又把错误给 return 了,实际上这与 go 语言哲学中说的错误只处理一次相违背

// ❌
+func age()error{
+	 _, err := os.Open("./a")
+	if  err!= nil {
+		log.Prinln(err)
+		return err
+	}
+}
+
// ✅
+func age()error{
+	 _, err := os.Open("./a")
+	if  err!= nil {
+		return errors.Wrap(err,"open error")
+	}
+}
+

正确的处理方法是错误只处理一次,那么在什么时候处理呢?

上文提到了多层的架构设计,我们在底层 (带有堆栈的错误向上抛出) 和中层 (仅仅附加信息再次向上抛出) 仅仅是向上抛出,并不需要将错误记录在日志中,在应用层才需要去使用日志记录错误,日志记录完错误以后,也不需要再向上抛出错误了 (最顶端了) 完全满足 “只处理一次错误” 的要求。

业务 code 码的设置

常见的 http 错误码数量较少,比如常见的只有例如 404 301 302 200 503 等,绝对数量还是较少,无法去表达业务上的错误,因此我们需要设置一套能表达具体生产业务的 code 码。

为了保证服务端的安全,我们设置的 code 码应该设置两套数据,一套显示给客户端,一套自用,以此来保证服务端的绝对安全。

有三种设计业务 code 码的方式:

一律返回 http status 200,具体 code 码单独设置

例如

{
+  "error": {
+    "message": "Syntax error \"Field picture specified more than once. This is only possible before version 2.1\" at character 23: id,name,picture,picture",
+    "type": "OAuthException",
+    "code": 2500,
+    "fbtrace_id": "xxxxxxxxxxx"
+  }
+}
+
  • http status code 通通 200
  • code 2500,才是真实的面向客户端的 code 码

使用这种方法的一大缺陷就是必须解析 body 内容才能发现具体的错误业务码,很多场景我们仅仅需要知道返回的是成功或者错误,并不需要知晓具体的业务码,这是这种方式的一大弊端。

使用合适的 http status code + 简单的信息以及业务错误代码

HTTP/1.1 400 Bad Request
+x-connection-hash: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+set-cookie: guest_id=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+Date: Thu, 01 Jun 2017 03:04:23 GMT
+Content-Length: 62
+x-response-time: 5
+strict-transport-security: max-age=631138519
+Connection: keep-alive
+Content-Type: application/json; charset=utf-8
+Server: tsa_b
+
+# 注意这里:仅仅返回简单的错误信息
+{"errors":[{"code":215,"message":"Bad Authentication data."}]}
+

这种方案也是大多数公司采纳的方案,使用具体的 http status code 可以知晓大概的业务类型,是错误还是正常运行,然后使用简单的错误信息和业务错误代码去定位具体的错误

如果业务不是特别复杂,使用这种方式即可

使用合适的 http status code + 非常详细的业务错误代码以及信息

HTTP/1.1 400
+Date: Thu, 01 Jun 2017 03:40:55 GMT
+Content-Length: 276
+Connection: keep-alive
+Content-Type: application/json; charset=utf-8
+Server: Microsoft-IIS/10.0
+X-Content-Type-Options: nosniff
+
+{"SearchResponse":{"Version":"2.2","Query":{"SearchTerms":"api error codes"},"Errors":[{"Code":1001,"Message":"Required parameter is missing.","Parameter":"SearchRequest.AppId","HelpUrl":"http\u003a\u002f\u002fmsdn.microsoft.com\u002fen-us\u002flibrary\u002fdd251042.aspx"}]}}
+

当业务逻辑稍微复杂一些,并且需要极其精准和快速的定位错误时,就需要在返回的 body 中去设置非常详细的错误信息

综上所述:

  • 使用正确的 http status code 让业务的第一步变得更加直观
  • 区别于 http status code,具体业务的 code 码会更加丰富
  • 返回尽可能详细的错误,有助于复杂逻辑的快速错误定位
  • 直接返回给客户的错误代码不应该包括敏感信息,敏感信息的 code 码仅供内部使用
  • 错误信息要求规范,简洁以及有用

业务 code 码的具体设计

引入业务 code 码的核心原因就是 http status code 太少,以及他们并不能跟具体业务挂钩。

当我们设置好良好又详细的 code 码时,我们就可以快速定位业务代码,以及可以快速知晓发生错误的等级模块,具体信息等

下面给出具体的设计思路:纯数字表达,不同的数字段表达不同的模块不同的业务

例如 100101

  • 10:表示某个服务
  • 01:表示某个服务下的模块
  • 01:模块下的错误码

10 服务 01 模块 01 错误,--- 服务 10 数据库模块未找到记录错误

一共最多有 100 个服务,每个服务最多有 100 个模块,每个模块最多有 100 个错误,如果某些模块 100 个都不够用,那怎么这个模块有必要去拆分一下了。

如何设置 http status code

  • 1xx:请求已接收,继续处理
  • 2xx:成功处理了请求
  • 3xx:请求被重定向
  • 4xx:请求错误
  • 5xx:服务器错误

由于 http status code 相对数量也不算太少,如果每一个都利用上,难免会增加复杂度,建议仅使用基本的几个即可

  • 200 - 表示请求成功执行。
  • 400 - 表示客户端出问题。
  • 500 - 表示服务端出问题。

如果上述的感觉太少,再增加下面几个也可以

  • 401 - 表示认证失败。
  • 403 - 表示授权失败。
  • 404 - 表示资源找不到,这里的资源可以是 URL 或者 RESTful 资源。

将 http status code 控制在个位数,有利于后端的逻辑代码简洁性,比如 301 302 确实是代表不同的含义,前端或许可以设置丰富的 http status code,因为浏览器会进行相关的具体操作,但是后端返回给前端的 http status code 并没有任何的操作,使用过多只会增加复杂度。

设计一个生产级的错误包

生产级的错误包需要的功能

  1. 支持错误堆栈
2021/07/02 14:17:03 call func got failed: func called error
+
+main.funcB /home/colin/workspace/golang/src/github.com/marmotedu/gopractise-demo/errors/good.go:27
+main.funcA /home/colin/workspace/golang/src/github.com/marmotedu/gopractise-demo/errors/good.go:19
+main.main /home/colin/workspace/golang/src/github.com/marmotedu/gopractise-demo/errors/good.go:10
+runtime.main /home/colin/go/go1.16.2/src/runtime/proc.go:225runtime.goexit /home/colin/go/go1.16.2/src/runtime/asm_amd64.s:1371
+exit status 1
+

拥有错误的堆栈,我们才能定位错误的根源。

  1. 支持打印不同的格式比如 %s %w %d

  2. 支持 Wrap() Unwrap() 的功能,就是错误的嵌套和反嵌套

  3. 错误包要支持 Is() 和 As() 方法,这主要是因为有错误的嵌套,所以无法再使用接口相比较的方式进行判断接口类型是否相等 (类型相同,值相同)

  4. 要支持格式化和非格式化的创建方式

errors.New("err")
+fmt.Errorf("%w",err)
+

具体实现

从 github.com/pkg/errors 包中改造即可。

增加以下字段的结构体就可以满足上面的需求

type withCode struct {
+	err   error
+	code  int
+	cause error
+	*stack
+}
+

issues

问题一: 请说出下列代码的执行输出*

package main
+
+import "fmt"
+
+func main() {
+	defer func() {
+		println("3")
+	}()
+	defer func() {
+		println("2")
+	}()
+	defer func() {
+		println("1")
+	}()
+	fmt.Println("hi1")
+	panic("oops")
+	// 这里的defer将不会进栈,所以也就不会执行了。
+	defer func() {
+		println("x")
+	}()
+	fmt.Println("hi2")
+}
+
+

答案是

hi1
+1
+2
+3
+oops
+panic: oops
+
+goroutine 1 [running]:
+main.main()
+	/tmp/sandbox1932632082/prog.go:18 +0xa7
+
+Program exited.
+

解释:Panic,意味着恐慌,意思等于 return,所以 panic 下面的数据是无法执行的,defer 不同,他们是顺序的将这些 defer 函数装入函数内置的 defer 栈的,所以在 return 之后,defer 栈会执行,所以这里的 defer 1 2 3 可以执行,Panic 前面的 hi1 可以执行,但是 Panic 之后,相当于 return 后面的 hi2 就无法执行了。

问题二: 看一段代码,分析答案

func f() {
+	defer func() {
+		if r := recover(); r != nil {
+			fmt.Println("Recovered in f", r)
+		}
+	}()
+	fmt.Println("starting f")
+	g(2)
+	fmt.Println("behind  g") //会终止执行
+}
+
+func g(i int) {
+	defer fmt.Println("Defer in g", i)
+	fmt.Println("Panicking!")
+	panic(fmt.Sprintf("%v", i))
+	fmt.Println("Printing in g", i) //终止执行
+}
+

答案是

starting f
+panicking
+Defer in g 2
+recoverd in f
+

解释一下,首先执行的是 f 函数的代码,然后开始执行 g,在 g 中遇到了 Panic,所以 panic 后面的 parinting in g 就无法执行了,所以执行了 defer in g +这个时候 f 中的 g(2) 后面的数据也无法执行了,因为整个 f 也陷入了恐慌,所以它只能 return 进入 defer 了,defer 中刚好有 recover,所以执行了 recover 信息后,就退出了函数。

参考资料

  • https://mp.weixin.qq.com/s/EvkMQCPwg-B0ffZonpwXodg
  • https://mp.weixin.qq.com/s/D_CVrPzjP3O81EpFqLoynQ
  • https://time.geekbang.org/column/article/391895
  • 极客时间《go 进阶训练营》
+ + + diff --git "a/\345\237\272\347\241\200/\351\233\266\345\200\274/index.html" "b/\345\237\272\347\241\200/\351\233\266\345\200\274/index.html" new file mode 100644 index 000000000..d0dcb086f --- /dev/null +++ "b/\345\237\272\347\241\200/\351\233\266\345\200\274/index.html" @@ -0,0 +1,117 @@ + + + + + + go 语言零值 | GOFamily - go 程序员宝典 + + + + + + + + +

go 语言零值

go 在声明时期就会为变量提供默认值。

下面是所有的原生 go 类型的零值:

  • 整数类型:0
  • 浮点数:0.0
  • 布尔类型:false
  • 字符串类型:“”
  • 指针,接口,切片,channel,map,function:nil

至于复合类型,比如数组,和结构体的声明过程,就是 递归的,针对于它所有的子元素的初始化

零值可用

go 奉行零值可用,最经典的案例是切片的 append 过程,即是你最初的切片变量只是声明,并未 make 赋予底层数组,那么 go 系统仍然处理的结果是可用,并非 Panic

var s []int
+s = append(s,1)
+fmt.Println(s)
+

下吗再看一个例子

func main() {
+	var b *bytes.Buffer
+	fmt.Println(b)
+}
+

fmt.Println(b) 是调用的 b.String(),那么为什么这里输出的是 <nil> 不是 Panic 呢?

func (b *Buffer) String() string {
+	if b == nil {
+		// Special case, useful in debugging.
+		return "<nil>"
+	}
+	return string(b.buf[b.off:])
+}
+

这就是答案,它在实现的 String 中实现了零值可用,原因一: nil 的对象是完全可以调用属于它身上的方法的原因二: 这个方法内部实现的时候又是直接 return 一个 <nil> 避免了取 nil 索引值的情况。所以说基于这两点,它零值可用。

再看一个经典的 buffer 例子:

package main
+
+import (
+	"bytes"
+	"fmt"
+)
+
+func main() {
+	var a bytes.Buffer
+	a.Write([]byte("12"))
+	fmt.Println(a.String())
+}
+

为什么只是声明了 buffer 就可以直接往里面写值呢?

// 结构体
+type Buffer struct {
+	buf      []byte // contents are the bytes buf[off : len(buf)]
+	off      int    // read at &buf[off], write at &buf[len(buf)]
+	lastRead readOp // last read operation, so that Unread* can work correctly.
+}
+
+// 取自 Write() 相关的代码 这段代码保证了即便是初始化的buf是nil也可以零值可用。
+if b.buf == nil && n <= smallBufferSize {
+  b.buf = make([]byte, n, smallBufferSize)
+  return 0
+}
+
+

另外,当切片是 nil 的时候,也是完全可以取它的 [:] 切片的,只要不超过 index 都没问题,这也是满足零值可用的一个小 tips

var s []int
+s = s[:] // 或者 s = s[:0]  or  s = s[0:] or  s= s[0:0]
+

零值不可用

  • slice 零值赋值
  • map 零值赋值
  • 互斥锁的值复制
var s slice
+s[0] = 1 // 错误 ❌
+
+var m map[int]int
+m[1] = 12 // 错误 ❌
+

再看互斥锁的案例

var mu sync.Mutex
+mu.Lock()
+mu.Unlock()
+

这段代码是可以正常使用的

但是,如果队 mu 进行值的复制就不能使用了

package main
+
+import (
+	"sync"
+)
+
+func main() {
+	var mu sync.Mutex
+	mu.Lock()
+	foo(mu)
+	mu.Unlock()
+}
+
+func foo(mu sync.Mutex) {
+	mu.Lock()
+	mu.Unlock()
+}
+
+

这个问题的解释是这样的:互斥锁是带有状态的,就是说,当你复制的时候本来是 a 的状态,然后复制过去还是 a 的状态,但是这是一个新的对象了按道理应该是初始状态,所以就会出现错误,这也是传说中的重入锁 (go 不支持),因为 go 的互斥锁是带有状态的,所以这种复制的方法就会出现错误。

参考资料

  • https://github.com/golang/go/blob/037b209ae3e0453004a4d57e152aa522c56f79e4/src/bytes/buffer.go#L117
+ + + diff --git "a/\345\267\245\347\250\213/cgo/index.html" "b/\345\267\245\347\250\213/cgo/index.html" new file mode 100644 index 000000000..f1dd6b6f2 --- /dev/null +++ "b/\345\267\245\347\250\213/cgo/index.html" @@ -0,0 +1,50 @@ + + + + + + cgo | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/copilot/index.html" "b/\345\267\245\347\250\213/copilot/index.html" new file mode 100644 index 000000000..47a168a89 --- /dev/null +++ "b/\345\267\245\347\250\213/copilot/index.html" @@ -0,0 +1,50 @@ + + + + + + Github Copilot | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/goweb\347\274\226\347\250\213/index.html" "b/\345\267\245\347\250\213/goweb\347\274\226\347\250\213/index.html" new file mode 100644 index 000000000..cee508988 --- /dev/null +++ "b/\345\267\245\347\250\213/goweb\347\274\226\347\250\213/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\345\221\275\345\220\215\346\203\257\344\276\213/index.html" "b/\345\267\245\347\250\213/go\345\221\275\345\220\215\346\203\257\344\276\213/index.html" new file mode 100644 index 000000000..a8181cf22 --- /dev/null +++ "b/\345\267\245\347\250\213/go\345\221\275\345\220\215\346\203\257\344\276\213/index.html" @@ -0,0 +1,121 @@ + + + + + + go 命名惯例 | GOFamily - go 程序员宝典 + + + + + + + + +

go 命名惯例

go 语言的命名哲学就是尽可能的简单化

go 要求的简单,不仅仅是简单化,还要关注上下文的连贯性,go 喜欢一个简洁明了的命名+对字段的注释

// userTypeInfo stores the information associated with a type the user has handed
+// to the package. It's computed once and stored in a map keyed by reflection
+// type.
+type userTypeInfo struct {
+	user        reflect.Type // the type the user handed us
+	base        reflect.Type // the base type after all indirections
+	indir       int          // number of indirections to reach the base type
+	externalEnc int          // xGob, xBinary, or xText
+	externalDec int          // xGob, xBinary or xText
+	encIndir    int8         // number of indirections to reach the receiver type; may be negative
+	decIndir    int8         // number of indirections to reach the receiver type; may be negative
+}
+

比如这段代码,对于字段和结构体都进行了注释,但是结构体内部的字段都相对简洁

包名称一般使用小写单词进行命名,例如说:contexthttpnet,无需考虑包名称的重复化,因为在 go 引入包的过程中,实际上是引入的路径,并且 go 的包在引入过程中还可以进行包的重命名

import (
+
+  "example.com/hey/log"
+
+  blog "example.net/bob/log"
+)
+

包的路径名称最后一位尽量和包的名称保持一致性,不过不一致也是有不少情况的,比如说,现在有一个适应不同语言的 log,那么命名的时候就变成了 github.com/shgopher/gologgithub.com/shgopher/javalog,但是他们实际上的包都叫 log,并不是 golog 和 javalog,那么使用的时候就需要对其中一个包进行重命名了。

变量,类型,函数,方法

因为 go 语言使用导出的变量时需要带上包的名称 (除了。省略包名称的方式,一般不提倡),所以命名的时候变量不需要再加上包的称呼,比如说 string 包的 Reader 就不需要写成 string.StringReader,写成 string.Reader 即可

go 变量的命名使用驼峰的命名方式,go 的文件名称使用下划线连接的方式,比如说 UpperNamelowName 区别就是前者是大驼峰,导出的变量,后者是小驼峰,不可导出变量,缩略词如果首字母大写那么全部大写,比如 HTTP,不能写成 Http

go 代码里使用了大量单字母或者短字母的命名方式。

for 循环中以及 if 条件语句中使用单字母

for k,v := range users{
+
+}
+
+if err := method1();err != nil {
+
+}
+

函数和方法的参数或者返回值,常见单字母或单个单词的方式出现。

func method1(name string){}
+

方法调用的时候会绑定类型信息,可以尽量使用单个单词的命名方法

函数和类型多以多单词驼峰的方法组合,比如 func MakeFile(){}type gobEncoderType struct {}

在命名变量的时候,不要带上类型信息,比如说 namesnameSlice 要好,因为 go 强调命名和使用越近越好,没必要在字段上加上类型的名称。

go 使用一致性来强调单字母或者单个单词的意义,意思就是说使用的单个字母在任何地方表示的意思都是一样的。

for k,v := range names{}
+for i,v := range users{}
+

从 k v i 这三个字母就可以观察到,绝大多数情况下,在 go 语言中 kv 就是表示的 key 和 value,i 表示的就是 index,那么即便是单个字母也不会造成识别障碍

另外 t b n 也很常见,他们通常表示 time 和 byte 以及数量

常量

  • go 语言的常量并不要求全大写,通常只有本身是全大写的常量才是全大写,比如 PI
  • 常量不需要赋予类型,系统会根据根据使用时期左变量的类型,以及运算操作进行自动推断

一般常量

const(
+  keepHostHeader = false
+)
+

全大写常量

const PI = 3.14
+
+const SIGABRT = Signal(0x6)
+

接口

拥有唯一方法的接口,或者内嵌多个拥有唯一方法的接口一律使用 单词+er 结尾,比如 Reader 或者 WriteReadCloser

go 语言推崇简单接口,和内嵌多个简单接口的接口,所以你会看到 go 语言标准库很多带有 er 结尾的接口

type Reader interface{
+  Reade()
+}
+type Writer interface{
+  Write()
+}
+type WriteReadeCloser interface{
+  Writer
+  Reader
+  Closer
+}
+

两种命名的对比

简洁的并且考虑上下文一致性的代码:

func RuneCount(b []byte)int{
+  count := 0
+  for i:= 0;i< len(b);{
+    if b[i] < RuneSelf {
+      i++
+    } else{
+      _,n := DecodeRune(b[i:])
+      i+=n
+    }
+    count++
+  }
+  return count
+}
+

见名知意的 java 式的代码:

func RuneCount(buffer []byte)int{
+  runeCount := 0
+  for index := 0;index < len(buffer);{
+    if buffer[index] < runeSelf {
+      index++
+    } else {
+      _, size := DecodeRune(buffer[index:])
+      index += size
+    }
+    runeCount++
+  }
+  return runeCount
+}
+

参考资料

  • 图书:go 语言精进之路
+ + + diff --git "a/\345\267\245\347\250\213/go\346\240\207\345\207\206\345\272\223/encoding-hex.html" "b/\345\267\245\347\250\213/go\346\240\207\345\207\206\345\272\223/encoding-hex.html" new file mode 100644 index 000000000..f27a0fd44 --- /dev/null +++ "b/\345\267\245\347\250\213/go\346\240\207\345\207\206\345\272\223/encoding-hex.html" @@ -0,0 +1,50 @@ + + + + + + encoding/hex | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\346\240\207\345\207\206\345\272\223/fmt.html" "b/\345\267\245\347\250\213/go\346\240\207\345\207\206\345\272\223/fmt.html" new file mode 100644 index 000000000..ad3b1defe --- /dev/null +++ "b/\345\267\245\347\250\213/go\346\240\207\345\207\206\345\272\223/fmt.html" @@ -0,0 +1,50 @@ + + + + + + fmt | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\346\240\207\345\207\206\345\272\223/index.html" "b/\345\267\245\347\250\213/go\346\240\207\345\207\206\345\272\223/index.html" new file mode 100644 index 000000000..306bd36d2 --- /dev/null +++ "b/\345\267\245\347\250\213/go\346\240\207\345\207\206\345\272\223/index.html" @@ -0,0 +1,158 @@ + + + + + + go 标准库的简单介绍 | GOFamily - go 程序员宝典 + + + + + + + + +

go 标准库的简单介绍

https://pkg.go.dev/std

archive

https://pkg.go.dev/archive

目前不存在 archive 包本身,存在其子包,这个包,包含的内容是对于档案文件的处理,比如 tar,zip 这种文档文件。

archive/tar

https://pkg.go.dev/archive/tar

import "archive/tar"
+

archive/tar 包,实现了对 tar 文件的处理

  • tar 文件头的设置
  • tar 文件的读写

archive/zip

https://pkg.go.dev/archive/zip

import "archive/zip"
+

archive/zip 包,实现了对 zip 文件的处理

  • zip 文件头的设置
  • zip 文件的读写

bufio

https://pkg.go.dev/bufio

import "bufio"
+

bufio 包,提供了有缓冲的 i/o,比 io 包封装程度更高。使用缓冲区来一次读取多个字节,从而减少系统调用的次数。

  • 提供了基本的读写功能
  • 提供了逐行读取的功能

builtin

https://pkg.go.dev/builtin

注意,此包无法使用 import 的方式引入,因为它只是内部包和内部类型的一个存放位置而已

比如有 append() clear() max() min() int float64 等等

bytes

https://pkg.go.dev/bytes

import "bytes"
+

bytes 包,包含了很多处理 bytes 类型的操作。

跟字符串的基本操作类型

cmp

https://pkg.go.dev/cmp

import "cmp"
+

cmp 包提供了有关比较的相关内容。

  • cmp.Ordered 表示可比较的类型约束
  • 提供了比较函数

compress

https://pkg.go.dev/compress

不直接提供 compress 包,提供了众多子包,所有的内容都是关于压缩算法

compress/bzip2

https://pkg.go.dev/compress/bzip2

import "compress/bzip2"
+

提供了 bzip2 的解压功能,然而并没有提供压缩的功能

compress/flate

https://pkg.go.dev/compress/flate

import "compress/flate"
+

flate 实现了 RFC1951 中描述的 DEFLATE 压缩数据格式。

compress/gzip

https://pkg.go.dev/compress/gzip

import "compress/gzip"
+

gzip 实现了对 gzip 格式压缩文件的解压 (读取) 和压缩 (写入)

compress/lzw

https://pkg.go.dev/compress/lzw

import "compress/lzw"
+

lzw 实现了 lzw 文件 (Lempel-Ziv-Welch) 的解压缩操作。

compress/zlib

https://pkg.go.dev/compress/zlib

import "compress/zlib"
+

zlib 实现了对 zlib 格式文件的解压和压缩操作。

container

https://pkg.go.dev/container

container 没有提供本包,提供了众多子包,这个目录下的内容都是关于容器的,这也是 go 提供的内置容器

container/heap

https://pkg.go.dev/container/heap

import "container/heap"
+

go 内置的堆,值得注意的是,go 语言仅提供了接口以及接口的相关函数,并没有具体的实现,使用时还需要自行实现接口。

container/list

https://pkg.go.dev/container/list

import "container/list
+

go 内置的双向链表,这里不是接口了,是已经实现好了的双向链表

container/ring

https://pkg.go.dev/container/ring

import "container/ring"
+

go 内置的循环链表,非接口,已经实现好了

context

https://pkg.go.dev/context

import "context"
+

context 提供了在 “多线程” 的场景下的线程控制功能,简单的说就是 context 这个上下文可以统一取消所有的上下文环境中的 goroutine

func main() {
+  // cal的调用,以及计时器的到达均可调用 ctx.Done() 的发生。
+	ctx, cal := context.WithTimeout(context.Background(), time.Second*1)
+	defer cal()
+	go func() {
+		select {
+		case <-time.After(time.Second * 2):
+			fmt.Print(1)
+		case <-ctx.Done():
+			fmt.Print(2)
+		}
+	}()
+	time.Sleep(time.Second * 3)
+}
+

crypto

https://pkg.go.dev/crypto

import "crypto"
+

crypto 包,包含了很多加密算法

crypto/aes

https://pkg.go.dev/crypto/aes

import "crypto/aes"
+

提供了 aes 加密算法的加密过程。此算法为对称加密算法

  • 创建一个密钥

crypto/cipher

https://pkg.go.dev/crypto/cipher

import "crypto/cipher"
+

crypto/des

https://pkg.go.dev/crypto/des

import "crypto/des"
+

crypto/dsa

https://pkg.go.dev/crypto/dsa

import "crypto/dsa"
+

crypto/ecdh

https://pkg.go.dev/crypto/ecdh

import "crypto/ecdh"
+

crypto/ecdsa

https://pkg.go.dev/crypto/ecdsa

import "crypto/ecdsa"
+

crypto/ed25519

https://pkg.go.dev/crypto/ed25519

import "crypto/ed25519"
+

crypto/elliptic

https://pkg.go.dev/crypto/elliptic

import "crypto/elliptic"
+

crypto/hmac

https://pkg.go.dev/crypto/hmac

import "crypto/hmac"
+

crypto/md5

https://pkg.go.dev/crypto/md5

import "crypto/md5"
+

crypto/rand

https://pkg.go.dev/crypto/rand

import "crypto/rand"
+

crypto/rc4

https://pkg.go.dev/crypto/rc4

import "crypto/rc4"
+

crypto/rsa

https://pkg.go.dev/crypto/rsa

import "crypto/rsa"
+

crypto/sha1

https://pkg.go.dev/crypto/sha1

import "crypto/sha1"
+

crypto/sha256

https://pkg.go.dev/crypto/sha256

import "crypto/sha256"
+

crypto/sha512

https://pkg.go.dev/crypto/sha512

import "crypto/sha512"
+

crypto/subtle

https://pkg.go.dev/crypto/subtle

import "crypto/subtle"
+

crypto/tls

https://pkg.go.dev/crypto/tls

import "crypto/tls"
+

crypto/x509

https://pkg.go.dev/crypto/x509

import "crypto/x509"
+

crypto/x509/pkix

https://pkg.go.dev/crypto/x509/pkix

import "crypto/x509/pkix"
+

database

https://pkg.go.dev/database +此包不可直接使用,它包含了子包,这个子包是处理 sql 的统一接口,并不提供实际的实现

database/sql

https://pkg.go.dev/database/sql

import _ "database/sql"
+

当我们使用 MySQL,Redis 等数据库的时候,通常要使用上述的方式引入这个包,此包提供了 SQL 操作的接口。

使用此包必须跟第三方的数据库驱动结合,比如下面这种操作:

import(
+_ "database/sql"
+  "xx/mysql"
+) 
+

database/sql/driver

https://pkg.go.dev/database/sql/driver

import "database/sql/driver"
+

driver 包,包含了数据库 driver 的接口,要想实现某个数据库的驱动 (driver) 就必须引入此包,实现此包定义接口的具体内容。

在被用户使用的时候,引入第三方数据库驱动,加上引入 database/sql 这个 SQL 操作包,就可以实现正常的 SQL 操作。

debug

https://pkg.go.dev/debug

不提供 debug 包本身,debug 包含了众多子包,都是跟调试相关。

debug/buildinfo

https://pkg.go.dev/debug/buildinfo +不直接使用此包,此包提供二进制的功能,由 runtime/debug 来调用。

debug/dwarf

https://pkg.go.dev/debug/dwarf

import "debug/dwarf"
+

用于解析 DWARF 调试信息,DWARF 调试信息包含了程序的源代码、变量、函数、类型等相关信息,可以帮助调试器进行源代码级别的调试。

debug/elf

https://pkg.go.dev/debug/elf

import "debug/elf"
+

用于解析 ELF 可执行文件格式,

debug/gosym

https://pkg.go.dev/debug/gosym

import "debug/gosym"
+

debug/gosym 用于解析 Go 语言程序的符号表信息。

debug/macho

https://pkg.go.dev/debug/macho

import "debug/macho"
+

debug/macho 用于解析 Mach-O 格式的可执行文件

debug/pe

https://pkg.go.dev/debug/pe

import "debug/pe"
+

debug/pe 用于解析 PE 格式的可执行文件

debug/plan9obj

https://pkg.go.dev/debug/plan9obj

import "debug/plan9obj"
+

debug/plan9obj 用于解析 Plan 9 object 文件格式。

embed

https://pkg.go.dev/embed

import _ "embed"
+
//go:embed hello.text
+

mbed 是 Go 语言自 1.16 版本引入的一个标准库,用于将静态文件嵌入到 Go 代码中。

通过 embed 包,我们可以将静态文件 (如文本文件、JSON 文件、HTML 文件、图像文件等) 直接嵌入到 Go 代码中,而无需将文件作为独立的资源文件放在磁盘上。这样做的好处是,可以将所有的资源文件打包到可执行文件中,方便分发和部署。

将一个文件嵌入到一个 string 中

package main
+
+import _ "embed"
+
+// 这里的注释开头没有空格,这是 go 的自有命令注释;//go:xx 
+//
+//go:embed hello.txt
+var s string
+
+func main() {
+	print(s)
+}
+

将一个文件嵌入到文件系统中

package main
+
+import (
+	"embed"
+	"fmt"
+)
+
+//go:embed hello.txt
+var helloFile embed.FS
+
+func main() {
+	content, err := helloFile.ReadFile("hello.txt")
+	if err != nil {
+		fmt.Println("无法读取文件:", err)
+		return
+	}
+
+	fmt.Println(string(content))
+}
+

encoding

https://pkg.go.dev/encoding +使用时,不直接使用 encoding,除非你想亲自实现某个编码的加解码

encoding 是 Go 语言标准库中的一个包,用于处理数据的编码和解码。

encoding 包提供了许多子包,每个子包都专门用于处理特定的数据编码格式

  • encoding/json:用于处理 JSON 数据的编码和解码。
  • encoding/xml:用于处理 XML 数据的编码和解码。
  • encoding/csv:用于处理 CSV (逗号分隔值) 数据的编码和解码。
  • encoding/base64:用于进行 base64 编码和解码。
  • encoding/hex:用于进行十六进制编码和解码。
  • encoding/gob:用于进行 Go 对象的编码和解码。

encoding/ascii85

https://pkg.go.dev/encoding/ascii85

import "encoding/ascii85"
+

encoding/ascii85 用于进行 ASCII85 编码和解码

encoding/asn1

https://pkg.go.dev/encoding/asn1

import "encoding/asn1"
+

encoding/asn1 用于进行 ASN.1 (Abstract Syntax Notation One) 编码和解码。

encoding/base32

https://pkg.go.dev/encoding/base32

import "encoding/base32"
+

encoding/base64

https://pkg.go.dev/encoding/base64

import "encoding/base64"
+

encoding/base32 用于进行 Base32 编码和解码

encoding/binary

https://pkg.go.dev/encoding/binary

import "encoding/binary"
+

encoding/base64 用于进行 Base64 编码和解码。

encoding/csv

https://pkg.go.dev/encoding/csv

import "encoding/csv"
+

encoding/csv 用于处理 CSV (逗号分隔值) 格式的数据。

encoding/gob

https://pkg.go.dev/encoding/gob

import "encoding/gob"
+

encoding/gob 用于将 Go 的值编码为二进制格式,并进行序列化和反序列化。

encoding/hex

https://pkg.go.dev/encoding/hex

import "encoding/hex"
+

十六进制编码将二进制数据编码为十六进制字符串,每个字节对应两个十六进制字符。十六进制编码通常用于将二进制数据转换为可打印的 ASCII 字符串,例如在 URL 参数中传递二进制数据或在文本文件中嵌入二进制数据。

encoding/hex 包提供了一组函数和类型,用于进行十六进制编码和解码。主要有两个函数:EncodeToString 和 DecodeString。

encoding/json

https://pkg.go.dev/encoding/json

import "encoding/json"
+

encoding/json 用于对 JSON (JavaScript Object Notation) 格式的数据进行编码和解码操作。

JSON 是一种常用的数据交换格式,用于在不同平台和编程语言之间传输和存储数据。JSON 数据由键值对组成,可以表示复杂的数据结构和层次关系。

encoding/pem

https://pkg.go.dev/encoding/pem

import "encoding/pem"
+

encoding/pem 用于对 PEM (Privacy-Enhanced Mail) 格式的数据进行编码和解码操作。

PEM 是一种常用的文本格式,用于在非文本环境中传输和存储密钥、证书等数据。PEM 格式使用 ASCII 字符表示二进制数据,通常以 “-----BEGIN…” 和 “-----END…” 标记来标识不同类型的数据。

encoding/xml

https://pkg.go.dev/encoding/xml

import "encoding/xml"
+

encoding/xml 用于对 XML (eXtensible Markup Language) 格式的数据进行编码和解码操作。

XML 是一种常用的文本格式,用于存储和传输结构化的数据。XML 数据由标签、属性和文本内容组成,可以表示复杂的数据结构和层次关系。

errors

expvar

flag

fmt

go

go/ast

go/build

go/build/constraint

go/constant

go/doc

go/doc/comment

go/format

go/importer

go/parser

go/printer

go/scanner

go/token

go/types

hash

hash/adler32

hash/crc32

hash/crc64

hash/fnv

hash/maphash

html

html/template

image

image/color

image/color/palette

image/draw

image/gif

image/jpeg

png

index

index/suffixarray

io

io/fs

io/ioutil

log

log/slog

log/syslog

maps

math

math/big

math/bits

math/cmplx

math/rand

mime

mime/multipart

mime/quotedprintable

net

net/http

net/http/cgi

net/http/cookiejar

net/http/fcgi

net/http/httptest

net/http/httptrace

net/http/httputil

net/http/pprof

net/mail

net/netip

net/rpc

net/rpc/jsonrpc

net/smtp

net/textproto

net/url

os

os/exec

os/signal

os/user

path

path/filepath

plugin

reflect

regexp

regexp/syntax

runtime

runtime/cgo

runtime/coverage

runtime/debug

runtime/metrics

runtime/pprof

runtime/race

runtime/trace

slices

sort

strconv

strings

sync

sync/atomic

syscall

sync/js

testing

testing/fstest

testing/iotest

testing/quick

testing/slogtest

text

text/scanner

text/tabwriter

text/template

template/parse

time

time/tzdata

unicode

unicode/utf16

unicode/utf8

unsafe

+ + + diff --git "a/\345\267\245\347\250\213/go\346\240\207\345\207\206\345\272\223/sync.html" "b/\345\267\245\347\250\213/go\346\240\207\345\207\206\345\272\223/sync.html" new file mode 100644 index 000000000..cf9f72ce3 --- /dev/null +++ "b/\345\267\245\347\250\213/go\346\240\207\345\207\206\345\272\223/sync.html" @@ -0,0 +1,64 @@ + + + + + + sync | GOFamily - go 程序员宝典 + + + + + + + + +

sync

sync.Map

sync.Map 的底层数据

sync.Map 适合在读多写少的场景下使用,sync.Map 的核心思想是读写分离,以及用空间换时间。

有两个数据结构:

dirty 数据结构

type Map struct {
+  // 互斥锁
+    mu Mutex
+  // 原子操作
+    read atomic.Value
+    // dirty数据 
+    dirty map[interface{}]*entry
+    // 标记tag 表示数据每次从diry中往read中迁移一个就+1
+    misses int
+}
+

read 数据结构

type readOnly struct {
+    m       map[interface{}]*entry
+    // 标记:false 证明数据不只是read中有,dirty也有
+    amended bool 
+

读数据是优先从 read 中读 (注意,read 是只读的数据结构,所以不加锁),如果 read 不包含这个数据,会从 dirty 中读取 (注意,从 dirty 查找的时候会加锁),并 misses+1,直到 misses 大于 dirty,开始迁移数据,数据一次性从 dirty 传到 read 中。

因为 read 没有锁,所以才说,sync.Map 读的时候效率高。

当新增的时候,从 read 和 dirty 中查找是否用数据,在 read 有数据且没有被标记为清除就无锁覆盖原来的值。这个时候开始加锁了,如果在 read 中虽然有数据,但是被标记为清除了,并且 dirty 没有数据,就加入到 diry 中,如果这个时候 read 有数据,虽然被被标记为删除了,dirty 也有数据,那么直接原子更新数据即可。如果数据不在 read,在 dirty 中,直接在 dirty 中更新即可,如果数据不在 read 中也不再 dirty 中,如果这个时候 read.amended 是 false (意思就是 dirty 空了),那么就必须将数据一点一点从 read 中复制到 dirty 中,如果是 true 就不用复制了,在完成这个工序后,再把数据放置在 dirty 中。

删除的时候,就先从 read 中查找,有了就原子性质的将其标记为 nil,并不是真的删,没有就加锁从 dirty 中找,有了就删。这个时候是真删,如果都没有就返回。

在写多读少的时候,涉及到 read 中无数据就会频繁的加锁去 dirty 中查找、read 和 dirty 交换等开销,比常规的 map 加锁性能更差。因为普通的只是加锁,sync.Map 不仅仅是加锁,还得复制数据,这花销就更大了。

总结:高并发,读多写少可以使用 sync.Map,如果读少写多,不要使用 sync.Map,比常规锁+map 更慢

参考资料

  • https://zhuanlan.zhihu.com/p/353440086
+ + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/index.html" new file mode 100644 index 000000000..1f0e2e1c4 --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

go 编程范式

创建模式

结构模式

行为模式

同步模式

并发模式

消息传递模式

稳定性模式

分析模式

函数式编程模式

参考资料

  • https://github.com/shgopher/go-patterns
  • https://time.geekbang.org/column/article/386238
  • https://mp.weixin.qq.com/s/Sv-SXDYxea_K_TtiI_CjTw
  • https://mp.weixin.qq.com/s/KRgNwJt1C7q2ckeqCu9pCQ
+ + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/k8s_visitor/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/k8s_visitor/index.html" new file mode 100644 index 000000000..58efb252a --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/k8s_visitor/index.html" @@ -0,0 +1,50 @@ + + + + + + k8s visitor | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/pipeline/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/pipeline/index.html" new file mode 100644 index 000000000..6a785dc8e --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/pipeline/index.html" @@ -0,0 +1,50 @@ + + + + + + pipeline | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\344\273\243\347\220\206/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\344\273\243\347\220\206/index.html" new file mode 100644 index 000000000..1e6050c2c --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\344\273\243\347\220\206/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\344\277\241\345\217\267\351\207\217/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\344\277\241\345\217\267\351\207\217/index.html" new file mode 100644 index 000000000..4f59e6db2 --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\344\277\241\345\217\267\351\207\217/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\210\233\345\273\272\350\200\205/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\210\233\345\273\272\350\200\205/index.html" new file mode 100644 index 000000000..b8bc40ff8 --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\210\233\345\273\272\350\200\205/index.html" @@ -0,0 +1,50 @@ + + + + + + 创建者模式 | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\212\237\350\203\275\351\200\211\351\241\271/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\212\237\350\203\275\351\200\211\351\241\271/index.html" new file mode 100644 index 000000000..0e9c38a76 --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\212\237\350\203\275\351\200\211\351\241\271/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\215\225\344\276\213/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\215\225\344\276\213/index.html" new file mode 100644 index 000000000..c6761e0d3 --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\215\225\344\276\213/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\216\237\345\236\213/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\216\237\345\236\213/index.html" new file mode 100644 index 000000000..b64bf7d71 --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\216\237\345\236\213/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\217\221\345\270\203\350\200\205\350\256\242\351\230\205\350\200\205/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\217\221\345\270\203\350\200\205\350\256\242\351\230\205\350\200\205/index.html" new file mode 100644 index 000000000..92e9a05e2 --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\217\221\345\270\203\350\200\205\350\256\242\351\230\205\350\200\205/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\256\232\346\227\266\345\207\275\346\225\260/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\256\232\346\227\266\345\207\275\346\225\260/index.html" new file mode 100644 index 000000000..01df1ec77 --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\256\232\346\227\266\345\207\275\346\225\260/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\257\271\350\261\241\346\261\240/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\257\271\350\261\241\346\261\240/index.html" new file mode 100644 index 000000000..982c46897 --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\257\271\350\261\241\346\261\240/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\267\245\345\216\202\346\226\271\346\263\225/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\267\245\345\216\202\346\226\271\346\263\225/index.html" new file mode 100644 index 000000000..10ccbfc43 --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\267\245\345\216\202\346\226\271\346\263\225/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\271\266\350\241\214/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\271\266\350\241\214/index.html" new file mode 100644 index 000000000..f32a80460 --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\345\271\266\350\241\214/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\346\211\207\345\205\245/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\346\211\207\345\205\245/index.html" new file mode 100644 index 000000000..edf8ea873 --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\346\211\207\345\205\245/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\346\211\207\345\207\272/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\346\211\207\345\207\272/index.html" new file mode 100644 index 000000000..ef1f6e0b6 --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\346\211\207\345\207\272/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\346\226\255\350\267\257\345\231\250/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\346\226\255\350\267\257\345\231\250/index.html" new file mode 100644 index 000000000..52801a131 --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\346\226\255\350\267\257\345\231\250/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\346\234\211\347\225\214\345\271\266\350\241\214/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\346\234\211\347\225\214\345\271\266\350\241\214/index.html" new file mode 100644 index 000000000..5f5bbaae8 --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\346\234\211\347\225\214\345\271\266\350\241\214/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\347\224\237\346\210\220\345\231\250/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\347\224\237\346\210\220\345\231\250/index.html" new file mode 100644 index 000000000..d97a0d8f0 --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\347\224\237\346\210\220\345\231\250/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\347\255\226\347\225\245/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\347\255\226\347\225\245/index.html" new file mode 100644 index 000000000..b5192f3bf --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\347\255\226\347\225\245/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\350\243\205\351\245\260\345\231\250/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\350\243\205\351\245\260\345\231\250/index.html" new file mode 100644 index 000000000..1e83b691d --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\350\243\205\351\245\260\345\231\250/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\350\247\202\345\257\237\350\200\205/index.html" "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\350\247\202\345\257\237\350\200\205/index.html" new file mode 100644 index 000000000..c64622e1a --- /dev/null +++ "b/\345\267\245\347\250\213/go\347\274\226\347\250\213\350\214\203\345\274\217/\350\247\202\345\257\237\350\200\205/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\350\257\255\350\250\200\350\247\204\350\214\203/index.html" "b/\345\267\245\347\250\213/go\350\257\255\350\250\200\350\247\204\350\214\203/index.html" new file mode 100644 index 000000000..776f8cb3e --- /dev/null +++ "b/\345\267\245\347\250\213/go\350\257\255\350\250\200\350\247\204\350\214\203/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/architect.html" "b/\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/architect.html" new file mode 100644 index 000000000..eef0b17da --- /dev/null +++ "b/\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/architect.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/index.html" "b/\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/index.html" new file mode 100644 index 000000000..a87fa2cad --- /dev/null +++ "b/\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/index.html" @@ -0,0 +1,50 @@ + + + + + + go 面试题 | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/intermediate.html" "b/\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/intermediate.html" new file mode 100644 index 000000000..8e41ee9bc --- /dev/null +++ "b/\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/intermediate.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/primary.html" "b/\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/primary.html" new file mode 100644 index 000000000..d34adff66 --- /dev/null +++ "b/\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/primary.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/senior.html" "b/\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/senior.html" new file mode 100644 index 000000000..75f52a0bd --- /dev/null +++ "b/\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/senior.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/\344\270\255\351\253\230\347\272\247go\351\235\242\350\257\225\351\242\230\344\270\200.html" "b/\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/\344\270\255\351\253\230\347\272\247go\351\235\242\350\257\225\351\242\230\344\270\200.html" new file mode 100644 index 000000000..cb1b8ab24 --- /dev/null +++ "b/\345\267\245\347\250\213/go\351\235\242\350\257\225\351\242\230/\344\270\255\351\253\230\347\272\247go\351\235\242\350\257\225\351\242\230\344\270\200.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +
  1. 自我介绍
  2. 代码效率分析,考察局部性原理
  3. 多核 CPU 场景下,cache 如何保持一致、不冲突?
  4. uint 类型溢出
  5. 介绍 rune 类型
  6. 编程题:3 个函数分别打印 cat、dog、fish,要求每个函数都要起一个 goroutine,按照 cat、dog、fish 顺序打印在屏幕上 100 次。
  7. 介绍一下 channel,无缓冲和有缓冲区别
  8. 是否了解 channel 底层实现,比如实现 channel 的数据结构是什么?
  9. channel 是否线程安全?
  10. Mutex 是悲观锁还是乐观锁?悲观锁、乐观锁是什么?
  11. Mutex 几种模式?
  12. Mutex 可以做自旋锁吗?
  13. 介绍一下 RWMutex
  14. 项目中用过的锁?
  15. 介绍一下线程安全的共享内存方式
  16. 介绍一下 goroutine
  17. goroutine 自旋占用 cpu 如何解决 (go 调用、gmp)
  18. 介绍 linux 系统信号
  19. goroutine 抢占时机 (gc 栈扫描)
  20. Gc 触发时机
  21. 是否了解其他 gc 机制
  22. Go 内存管理方式
  23. Channel 分配在栈上还是堆上?哪些对象分配在堆上,哪些对象分配在栈上?
  24. 介绍一下大对象小对象,为什么小对象多了会造成 gc 压力?
  25. 项目中遇到的 oom 情况?
  26. 项目中使用 go 遇到的坑?
  27. 工作遇到的难题、有挑战的事情,如何解决?
  28. 如何指定指令执行顺序?
+ + + diff --git "a/\345\267\245\347\250\213/index.html" "b/\345\267\245\347\250\213/index.html" new file mode 100644 index 000000000..6b3aab382 --- /dev/null +++ "b/\345\267\245\347\250\213/index.html" @@ -0,0 +1,50 @@ + + + + + + 工程 | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/ioc/index.html" "b/\345\267\245\347\250\213/ioc/index.html" new file mode 100644 index 000000000..d2d1373dd --- /dev/null +++ "b/\345\267\245\347\250\213/ioc/index.html" @@ -0,0 +1,50 @@ + + + + + + IOC | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/log/index.html" "b/\345\267\245\347\250\213/log/index.html" new file mode 100644 index 000000000..cf4813974 --- /dev/null +++ "b/\345\267\245\347\250\213/log/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/wasm/index.html" "b/\345\267\245\347\250\213/wasm/index.html" new file mode 100644 index 000000000..f24f47e55 --- /dev/null +++ "b/\345\267\245\347\250\213/wasm/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/\344\273\243\347\240\201\346\243\200\346\237\245/index.html" "b/\345\267\245\347\250\213/\344\273\243\347\240\201\346\243\200\346\237\245/index.html" new file mode 100644 index 000000000..1fd7c6790 --- /dev/null +++ "b/\345\267\245\347\250\213/\344\273\243\347\240\201\346\243\200\346\237\245/index.html" @@ -0,0 +1,133 @@ + + + + + + 代码检查工具 | GOFamily - go 程序员宝典 + + + + + + + + +

代码检查工具

代码检查通常会被配置再 CI 中,用于自动检查代码的质量,本次我们介绍三个用于代码检查的工具

  • go vet / go tool vet
  • golangci-lint
  • govulncheck

go vet / go tool vet

go vet 命令是 go tool vet 的简单封装,go vet 实际上还是需要调用 go tool vet 才能完成工作,这俩命令的主要目的就是为了基础的代码检查。不过这个命令只能做简单的检查,下面我们介绍一下更常用的工具。

golangci-lint

首先在介绍 golangci-lint 之前我们先下载它,它是一个 go 语言写的可执行文件,使用

go install  github.com/golangci/golangci-lint/cmd/golangci-lint@latest 
+

即可下载到本地的~/go/bin/ 目录,这里专门存储使用 go install 下载的使用 go 写的可执行文件,记得将这个路径加入 PATH。

通过在要检查的项目中设置配置文件,用来配置 lint 工具的选项,使用 .golangci.yaml 即可

例如:


+run:
+  skip-dirs: # 设置要忽略的目录
+    - util
+    - .*~
+    - api/swagger/docs
+  skip-files: # 设置不需要检查的go源码文件,支持正则匹配,这里建议包括:_test.go
+    - ".*\\.my\\.go$"
+    - _test.go
+linters-settings:
+  errcheck:
+    check-type-assertions: true # 这里建议设置为true,如果确实不需要检查,可以写成`num, _ := strconv.Atoi(numStr)`
+    check-blank: false
+  gci:
+    # 将以`github.com/marmotedu/iam`开头的包放在第三方包后面
+    local-prefixes: github.com/marmotedu/iam
+  godox:
+    keywords: # 建议设置为BUG、FIXME、OPTIMIZE、HACK
+      - BUG
+      - FIXME
+      - OPTIMIZE
+      - HACK
+  goimports:
+    # 设置哪些包放在第三方包后面,可以设置多个包,逗号隔开
+    local-prefixes: github.com/marmotedu/iam
+  gomoddirectives: # 设置允许在go.mod中replace的包
+    replace-local: true
+    replace-allow-list:
+      - github.com/coreos/etcd
+      - google.golang.org/grpc
+      - github.com/marmotedu/api
+      - github.com/marmotedu/component-base
+      - github.com/marmotedu/marmotedu-sdk-go
+  gomodguard: # 下面是根据需要选择可以使用的包和版本,建议设置
+    allowed:
+      modules:
+        - gorm.io/gorm
+        - gorm.io/driver/mysql
+        - k8s.io/klog
+      domains: # List of allowed module domains
+        - google.golang.org
+        - gopkg.in
+        - golang.org
+        - github.com
+        - go.uber.org
+    blocked:
+      modules:
+        - github.com/pkg/errors:
+            recommendations:
+              - github.com/marmotedu/errors
+            reason: "`github.com/marmotedu/errors` is the log package used by marmotedu projects."
+      versions:
+        - github.com/MakeNowJust/heredoc:
+            version: "> 2.0.9"
+            reason: "use the latest version"
+      local_replace_directives: false
+  lll:
+    line-length: 240 # 这里可以设置为240,240一般是够用的
+  importas: # 设置包的alias,根据需要设置
+    jwt: github.com/appleboy/gin-jwt/v2         
+    metav1: github.com/marmotedu/component-base/pkg/meta/v1
+
+

使用 golangci-lint run (等于 golangci-lint run ./... 意思就是把所有的包,子包,遍历完全) 你会得到一个类似:


+collie.go:171:41: composites: image/jpeg.Options struct literal uses unkeyed fields (govet)
+				if err := jpeg.Encode(file, i.img, &jpeg.Options{q}); err != nil {
+				                                    ^
+collie.go:241:2: printf: `fmt.Println` arg list ends with redundant newline (govet)
+	fmt.Println("声明:本程序来自GitHub:shgopher,欢迎关注公众号:科科人神;\n免费软件,如果使用期间出现任何后果,本软件不承担任何责任谢谢\n")
+	^
+collie.go:244:2: printf: `fmt.Println` arg list ends with redundant newline (govet)
+	fmt.Println("运行结束 ☕️ ☕ ☕\n")
+	^
+collie.go:162:5: SA9001: defers in this range loop won't run unless the channel gets closed (staticcheck)
+				defer file.Close()
+				^
+

这样的结果,这样你就会发现是哪个配置的 linter 发出的警告,以及是什么样子的警告。

govulncheck

go 官方维护了一个 https://vuln.go.dev/ 的漏洞库,我们可以使用 go install golang.org/x/vuln/cmd/govulncheck@latest 的方式,下载目前 (go 1.19) 还在测试阶段的这一功能,govulncheck 将会是一个独立的工具,并且 go 在 https://pkg.go.dev/golang.org/x/vuln/vulncheck 还提供了相关功能的 API,可以更灵活的去使用这个功能,目前已知的即将推出的功能分别是提供 vscode 插件,以及将此功能集成在 pkg.go.dev 这个 go 包的集合地,也就是说只要被收录在这个网站的 go 包都将自动接受漏洞检查,另外,go 以后可能还会将这个功能直接集成在例如 go build 这种常用命令上。

  • 下载 govulncheck go install golang.org/x/vuln/cmd/govulncheck@latest
  • 在一个拥有 go.mod 的目录下,使用 govulncheck 跟上一个有 go 文件的路径,例如:govulncheck ./pkg/watcher

只需要这样简单的设置就可以去检查代码中存在的风险和漏洞,govulncheck 就会打印出这样的信息:

Vulnerability #2: GO-2022-0493
+  When called with a non-zero flags parameter, the Faccessat
+  function can incorrectly report that a file is accessible.
+  Found in: golang.org/x/sys/unix@v0.0.0-20211020064051-0ec99a608a1b
+  Fixed in: golang.org/x/sys/unix@v0.0.0-20220412211240-33da011f77ad
+  More info: https://pkg.go.dev/vuln/GO-2022-0493
+

信息中包括了你引用的某些包出现的一些漏洞,在 fix 中有修复的信息,可以把你引用的包进行一个升级。

x/tools 工具系列

https://pkg.go.dev/golang.org/x/tools#section-readme

比如检测变量 shadow 的工具

go install golang.org/x/tools/go/analysis/passes/shadow/cmd/shadow
+

参考资料

  • https://go.dev/blog/vuln
  • https://time.geekbang.org/column/article/390401
+ + + diff --git "a/\345\267\245\347\250\213/\344\274\230\347\247\200\347\254\254\344\270\211\346\226\271\345\214\205/gin/index.html" "b/\345\267\245\347\250\213/\344\274\230\347\247\200\347\254\254\344\270\211\346\226\271\345\214\205/gin/index.html" new file mode 100644 index 000000000..f92d5a333 --- /dev/null +++ "b/\345\267\245\347\250\213/\344\274\230\347\247\200\347\254\254\344\270\211\346\226\271\345\214\205/gin/index.html" @@ -0,0 +1,50 @@ + + + + + + gin | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/\344\274\230\347\247\200\347\254\254\344\270\211\346\226\271\345\214\205/index.html" "b/\345\267\245\347\250\213/\344\274\230\347\247\200\347\254\254\344\270\211\346\226\271\345\214\205/index.html" new file mode 100644 index 000000000..500fe4c3c --- /dev/null +++ "b/\345\267\245\347\250\213/\344\274\230\347\247\200\347\254\254\344\270\211\346\226\271\345\214\205/index.html" @@ -0,0 +1,50 @@ + + + + + + 优秀第三方包 | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/\344\274\230\347\247\200\347\254\254\344\270\211\346\226\271\345\214\205/sonic/index.html" "b/\345\267\245\347\250\213/\344\274\230\347\247\200\347\254\254\344\270\211\346\226\271\345\214\205/sonic/index.html" new file mode 100644 index 000000000..dedca23c3 --- /dev/null +++ "b/\345\267\245\347\250\213/\344\274\230\347\247\200\347\254\254\344\270\211\346\226\271\345\214\205/sonic/index.html" @@ -0,0 +1,50 @@ + + + + + + sonic | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/\345\206\205\345\255\230\345\257\271\351\275\220\345\256\236\350\267\265/index.html" "b/\345\267\245\347\250\213/\345\206\205\345\255\230\345\257\271\351\275\220\345\256\236\350\267\265/index.html" new file mode 100644 index 000000000..8d19c2cd3 --- /dev/null +++ "b/\345\267\245\347\250\213/\345\206\205\345\255\230\345\257\271\351\275\220\345\256\236\350\267\265/index.html" @@ -0,0 +1,139 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

内存对齐实践

type Person struct {
+  Sex bool // 性别
+  Height uint16 // 身高
+  Addr byte
+  Postion byte
+  age int64
+  weight uint16
+}
+

我们分别算一下数据实际的大小和在内存中的的偏移量以及整个结构体因为内存对齐所一共占有的内存数据

实际大小

type Person struct {
+  Sex bool //1
+  Height uint16 //2
+  Addr byte //1
+  Postion byte //1
+  age int64 //8
+  weight uint16//2
+}
+

偏移量

type Person struct {
+  Sex bool // 0
+  Height uint16 // 2
+  Addr byte//4
+  Postion byte // 5
+  age int64 // 8
+  weight uint16 // 16
+}
+

实际在内存中的排序:

sex * height height addr postion * * age age age age age age age age weight weight 
+

在 Go 语言中,每种数据类型在内存中的字节对齐方式是不同的,主要遵循以下规则:

  • 每个变量都会占用一块内存区域,并从这块区域的起始地址开始存储值。
  • 结构体的起始地址必须是其最大字段的对齐值的倍数。
  • 每个字段的起始地址必须是其类型对齐值的倍数。
  • 内存对齐后的结构体大小必须是其最大字段的对齐值的倍数。
  • 如果存在内存对齐的需求,编译器会在具体字段之间插入对齐字节 (padding)。

我们都知道取内存地址的时候是通过下标去获取的,所以当 height 位于 sex 后面,但是 sex 只占有一个字节,但是 height 有两个字节的时候,如果要精确获取 height 的起始位置,那么必须要按照 uint16 的大小的倍数去获取,也就是 2 的倍数,那么 sex 后面肯定要跟一个空值才行,所以 height 的偏移量就是 2

position 也是一样,它的大小是 8,那么要获取它的位置,至少是 8 的倍数才行,所以前面要补充两个空值

具体的每个字段的偏移量具体分析如下:

  • Sex bool 占 1 字节,起始偏移为 0
  • Height uint16 需要以 2 字节对齐,所以从偏移 2 开始
  • Addr byte 占 1 字节,但前面已插入 1 字节 padding,所以从偏移 4 开始
  • Position byte 紧接着 Addr 的下一字节,所以偏移 5
  • age int64 需要以 8 字节对齐,所以从偏移 8 开始
  • weight uint16 需要以 2 字节对齐,并且要跟在 age 之后,所以从偏移 16 开始

也就是说整个的结构体按照空值来算一共是 18 个字节的占有量,那么整个结构体是多少呢?

结构体的大小计算方法是最大值的倍数,这里就是 age 的 8 的倍数,必须要大于实际值 18,所以这里取 24

下面对这个结构体进行优化

顺序优化去除 padding

type Person struct {
+  Sex bool // 0
+  Addr byte//1
+  Postion byte // 2
+  Height uint16 // 4
+  weight uint16 // 6
+  age int64 // 8
+}
+

现在我们看一下具体的内存排布

sex addr postion * height height weight weight age age age age age age age age
+

可以看到这个时候的一共需要的数据只有 16,刚好结构体的大小是 8 的倍数,那么这个时候结构体的大小就变成了 16 字节

数据类型调整

我们可以使用更小的类型去承载数据,比如使用 uint8 去替代 uint16,和 int64

type Person struct {
+  Sex bool // 0
+  Addr byte//1
+  Postion byte // 2
+  Height uint8 // 3
+  weight uint8 // 4
+  age uint8 // 5
+}
+

整个结构体的大小就变成了 6 个字节

测试优化顺序的结构体大小

package main
+
+import (
+	"fmt"
+	"unsafe"
+)
+
+// 原始结构体
+type OriginalPerson struct {
+	Sex     bool   // 0  offset
+	Height  uint16 // 2
+	Addr    byte   // 4
+	Postion byte   // 5
+	Age     int64  // 8
+	Weight  uint16 // 16
+}
+
+// 优化后的结构体
+type OptimizedPerson struct {
+	Sex     bool   // 0 offset
+	Addr    byte   //1
+	Postion byte   // 2
+	Height  uint16 // 4
+	weight  uint16 // 6
+	age     int64  // 8
+}
+
+type OptimizedPerson1 struct {
+	Sex     bool  // 0
+	Addr    byte  //1
+	Postion byte  // 2
+	Height  uint8 // 3
+	weight  uint8 // 4
+	age     uint8 // 5
+}
+
+func main() {
+	// 测试原始结构体大小
+	fmt.Println("原始结构体大小:", unsafe.Sizeof(OriginalPerson{}))
+	// 测试优化后结构体大小
+	fmt.Println("优化后结构体大小:", unsafe.Sizeof(OptimizedPerson{}))
+	// 测试优化后结构体大小1
+	fmt.Println("优化后结构体大小:", unsafe.Sizeof(OptimizedPerson1{}))
+}
+
原始结构体大小: 24
+优化后结构体大小: 16
+优化后结构体大小1: 6
+
+ + + diff --git "a/\345\267\245\347\250\213/\345\212\250\346\200\201\350\260\203\350\257\225/index.html" "b/\345\267\245\347\250\213/\345\212\250\346\200\201\350\260\203\350\257\225/index.html" new file mode 100644 index 000000000..efc321d68 --- /dev/null +++ "b/\345\267\245\347\250\213/\345\212\250\346\200\201\350\260\203\350\257\225/index.html" @@ -0,0 +1,50 @@ + + + + + + 动态调试 | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/\345\214\205\345\217\212\345\205\266\346\236\204\345\273\272\345\267\245\345\205\267/index.html" "b/\345\267\245\347\250\213/\345\214\205\345\217\212\345\205\266\346\236\204\345\273\272\345\267\245\345\205\267/index.html" new file mode 100644 index 000000000..bc3267fd5 --- /dev/null +++ "b/\345\267\245\347\250\213/\345\214\205\345\217\212\345\205\266\346\236\204\345\273\272\345\267\245\345\205\267/index.html" @@ -0,0 +1,275 @@ + + + + + + go 包管理工具 | GOFamily - go 程序员宝典 + + + + + + + + +

go 包管理工具

  • go 导包的过程
  • go 包的版本管理
  • go 包的最小版本原则
  • go module 命令的使用 +
    • go get / go install
    • GOPROXY
    • GOSUMDB
    • go 使用私有服务器
    • workspace

go 语言的包导入过程

  • go 编译快的原因
  • go 程序的构建过程

go 程序编译快的原因。1 是因为 go 要求每一个包都必须显式的标记处引入的包。

package hi
+
+import (
+  "fmt"
+  "io"
+
+  "github.com/shgopher/hui"
+)
+

所以编译器无需去查找这个文件中具体引入了哪些包就可以在头部检索出全部的导包名单

2 是因为 go 语言不允许循环引用,比如说 a 引用了 b,b 又引入了 a,所以每一个包都是独立的存在,处于 “有向无环图 “这种格式下,进而可以并行去编译不同的包,提高编译效率。

3 包编译的结果里 (xx.oxx.a) 中不仅存储了此包的导出信息,还存储了它引入的包的导出信息,这样编译器只需要读取一个目标文件就可以获取全部的包信息,不需要读取全部的文件。比如 p 包要引入 q 包,那么 q 包的。a 文件已经包括了它引入的全部包的导出信息,这样编译器可以快速统计某一个包被引用的情况,比如包引用了 a b,a b 分别引用了 c 但是版本不同,那么这种情况下因为主包已经了解了他们的情况,所以就可以用最小版本原则去选择一个版本的包进行编译即可。有点类似分封制,我帝王 p 包,我的直接负责人是诸侯 q,q 掌握了它下级 s 的所有数据,假设 s 还有下级,s 掌握了 w 的所有数据,然后层层回报,当帝王 p 最终编译的时候,它已经完全拥有了所有的数据,可以精准调控。再结合每一个包都可以独立的并行编译

所以这一整套下来 go 的编译速度就会很快。并且在 go.mod 文件中也会详细记录使用过的包及其版本,那么并行下载,快速编译就不是梦了。

go 程序的构建过程。

go 程序的建立是由编译和链接两个阶段组成的,举个例子 +有一个项目拥有一个 main 包,一个 lib 包,lib 包会被引入到 main 包中,那么编译的过程是这样的,首先创建一个临时工作区域,编译 lib 包为 lib.a,编译 main 包为 main.a,链接器将 lib.a 和 main.a 链接成 name.out,然后改名为 name (unix-like),使用第三方包的意思就是链接了该包的源代码编译的最新的。a 文件而已,并且每次编译都会重新编译最新的。a 文件 (所以只保留。a 文件,删除源码是不可行的),但是标准库除外,并不会每次编译都会重新编译标准库,所以说如果你修改了源码一定得把标准库。a 文件删除,并且 build 的时候使用 build -a 的方式才可以使用自己更新的标准库。

一个小 tip:go 导包的时候引入的是路径名称,路径名称通常最后一位路径名称跟包名保持一致,当然也可以不一致。例如 github.com/shgopher/go-hui 但是实际上包名称是这个 package hui,所以导入的时候按照 github.com/shgopher/go-hui,使用的时候用 hui.xxx()

因为包的名称很容易发生冲突,所以 go 接受包名称的重命名

import (
+
+  app  "github.com/shgopher/app"
+  app1  "github.com/googege/app"
+)
+

除此之外还有 _ 方式,它会计算包级变量的初始化表达式执行导入包的 init 初始化函数,意思就是跟 init 函数有关的内容都会被计算,并且导入进去。

package main
+
+import (
+	"fmt"
+	"image"
+	"image/jpeg"
+	_ "image/png"
+	"io"
+	"os"
+)
+
+func main() {
+	if err := toJPEG(os.Stdin, os.Stdout); err != nil {
+		fmt.Fprintf(os.Stderr, "jpeg: %v\n", err)
+		os.Exit(1)
+	}
+}
+
+func toJPEG(in io.Reader, out io.Writer) error {
+	img, kind, err := image.Decode(in)
+	if err != nil {
+		return err
+	}
+	fmt.Fprintln(os.Stderr, "Input format =", kind)
+	return jpeg.Encode(out, img, &jpeg.Options{Quality: 95})
+}
+
+

比如这个例子,image.Decode 会查询注册表,看看注册表里都有谁,这个时候我们引入的 _ iumage/png 的 init 函数就是将 png 中实现了接口的具体数据导入到了注册表中,所以说这里只需要导入这个 init 函数即可。

这种用法还是比较重要的,我们来自己编写一段代码,来看看实现这种注册器的基本原理:

// 这里是实现这个注册器
+package go2
+// 在某个包实现go2的时候,这里是go3,这个drivers就被初始化了。
+var drivers = make(map[string]Driver)
+
+type Driver interface {
+	Open()
+}
+
+func Register(name string, driver Driver) {
+	drivers[name] = driver
+}
+
+func DDD(name string) {
+	var d Driver
+	if d1, ok := drivers[name]; !ok {
+		panic("no real driver registered")
+	} else {
+		d = d1
+	}
+	d.Open()
+}
+
+
// 这里是实际的实现我们注册器那个包实现的抽象方法
+package go3
+
+import (
+	"fmt"
+
+	"shgopher.com/go2"
+)
+
+type ddd struct{}
+
+func (d *ddd) Open() {
+	fmt.Println("this is go3")
+}
+
+func init() {
+	go2.Register("go3", &ddd{})
+
+}
+
+

+// 最后我们来使用一下:
+
+package main
+
+import (
+	"shgopher.com/go2"
+	_ "shgopher.com/go3"
+)
+
+func main() {
+
+	go2.DDD("go3")
+}
+
+

另外还有一种导入包的奇妙用法,就是

	. "fmt"
+
+	func main(){
+		println("hi")
+	}
+

它的意思是省略包名,no!,请不要这么用。

go module 构建工具

  • go111MODULE
  • GOPROXY
  • GOSUMDB
  • module 版本的升级
  • workspace

go 自带包管理工具 go module,本文章写作时 go 的版本是 1.19,所以过去的 go path,go vendor,go dep 均不再提及,读者也不用去 care,鉴于干谈 go module 非常的枯燥,我设立一个例子来讲述 go module。

在桌面,我们使用 go module 的创建命令

cd ~/Desktop
+
+mkdir hello
+
+go mod init github.com/shgopher/hello 
+
+

创立了一个以 hello 作为包名称的包。这里我们使用 github.com/shgopher/hello 是证明这个包使用 GitHub 进行存储,因为 go 使用 git 来管理版本,在项目内部,这个项目的包名称就是最后一个名称,hello 才是这个包的正式名称,此处是惯用,实际上真实管理包正式名称的是每一个文件上的 package xx 管理的,整个就是路径而已,不过习惯于最后一个路径名称为包的名称,即:最后一个名称 == package 后面的 xxx。

然后我们在这个项目的 root 路径下会发现 go 帮我们创建了一个 go.mod 的文件,它就是 go module 的版本文件,我们拿一个 go.mod 来作为举例

// 一个 go.mod 目录:
+
+module github.com/shgopher/hello
+
+go 1.19
+
+
+require (
+  github.com/apache/thrift v0.13.0
+  github.com/bytedance/gopkg v0.0.0-20220531084716-665b4f21126f
+  github.com/appleboy/gin-jwt/v2 v2.6.4
+  gopkg.in/yaml.v3 v3.0.1
+)
+
+require (
+
+	github.com/beorn7/perks v1.0.1 // indirect
+)
+
+replace (
+	// 后面指向的要么是相对路径例如 ../shgopher/.com/net
+	// 要么一定要在后面加上版本,并且是可以获取到的包
+	golang.org/x/net v1.1.2 => shgopher.com/net v1.4.5
+)
+
+

我们一个一个解释,首先最开头的是这个包的路径名称,实际上 go 会使用 git clone https://github.com/shgopher/hello.git 的方式下载包,当然,假设你不使用 GitHub 的 git 作为项目的存储位置,使用自建的也是 OK 的,比如 module shgopher.com/hello 只要同样配置了 git 服务器都可以,因为底层都是使用的 git clone https://xxx.com/xx.git 模式。

接下来的 go 1.19 是这个项目使用的 go 的大版本,比如你使用的是 1.19.11.19.2,上面写的都是 go 1.19,比如你使用 go mod edit -go=1.19 来更新此项目使用的 go 版本的时候,只能写到 minor,而不能加上 patch (minor 和 patch 下文有说)。

接下里有两个 require,其中第一个 require 指的是直接引入的包,后面的 require 是间接引用的包。意思就是你引用的包,它引入的包。

第一个 require 中,我给出了四种常见的版本用法

  1. 使用 git version 命名的版本 v0.13.0
  2. 项目没有使用 version 命名,go 官方使用最新的文件,并且给予了它一个临时的版本号 v0.0.0-20220531084716-665b4f21126f
  3. 当项目的版本超过 1.x 的时候,go 推荐使用再加上一个/v2 的方式进行命名,但是实际上它的包名称仍然是 gin-jwt,并且 1.x 和 2.x 的包可以同时引入项目中,因为他们算两个包,只需要重命名即可。版本 2 和版本 1 的 module 后面写的也不一样,比如一个是 module github.com/shgopher/collie 版本 2 就是 module github.com/shgopher/collie/v2 不过虽然最后结尾的是 v2 但是 package 后面写的仍然是 collie
  4. 也可以使用 yaml.v3 的方式进行命名,因为路径名称并不是包的名称,所以这种方式也有不少项目使用

第二个 require,全部都是间接引用的包名称,go 也会一并下载到本地缓存,go 会使用 ~/go/pkg/mod/包名称@版本号 的地方去保存下载下来的包。

使用 go list -m -json all 命令可以查看当前项目的所有依赖的包名称,例如

{
+	"Path": "github.com/marmotedu/iam",
+	"Main": true,
+	"Dir": "/Users/shgopher/Desktop/github-projects/iam",
+	"GoMod": "/Users/shgopher/Desktop/github-projects/iam/go.mod",
+	"GoVersion": "1.18"
+}
+{
+	"Path": "cloud.google.com/go",
+	"Version": "v0.93.3",
+	"Time": "2021-08-17T22:38:11Z",
+	"Indirect": true,
+	"GoMod": "/Users/shgopher/go/pkg/mod/cache/download/cloud.google.com/go/@v/v0.93.3.mod",
+	"GoVersion": "1.11"
+}
+{
+	"Path": "github.com/marmotedu/component-base",
+	"Version": "v1.6.2",
+	"Time": "2021-12-21T06:47:41Z",
+	"GoMod": "/Users/shgopher/go/pkg/mod/cache/download/github.com/marmotedu/component-base/@v/v1.6.2.mod",
+	"GoVersion": "1.17"
+}
+

我们重点关注几个字段,首先第一个对象中,"Main":true,表示这个对象描述的包属于主包,即,这个项目的 root 路径下的 go.mod;第二个对象中,我们看到 "Indirect": true, 意思就是指这个包是间接引入的包,第三个对象里,并没有出现 indirect 的字段,就证明这个包是直接引入的。因为这个列举的主要是 go.mod 的目录,又因为拥有一个 go.mod 的包拥有同样的版本号,所以包和包的子包是公用一个 go.mod 的,只会出现一次。

如何控制 go 引入的包的版本号。

当我们更新一个旧的项目中的依赖时,我们可以使用 go clean -modcache 的命令去删除 go 缓存的 go 包。然后显式的为包设置版本,第一种方法是直接在 go.mod 写入要引入的包的版本,第二种方法是使用 go mod -require=github.com/shgopher/hello@v0.1.1 的方式写入你想要的包的版本,除了第二种这种比较精确的方式,go mod 还支持 query 的方式去指定范围,例如说 go mod -require=github.com/shgopher/hello@>=v0.1.1

go 包采用 “最小版本” 选择的理念。举个例子,我的项目 hello,直接引入了 c 包和 d 包,然后 c 和 d 又分别引入了 e 包,那么最后本项目使用的 c,d,e 包采用的是哪个版本呢?

我们详细说明一下,假设我们使用的 cde 都是@latest,那么每次 build 的时候,都会去下载最新的包;如果我们使用了具体的版本,假设我们在 go.mod 中给定的 c d 分别是 v0.1.0 v1.0.1 但是呢,c 存在多个版本,比如现在有 v0.1.1 v0.2.1 那么根据最小版本的选择问题,go 会选择一个符合 v0.1.0 的最小版本,即:>=v0.1.0,所以此处会选用 v0.1.1,也就是说 go 的版本的隐藏含义是大于等于选最小,很多别的语言都是大于等于选最大,例如 rust,但是强调一下,这个大于等于选最大或者最小说的是一个版本的情况下,超过版本号的时候,算俩包 (例如 v1.1.0 和 v2.1.0 就算俩大版本了,所以说他们是俩包都不为过),而且我们在改变版本的时候应该先把缓存给清除了 go clean -modcache,接下来我们的 cd 包分别引入了 e 包,假如 e 存在 v1.1.0 v1.2.0 v1.3.0 v1.4.0,c d 分别引入的是 v1.1.0 v1.3.0 那么 go 会合并需求 (只引入包众多版本的一个版本),并且依然采用最小版本的方式即:最终只选用 e 包的 v1.3.0 这一个版本的包引入,总结一下:单个包,使用 >= 的最小值引入,多个包引入同一个包的情况,使用满足他们共同条件下的最小值。

使用 go mod tidy 可以对这个项目的包进行梳理,比如使用 latest 的包会重新比对,然后下载最新版本的包,比如在 require 中明确引入的包,但是在实际上线前发现没有继续使用这个包了,那么使用 go mod tidy 也可以删除这个包,这个命令类似于 “刷新” 这个概念。

如何优雅的升级和降级引入的包版本?

我们会用到下面两个命令

  • go list
  • go get

首先,我们使用 go list -m -versions github.com/shgopher/hello 的命令查找这个包的众多版本 (没有使用 git 给定版本的就没办法了,给定@latest,直接用最新的了),例如说会有 v1.1.0 v1.2.0,假设我们本来用的是 v1.1.0 想升级一下,我们使用 go get github.com/shgopher/hello@v1.2.0 即可优雅的升级这个包的版本,当然降级也是一样的,反正指定一个包即可,要注意的是,go 在升级或者降级的时候,会自动将间接使用的包也做出相应的版本调整。假如我们现在想把所有的依赖包升级为这个版本下的最新包,使用 go get -u 就可以更新所有的直接依赖和间接依赖的包为最新包。如果只想升级 patch 而不是 minor (v1.2.0;1 是 version,2 是 minor,3 是 patch) 使用 go get -u=patch,如果只想升级某一个包 (及其依赖包) 到最新版本,在后面指出来具体的包名称即可,go get -u github.com/shgopher/hello

go install 和 go get 是两个比较像的命令,其中 go get 仅仅用在 go module 中调整版本的时候,比如升级,降级,go install 用于下载包,两者都是一样的,如果包的后面带上版本那么就是指定版本,如果不带就是最新版本。

go install github.com/shgopher/hello 
+
+go install github.com/shgopher/hello@v1.1.2
+
go get -u github.com/shgopher/hello
+go get github.com/shgopher/hello@v1.3.4
+

在使用 go install 的时候其实是忽略 go.mod 的,所以 go install 跟 go.mod 没有任何关系,也不会记录 go.mod 中,它的作用就是下载包,go get 跟 go.mod 紧密相关,它需要 go.mod,并且每次更改都会记录在 go.mod 中,当你在一个没有 go.mod 的路径下 (指的是此路径,此路径的父路径也没有) 使用 go get 会提示如下错误

go: go.mod file not found in current directory or any parent directory.
+	'go get' is no longer supported outside a module.
+	To build and install a command, use 'go install' with a version,
+	like 'go install example.com/cmd@latest'
+	For more information, see https://golang.org/doc/go-get-install-deprecation
+	or run 'go help get' or 'go help install'.
+

所以这个时候应该使用的是 go install。

引入的 go 包 git 更改名称了怎么办?

Go 语言提出了一个模块代号 (Module Path) 的概念来解决这个问题。

简单来说,模块代号就是一个模块的唯一标识,即使模块仓库的路径名改变了,但其模块代号不变。

例如一个模块:

模块路径:github.com/user/project +模块代号:github.com/user/project +如果模块 rename 后路径变成了 github.com/newUser/newProj,但模块代号不变:

模块新路径:github.com/newUser/newProj +模块代号:github.com/user/project +这样通过模块代号,其他依赖它的模块导入语句就不用修改了,还是导入老的模块代号,但实际上会去新路径下找。

在发布新版本代码时,只需要在 go.mod 文件中声明:

module github.com/user/project

就可以确保模块代号不变,其他依赖不受影响。

所以 Go 语言通过模块代号很好地解决了模块路径变更的问题,最大程度保证了导入的稳定性。

go 包代理

GOPROXY 是 go 的代理服务器。go 之前使用 GitHub,gitlab 等托管平台,goproxy 命令可以设置一个集中式的代理服务,比如

export GOPROXY = https://goproxy.cn,direct 
+
+export GOPROXY = https://proxy.golang.org,direct
+
+

其中前者是国内常用的代理服务器,后者是 go 官方的代理服务器;direct 的意思是,直接使用代理服务器的内容,, 的意思是前面的服务器只有出现 404 和 410 错误的时候才会去选择逗号后面的服务,如果想设置只要发生错误就使用后者的命令,那么可以使用 |,例如使用

go env -w GOPROXY=https://proxy.golang.org|https://goproxy.cn|direct`
+

这里有个小知识,因为 在 unix-like 操作系统中通常还表示通道的含义,就是前面的输出等于后面的输入,所以我们需要将这个符号进行转义才能正常使用:

go env -w GOPROXY=https://proxy.golang.org\|https://goproxy.cn\|direct
+
+

go module 还有 go.sum 这个文件,它存储的是包的一些基础信息,最重要的是对于一个包求 hash 值,记录这个 hash,在每次 build 的时候对于缓存的包和 go.sum 中的 hash 值做对比,来规避恶意更改,go.sum 会在项目的更新换代过程中保存多个版本的包信息。

GOSUMDB 命令指向的服务就是保存公有包的校验和的数据库。一个新的包,在一切运行正确的情况下,go 会通过 GOSUMDB 配置的数据库去查询这个包的校验和,查询出结果后和下载的包进行比对,正确的情况下存入 go.sum;如果一个已经缓存的包,每次 run build 的时候都会将缓存的包文件校验跟 go.sum 进行比对来保证正确性。

当然,如果你不想使用 GOSUMDB,使用 go env -w GOSUMDB=off 即可。这样就无法对比包和数据库中的校验和,只能做本地校验了。

如何使用一个私有包

我们讲解了如果配置公有的代理服务器 GOPROXY,文件校验和数据库 GOSUMDB,接下来我们谈一下如果我们想使用一个私有的包,比如一个 GitHub 上的私有包,一个本地 git 服务器上的包,我们使用 GOPRIVATE,它的目的就是绕过 GOPROXY 和 GOSUMDB,因为是私有的所以在代理服务器和文件校验和数据库都不会有它的记录,我们可以这么设置

# 意思是这个路径下的所有包都不会经过代理服务器了。
+# 这个命令支持多个路径使用逗号分隔
+export GOPRIVATE = shgopher.com,shgopher.io,*.api.shgopher.com
+

除了设置这个命令之外,还需要设置一个密钥用来 ssh 的方式去访问 GitHub 上的私有仓库,或者是 GitHub --- personal access tokens

使用 ssh,将主机公钥 (~/.ssh/id_rsa.pub) 添加到 github.com 的 ssh keys 中。

	#我们谈一下如果生成公钥:
+	## 在~/.ssh/ 路径下
+	ssh-keygen -t rsa -C "个人邮箱"
+	## 将这个id_rsa.pub中的公钥 添加到GitHub中的ssh keys 中
+

如果使用 ssh 的方式获取代码,那么在~/.gitconfig 中添加 (这一步其实就是一个映射:保持你的日常习惯的情况下,使用了 ssh)


+[url "ssh://git@github.com"]
+	insteadOf = https://github.com
+

如果是本地服务器那么就是

[url "ssh://git@local.com"]
+	insteadOf = https://git.local.com
+

不过要注意一下,如果使用 ssh 那么远程的服务就得变更名称,因为通常我们的 GitHub 给我们的都是 https 的方式,使用 ssh 的话就是:

 git remote set-url origin git@github.com:USERNAME/REPOSITORY.git
+

从 ssh 更改为 https 就是

git remote set-url origin https://github.com/USERNAME/REPOSITORY.git
+

使用 GitHub personal access token 的方式

# 在GitHub personal access tokens中申请即可,然后配置在~/.netrc
+
+machine github.com login shgopher password 你的 personal access tokens
+

在 Linux 中要配置~/.netrc,但在 macOS 中,git 输入的 username 和令牌会自动的缓存,不用设置这个配置文件。

更多关于 GitHub 访问的信息可以访问这里 (opens new window)

我个人建议直接使用 GitHub 的 personal access tokens (仅支持 https) 这种令牌的方式代替密码,并且使用 https 即可,简单,安全,好用, +另外如果你的令牌更新了,假设是 Macos 的情况下,可以去钥匙串访问的互联网密码种类中去更新令牌

如果我们引入的包是不支持 https 协议的,那么我们可以设置 GOINSECURE = private.res.com 来使用这种私有库。

go 仅仅支持 https 和 http 标准端口的带有域名的包,比如我们使用 ip,或者端口不是 80 和 443,那么设置 GOPROXY 和 GOPRIVATE GOINSECURE 都没用了。

比如类似这种:

// 这种无法使用
+import 192.168.1.1:9090/test/t
+

这种直接使用 ip 的方式不能写到 go 的代码中,所以我们可以使用 git 的 insteadof 功能来改变一下,使用一个正常的 url 去改变掉这个 ip

# ~/.gitconfig
+[url "132.148.1.1:9090/test/t"]
+	insteadof="shgopher.com/test/t"
+

这个时候你引入的时候写 shgopher.com/test/t 就可以了,它会自己找 192.168.1.1:9090/test/t 真实值去代理,并且 shgopher.com/test/t 也要加入到 GOPRIVATE 中才可以。

这个时候有个问题,就是如果 shgopher.com 没有配置版本管理软件,例如 git 这种,go 是无法获取数据的,go 一般会存储一些网站,例如 GitHub,gitlab,所以它看到类似 github.com/xxx/的地址就去找使用 git,或者你手动写上 git,比如 shgopher/d/x.git go 也会明白你使用的 git,go 还能发送请求的方式去获取你使用的版本软件,比如 golang.org/x/net go 就会请求 https://golang.org/x/net?go-get=1 这个服务提供了一个 html:

<head>
+<meta name="go-import" content="golang/x/net git https://go.googlesource.com/net">
+<meta name="go-source" content="golang.org/x/net https://github.com/golang/net">
+<head>
+

这表示这个 golang.org/x/net 的包实际是通过 git 的方式从 https://go.googlesource.com/net 获取的。

我们可以让 GET shgopher.com?go-get=1 返回一个 html,在里面设置为 content=“shgopher.com/test/t git 132.148.1.1:9090/test/t”。

其实,老老实实的使用 go 推荐的导包方式挺好的,即便是私有包也好好的安排一个 git 版本服务器,仅限内部使用的话,配置好 GOPRIVATE 并且使用 ssh 公钥和私钥或者 GPG 的方式加密获取包,这种才是王道。

配置私有的 GOPROXY,你不能总想着靠别人,如果想搭建一个属于自己的 goproxy 服务器,那么可以使用 https://github.com/goproxy/goproxy 这个项目

本地开发使用的配置文件 go.work

go 推出了仅用于本地开发的 workspace,我们来介绍一下这个功能,比如说我们要在一个项目中你引入一个还未公开到公共仓库的包 github.com/shgopher/hui,那么在这个时候就需要 workspace 了。

package main
+
+import(
+	"github.com/shgopher/hui"
+)
+

很显然这个包还未发布,所以不可能引入,在之前通常使用 replace 的方式,但是有了 workspace 以后,不到特定的场景就不需要使用 replace,比如尚未发布的包这种场景用 workspace 最好用 (包的子包不需要任何设置,就可以主包直接引入这个子包了,这一点要搞清楚)。

使用 go work init 来创建一个工作区,go.work 形如:

//go.work
+go1.19
+
+use(
+	. //go.work当前路径是可以省略的
+	/Users/ddd/src/go/hui
+)
+
+

go.work 可以设置在需要工作区的路径的父路径 (如果父路径没有就会一直忘外寻找直到根路径),工作区中的命令会向外部去寻找 go.work,所以我们通常可以在需要 workspace 的地方的父路行下设置 go.work,并且设置为绝对路径这样简单高效,记住,路径是不包含子路径的,比如本来是/workspace/go1 但是你设置的是/workspace 那么就是错的,它会寻找/workspace 中有没有 go.mod 它以 go.mod 作为寻找对象。当然了一个包的子包 (例如/workspace/go1/a) 无需再写进去 go.work 中,它跟外部的包是一个包。

use(
+	/workspace/a
+	/workspace/b
+	/workspace/c
+)
+

在工作区同样可以设置 replace,但是级别没有 go.mod 中的高,会被 go.mod 覆盖。

go 的包和包的管理工具 go module 基本上已经讲解完毕了,以后有了新的见解再更新这篇文章。

参考资料

  • https://go.dev/blog/get-familiar-with-workspaces
  • https://book.douban.com/subject/35720728/ 图书下册 321 页 - 349 页;图书上册 120 页 - 131 页
+ + + diff --git "a/\345\267\245\347\250\213/\345\217\215\345\260\204/index.html" "b/\345\267\245\347\250\213/\345\217\215\345\260\204/index.html" new file mode 100644 index 000000000..c6c730caf --- /dev/null +++ "b/\345\267\245\347\250\213/\345\217\215\345\260\204/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/\345\221\275\344\273\244/index.html" "b/\345\267\245\347\250\213/\345\221\275\344\273\244/index.html" new file mode 100644 index 000000000..99727ccf4 --- /dev/null +++ "b/\345\267\245\347\250\213/\345\221\275\344\273\244/index.html" @@ -0,0 +1,61 @@ + + + + + + 命令 | GOFamily - go 程序员宝典 + + + + + + + + +

命令

go 拥有众多命令操作,这里将讲述关于这些命令的使用方法

介绍一下最常见的命令

  • go help 显示一个命令基本的用法,例如:go help fmt
  • go doc 显示一个命令全部的用法,例如:go doc cmd/gofmt

使用 go help 可以显示全部的形如 go fmt go build 这种挂靠在 go 后面的命令,然后 help 加具体的命令,就可以显示基本用法,然后在 help 提示的内容中,通常会有提示你,如果使用 go doc 命令去寻找更加详细的内容,比如下文要写到的,使用 go help fmt 就会显示去寻找 go doc cmd/gofmt

gofmt

go fmt 命令简单封装了 gofmt 命令

gofmt 的目的是标准化 go 语言的代码,增加代码的亲切感,消除不同人员写的代码的之间的隔阂感

介绍几个常见的使用方法,详细内容可以使用 go doc cmd/gofmt

  • gofmt -s 简化代码

    v := []int{1,2,3}
    +  // 复杂
    +  for _ = range v {
    +
    +  }
    +
    +  // 使用 -s 后
    +
    +  for range v {
    +
    +  }
    +

    不过,这个命令虽然会显示出来要优化的简单写法,但是,并不会更改用户的代码,需要自己去更改。

  • gofmt -r 代码重构 replace 能力

    例子:gofmt -r 'a -> Student' 意思可不是 a 字符改变成 Student,这里是采用的通配符,意思就是所有的英文字符都要改成 student。只要是小写字母都会被视为通配符。再举一个例子,gofmt -r 'a[b:len(a)] -> a[b:]' 这里的 a 代表所有的英文字符串,b 就会代表整数类型

  • gofmt -l 输出不满足 gofmt 要求的文件

    比如 gofmt -l $GOROOT 就会输出这个路径下不满足的文件列表,可以看出 go 的标准库不满足标准的也不少,😂

goimports

安装方法 go get golang.org/x/tools/cmd/goimports,一般的 IDE 都会内置这个工具,比如 goland

  • 对于代码中使用了,但是没有 import 的包
  • 对于代码中没有使用,但是 import 了的包

这个工具都会一一管理,少了加上,多了取消掉

go build

go install

go get

go clean

go doc godoc

go run

go test

go list

go fix go tool fix

go vet / go tool vet

go tool pprof

go tool cgo

go env

+ + + diff --git "a/\345\267\245\347\250\213/\345\235\221/index.html" "b/\345\267\245\347\250\213/\345\235\221/index.html" new file mode 100644 index 000000000..5c698a470 --- /dev/null +++ "b/\345\267\245\347\250\213/\345\235\221/index.html" @@ -0,0 +1,50 @@ + + + + + + 坑 | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/\345\255\227\347\254\246\347\274\226\347\240\201/index.html" "b/\345\267\245\347\250\213/\345\255\227\347\254\246\347\274\226\347\240\201/index.html" new file mode 100644 index 000000000..3ee519037 --- /dev/null +++ "b/\345\267\245\347\250\213/\345\255\227\347\254\246\347\274\226\347\240\201/index.html" @@ -0,0 +1,50 @@ + + + + + + 字符编码 | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/\346\265\213\350\257\225/index.html" "b/\345\267\245\347\250\213/\346\265\213\350\257\225/index.html" new file mode 100644 index 000000000..ba2a774c1 --- /dev/null +++ "b/\345\267\245\347\250\213/\346\265\213\350\257\225/index.html" @@ -0,0 +1,50 @@ + + + + + + go 中的测试 | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/\346\265\213\350\257\225/\345\237\272\345\207\206\346\265\213\350\257\225.html" "b/\345\267\245\347\250\213/\346\265\213\350\257\225/\345\237\272\345\207\206\346\265\213\350\257\225.html" new file mode 100644 index 000000000..276cf4a2f --- /dev/null +++ "b/\345\267\245\347\250\213/\346\265\213\350\257\225/\345\237\272\345\207\206\346\265\213\350\257\225.html" @@ -0,0 +1,50 @@ + + + + + + 基准测试 | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/\346\265\213\350\257\225/\345\237\272\347\241\200\346\265\213\350\257\225.html" "b/\345\267\245\347\250\213/\346\265\213\350\257\225/\345\237\272\347\241\200\346\265\213\350\257\225.html" new file mode 100644 index 000000000..ff5ba05f3 --- /dev/null +++ "b/\345\267\245\347\250\213/\346\265\213\350\257\225/\345\237\272\347\241\200\346\265\213\350\257\225.html" @@ -0,0 +1,50 @@ + + + + + + 基础测试 | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/\346\265\213\350\257\225/\346\250\241\347\263\212\346\265\213\350\257\225.html" "b/\345\267\245\347\250\213/\346\265\213\350\257\225/\346\250\241\347\263\212\346\265\213\350\257\225.html" new file mode 100644 index 000000000..801c4cdb8 --- /dev/null +++ "b/\345\267\245\347\250\213/\346\265\213\350\257\225/\346\250\241\347\263\212\346\265\213\350\257\225.html" @@ -0,0 +1,50 @@ + + + + + + 模糊测试 | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/\347\263\273\347\273\237\344\277\241\345\217\267\347\232\204\345\244\204\347\220\206/index.html" "b/\345\267\245\347\250\213/\347\263\273\347\273\237\344\277\241\345\217\267\347\232\204\345\244\204\347\220\206/index.html" new file mode 100644 index 000000000..3ef3c7b84 --- /dev/null +++ "b/\345\267\245\347\250\213/\347\263\273\347\273\237\344\277\241\345\217\267\347\232\204\345\244\204\347\220\206/index.html" @@ -0,0 +1,50 @@ + + + + + + 系统信号的处理 | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/\350\257\273\345\206\231\346\250\241\345\236\213/index.html" "b/\345\267\245\347\250\213/\350\257\273\345\206\231\346\250\241\345\236\213/index.html" new file mode 100644 index 000000000..b73fff0c7 --- /dev/null +++ "b/\345\267\245\347\250\213/\350\257\273\345\206\231\346\250\241\345\236\213/index.html" @@ -0,0 +1,50 @@ + + + + + + go 语言的读写模型 | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\267\245\347\250\213/\351\241\271\347\233\256\347\273\204\347\273\207\345\275\242\345\274\217/index.html" "b/\345\267\245\347\250\213/\351\241\271\347\233\256\347\273\204\347\273\207\345\275\242\345\274\217/index.html" new file mode 100644 index 000000000..16853a59b --- /dev/null +++ "b/\345\267\245\347\250\213/\351\241\271\347\233\256\347\273\204\347\273\207\345\275\242\345\274\217/index.html" @@ -0,0 +1,50 @@ + + + + + + go 项目组织形式 | GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\271\266\345\217\221/atomic/index.html" "b/\345\271\266\345\217\221/atomic/index.html" new file mode 100644 index 000000000..ac89bf085 --- /dev/null +++ "b/\345\271\266\345\217\221/atomic/index.html" @@ -0,0 +1,61 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

atomic

原子包是比互斥锁更底层的包,如果在简单的场景下,使用 sync.Mutex 可能会比较复杂,并且耗费资源,那么使用更加底层的 atomic 就更加划算了

所谓原子操作,就是当某 goroutine 去执行原子操作时,其它 goroutine 只能看着,这个操作要么成功,要么失败,不会有第三个状态

原子包操作对象的时候,都是操作的地址,所以谨记不要使用值操作而是要指针操作

介绍一下 atomic 的内容

  • Add:例如 func AddInt32(addr *int32,delta int32)(new int32) 给第一个参数地址指向的数据值增加一个 delta 并返回新的数据
  • CompareAndSwap:例如 func CompareAndSwapInt32(addr *int32,old,new int32)(swapped bool) 比较 addr 指向的数据是否等于 old,如果不等于返回 false,如果等于就将此地址的值切换为 new 值,并且返回 true
  • Load:例如 func LoadInt32(addr *int32)(val int32) 读取 addr 指向的值并返回
  • Store:例如 func StoreInt32(addr *int32,val int32) 将 val 值写入到 addr 指向的内存空间中
  • Swap:例如 func SwapInt32(addr *int32,new int32)(old int32) 将 addr 指向的值切换为 new 值,并返回旧值
  • Value:type Value func(*Value) Load func(*Value) Store 原子的存取数据

目前 atomic 还没有部署泛型,所以里面到处充斥者 LoadInt32 LoadInt64 这种类型的函数,以后等泛型部署到原子包后就不会这么繁琐了

基于原子库的第三方扩展

  • uber-go/atomic 定义扩展了几种常见类型的原子操作,例如 bool error string 等

    var atom atomic.Uint32
    +atom.Store(42)
    +atom.Sub(2)
    +atom.CompareAndSwap(40, 11)
    +

    看起来的确比官方提供的原子包更加简洁一些

issues

对一个地址的赋值是原子操作吗?

如果对于单核处理器的机器来说,地址的赋值是原子操作

在现在的系统中,write 的地址基本上都是对齐的

对齐地址的写,不会导致其他人看到只写了一半的数据,因为它通过一个指令就可以实现对地址的操作,如果地址不是对齐的话,那么,处理器就需要分成两个指令去处理,如果执行了一个指令,其它人就会看到更新了一半的错误的数据,这被称做撕裂写

所以,你可以认为赋值操作是一个原子操作

但是,对于现代的多处理多核的系统来说,由于 cache、指令重排,可见性等问题,我们 +对原子操作的意义有了更多的追求。

在多核系统中,一个核对地址的值的更改,在更新到主内存中之前,是在多级缓存中存放的。这时,多个核看到的数据可能是不一样的

多处理器多核心系统为了处理这类问题,使用了一种叫做内存屏障 (memory fence 或 +memory barrier) 的方式。一个写内存屏障会告诉处理器,必须要等到它管道中的未完成 +的操作 (特别是写操作) 都被刷新到内存中,再进行操作。

atomic 包提供的方法会提供了一些的功能,不仅仅可以保证赋值的数据完整性,还能保证数据的可见性,一旦一个核更新了该地址的值,其它处理器总是能读取到它的最新值。

atomic 包主要利用了以下几点技术:

  • 编译器插入内存屏障 (Memory Barrier) 指令 +编译器会在 atomic 操作前后插入内存屏障指令,来限制 CPU 的乱序执行,保证在该操作前的读写操作都完成,之后的读写都待其完成后再执行。

  • 硬件支持的原子 CPU 指令 +如 X86 的 LOCK 指令可以将某些指令变为原子指令。atomic 会利用 CPU 提供的这些原子指令实现加锁。

  • 缓存一致性硬件协议 +如 Intel 的 MESI 协议可以在多核间保证缓存的一致性。atomic 利用缓存一致性,使得多个核心缓存中的数据版本是一致的。

  • 核心间互斥 +atomic 中的原子操作会在多核间加锁,保证同时只有一个核心可以操作共享变量。

需要注意的是,因为需要处理器之间保证数据的一致性,atomic 的操作是会降低性能的。

综上所述,对于单核机器来说,普通的地址赋值就是原子操作,但是对于多核机器来说,不属于原子操作,原子包去进行的赋值一定是原子操作

参考资料

  • go.dev
  • https://time.geekbang.org/column/intro/100061801
  • 《go 进阶训练营》
  • 《go 语言精进之路》
+ + + diff --git "a/\345\271\266\345\217\221/channel/index.html" "b/\345\271\266\345\217\221/channel/index.html" new file mode 100644 index 000000000..cb9982941 --- /dev/null +++ "b/\345\271\266\345\217\221/channel/index.html" @@ -0,0 +1,1158 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

channel

channel 是 csp 并发模型中的重要组成部分,它完成的使命是 goroutine 之间的通信。也就是 csp 中的 c。

channel 是 go 语言语言级的线程安全的通信组件,它是 go 语言的一个内置类型,也因此 go 语言才被称之为在设计之初就考虑了并发的语言。

go 语言之父 rob pike 曾说到 “不要使用共享内存的方式去通信,而是通过通信的方式去共享内存” 这句话就是形容的 channel,这句话的意思是说,我们正在执行的程序不应该通过共享内存的方式去完成这个并发操作,各个线程应该各自运行,互不干涉,使用通信的方式去交流去分享内存信息即可。

基础用法

创建 channel

channel 是一个引用类型,所以创建一个 channel 必须划定底层内存给 channel,所以需要使用 make,不使用 make 只用来声明的 channel,其初始值就是 nil,这一点跟其它引用类型,例如接口,切片,map 是一致的。

对于 nil 的 channel,发送和接收数据总是会阻塞的,对于 nil 的 channel,关闭它直接会 panic panic: close of nil channel

// 创建一个无缓存channel
+ch := make(chan int)
+// 创建一个有缓存的channel
+ch := make(chan int, 10)
+

一个无缓存的 channel,只有接收了数据,才能发送下一个数据往通道中,一个有缓存的数据,比如这里是 10,那么可以往这个 channel 中发送 10 个数据,并且不需要接收,只有发送第十一个数据的时候才会发送阻塞。

关于 channel 的多线程规则,请参考内存模型这一章节

channel 分为三种类型

  • 只能发送的 channel chan<-
  • 只能接收的 channel <-chan
  • 可发可收的 channel chan

channel 里面理论上可以存在任意的类型数据,当然也包括 channel 自己,那么当 channel 中出现了很多符号时该如何区分呢?

chan<- chan int 
+

这里有个规则,符号总是跟左侧的 chan 在一起表示含义,比如这里,就是一个只能发送的 channel,里面保存的是一个 int 类型的 channel

我们知道,channel 可以拥有三种不同的类型,能发能收的 channel 是满足前两者的,也就是说,当你声明的是一个只能接收的 channel,你可以传入一个能收能发的 channel,但当你声明的是一个能收也能发的 channel 时,就不能传递进去一个只能发或者只能收的 channel

package main
+
+func main() {
+	c := make(chan int, 10)
+	age(c)
+}
+
+func age(c <-chan int) {}
+
// ❌ 
+// cannot use c (variable of type <-chan int) as chan int value in argument to age
+
+package main
+
+func main() {
+	c := make(<-chan int, 10)
+	age(c)
+}
+
+func age(c chan int) {}
+

所以,通常我们实际使用当中都是这样的场景,某个函数中的参数是一个只读或者只发的一个 channel 类型,我们传递进去的是一个能收能发的 channel。

因为我们在函数的参数中这么设置是为了避免使用中出现 bug,比如某个函数它就只能收数据,所以给定它的是一个只能收的 channel 非常合理,但是为什么我们创建时要创建一个能收也能发的 channel 呢?因为这个 channel 必定要在其它的 goroutine 中被发送数据,这个其它 goroutine 参数可能被设置为只能发送。

总结一下,设置只能发或者只能收的 channel 本质上只是为了限制函数的能力是为了避免不必要的 bug,但是 channel 的本质我们还是要清楚的:在不同的 goroutine 之间传递信息

同时,除了上面这个禁忌之外,只能收的 channel 不能 close,一旦 close 就会 panic,不过只能发的 channel 倒是可以 close 这个操作

//❌
+// invalid operation: cannot close receive-only channel c (variable of type <-chan int)
+func main() {
+	c := make(<-chan int)
+	close(c)
+}
+//❌ 
+//invalid operation: cannot close receive-only channel c (variable of type <-chan int)
+func age(c <-chan int) {
+	close(c)
+}
+
+// ✅
+func main() {
+	c := make(chan<- int)
+	close(c)
+}
+//✅
+func age(c chan<- int) {
+	close(c)
+}
+

发送数据

ch := make(chan int)
+ch <- 12
+

接收数据

func age(ch chan int){
+  // 接收但舍弃数值
+  // <- ch
+  
+  // 接收并赋值
+  c := <- ch
+  fmt.Println(c)
+}
+

关闭 channel

ch := make(chan int)
+close(ch)
+

判断从 channel 中取出的值是否有效

v,ok := <-ch
+
+if ok {
+  // 是有效值
+}else{
+  //是零值
+}
+

ok 的值表示的是 “接收是否会成功”,而不是 channel 是否已关闭

当 channel 已关闭且数据已取完,再接收时 ok 会为 false,表示 “接收不会成功”

不过值得注意的是,即使 channel 已经关闭,如果之前 channel 中还有数据未被消费,那么数据也能被正常的读取:

package main
+
+import "fmt"
+
+func main() {
+	ch := make(chan int, 3)
+
+	// 向channel发送3个数据
+	go func() {
+		ch <- 1
+		ch <- 2
+		ch <- 3
+		close(ch)
+    fmt.Println("已经关闭了channel,并且没有取值")
+	}()
+  time.Sleep(time.Second*2)
+	// channel中还有未消费的数据,可以正常读取
+	fmt.Println(<-ch) // 1
+	fmt.Println(<-ch) // 2
+	fmt.Println(<-ch) // 3
+	// channel数据已取完,此时会立即得到零值
+	fmt.Println(<-ch) // 0
+	// 再次关闭channel会panic
+	close(ch)
+}
+

range channel

ch := make(chan int, 3)
+for value := range ch{
+    fmt.Println(value)
+}
+

值得注意的是,一个 channel 如果没有被关闭,那么 range 操作将会一直阻塞,所以通常我们都会关闭这个 channel,好让程序继续执行,所以当我们控制 channel 的时候,尽可能的还是让发送方去控制 channel 的关闭,不要在接收方去控制。

go func() {
+		wg1.Wait()
+		close(b)
+	}()
+
+for i := 0; i < 32; i++ {
+		go func(i int) {
+			defer wg2.Done()
+			for i := range b {
+        //
+			}
+		}(i)
+	}  
+

注意看,这个关闭的操作其实是另起了一个 goroutine,因为你为了 waitgroup 的完全执行完毕,所以使用新的 goroutine 去关闭 channel 的操作是比较常见的做法,你可以参考这个项目 (opens new window)

其它操作:len cap

ch := make(chan int, 10)
+
+// 获取channel中缓存的中还未被取走的元素数量
+fmt.Println(len(ch))
+
+// 获取channel的容量
+fmt.Println(cap(ch))
+

不同状态 channel 的总结

状态 发送数据 接收数据 关闭chan
正常 正常 正常 正常
nil 阻塞 阻塞 panic
closed panic 正常值+零值 panic

对于正常的 channel,如果它没有缓存,那么读写都会发生阻塞,除非另一方准备好,对于有 buffer 的 channel,当它还有缓存的时候,随意发送数据,这个时候收可以不准备好,当他满了,其实跟没有 buffer 的是一样的,这里就不过多讨论了,可以去查看内存模型那一章

select

select 是 go 语言提供的,供 channel 去操作的一个组件,它的基本使用方法如下

func main() {
+  var ch = make(chan int,10)
+  var ch1 = make(chan int,10)
+  var ch2 = make(chan int,10)
+  for i:=0;i< 10;i++ {
+    select {
+      case <-ch:
+        fmt.Println("ch被关闭了")
+      case ch1<- i:
+        fmt.Println("ch1 发送成功")
+      case v,ok := <- ch2:
+        if ok {
+          fmt.Println("ch2 接收到值",v)
+        }
+      default:
+        fmt.Println("ch没有被关闭")
+    }
+  }
+}
+

首先,select 的 case 中只能存放 channel 的收和发,以及一个 default 分支,各个分支如果在相同时间满足了条件是会随机去走分支的,不存在先后顺序,在 case 都阻塞的情况下再去走 defalut 分支,default 分支的优先级要低于 case 分支,所以 select 会优先去执行 case 中的 send 和 receive 操作,如果都满足条件,那么 select 会随机去执行一个 case 分支,如果都不存在条件,那么 select 会去执行 default 分支,如果 default 不存在,那么 select 会阻塞。

在 select 中也是可以去做判断的,判断 channel 是否存在正常值

select 本身不具有循环性质,所以通常被配合 for 循环使用

select 在没有任何 case 的时候会陷入阻塞,我们如果希望有一个阻塞存在,使用 select 是极好的:

func main(){
+//...
+select {}
+}
+
+
+

对于 select,for,time.Sleep 的阻塞机制的理解

1。使用 for 循环不会造成 cpu 的执行吗?还是说 cpu 陷入了休眠状态,time.Sleep 呢?

使用 for{} 循环会导致 CPU 高速空转。

for 循环本质上是一个忙等待/空转,CPU 会一直执行循环体内容,只不过这里的循环体为空,所以 CPU 会一直空转浪费资源。

相比而言,time.Sleep() 会让出 CPU 时间片,把 goroutine 暂停一段时间,在这段时间 CPU 可以去执行其他任务。当 sleep 时间结束,goroutine 会重新得到时间片继续执行。

所以从资源利用效率来说,time.Sleep() 明显优于 for 循环的空转方式。

2。那么 select {} 呢是高速空转还是让出资源呢?

当 select 里所有的 case 都不 ready 时,它会释放 CPU 时间片,使当前 goroutine 进入阻塞状态,这就避免了空转。

3。select 的不同 case,在等待 case ready 的时候,select 是靠调度器去看 case 是否 ready 还是不停的轮询呢?

select 内部实现了更精密的监听逻辑:

  • 为每个 case 的 channel 设置监听器
  • 当前 goroutine 阻塞时释放 CPU 执行权
  • 监听器异步监控 channel 状态
  • 有 channel ready 则唤醒 goroutine

所以 select 不是通过忙碌的轮询来判断 channel ready,而是通过异步监听的方式,只在必要的时刻唤醒 goroutine。

这种方式可以极大地减少 CPU 的占用,效率也更高。

综上所述,如果你想设置一个阻塞时,使用一个没有 case 的 select 是比一个空 for 循环更好的方法,如果你想设置一个有时间的阻塞时,使用 time.Sleep 无疑是更好的选择,不过这里需要注意的是 time.Sleep 的精度并不高,特别高精度的阻塞不要使用这个函数,select 设计非常精妙,在等待 case 的时候并不会大量耗费 cpu 的执行时间,而是让出 cpu 的执行片段,设置监听,异步的去获取状态,所以 select 的性能是非常不错的

定时器

在 go 语言中,go 提供了定时器给用户,基本的使用方法如下:

func main(){
+  for {
+    select {
+      // 在一秒后给case发送信息
+    case <-time.After(time.Second):
+      return
+    // 心跳信号,每间隔一秒发送一次信息
+    case <-time.Tick(time.Second):
+    }
+  }
+}
+

通常来说,这是为了超时而去设置的跳出机制

cron 规则定时任务框架 robfig/cron

robfig/cron 是一个 Go 语言编写的 Cron 计划任务管理器。

这个项目的主要功能和特点包括:

  • 实现了 cron 规范,可以基于 cron 表达式来调度任务
  • 支持在代码中以声明式方式定义定时任务
  • 支持在配置文件中定义定时任务
  • 支持任务链,一个任务可以触发其他任务
  • 支持任务锁,防止并发执行
  • 支持失败重试
  • 有默认的日志记录器,也可以自定义日志记录器
  • 轻量级,没有外部依赖

robfig/cron 因为其简单、轻量和高效而被许多 Go 项目用来实现定时任务。它可以用于项目中不同粒度的调度需求,如每分钟执行、每小时执行等。

下面解释一下 cron 机制的含义:

cron 表达式调度是 cron 作业调度程序的核心机制。

cron 表达式是一个字符串,由 5 或 6 个用空格分隔的字段组成,每个字段表示一个时间单位。Cron 会根据表达式中设置的值,在特定的时间点触发相应的任务。

cron 表达式的字段与意义如下:

# 文件格式說明
+# ┌──分钟(0 - 59)
+# │ ┌──小时(0 - 23)
+# │ │ ┌──日(1 - 31)
+# │ │ │ ┌─月(1 - 12)
+# │ │ │ │ ┌─星期(0 - 6,表示从周日到周六)
+# │ │ │ │ │ ┌─年份( * 表示全部年份,2024 表示只在2024年)
+# │ │ │ │ │ │
+# * * * * * *
+
+# 例如
+# 0 0 12 * * *
+# 每天 12 点触发
+

每个字段可以使用特殊字符表示多种时间设置:

  • *:表示匹配该字段的任意值
  • ,:表示分隔多个值,如在分钟字段使用 “5,15,25” 就表示5分、15分、25分都触发
  • -:表示一个范围,如在小时字段使用 “9-17” 表示 9 点到 17 点之间每个整点都触发
  • /:表示一个间隔,如在分钟字段使用 “0/15” 表示每15分钟触发一次,从0分开始
  • ?:表示不指定的值,会匹配该字段的任意值。

一些例子:

  • 0 0 12 * * ?:每天 12 点触发
  • 0 15 10 ? * *:每天 10:15 触发
  • 0 0/5 14 * * ?:每天 14:00 到 14:55 每5分钟触发一次
  • 0 0/5 14,18 * * ?:每天 14:00 到 14:55 和 18:00 到 18:55 每5分钟触发一次

通过这种表达式,cron 可以非常灵活地设置触发时间表,从而实现各种调度需求。

下面给出这个项目的 demo:

c := cron.New() 
+
+// 每小时的第30分钟执行
+c.AddFunc("30 * * * *", func() { fmt.Println("每小时的第30分钟") })
+
+// 在每天早上3点到6点,晚上8点到11点的范围内,每小时执行一次  
+c.AddFunc("30 3-6,20-23 * * *", func() { fmt.Println("在每天早上3点到6点,晚上8点到11点的范围内,每小时执行一次") }) 
+
+// 每天早上东京时间4点30分执行一次
+c.AddFunc("CRON_TZ=Asia/Tokyo 30 04 * * *", func() { fmt.Println("每天早上东京时间4点30分执行一次") })
+
+// 从现在起每小时执行一次
+c.AddFunc("@hourly", func() { fmt.Println("从现在起每小时执行一次") })
+
+// 从现在起每隔1小时30分执行一次  
+c.AddFunc("@every 1h30m", func() { fmt.Println("从现在起每隔1小时30分执行一次") })
+
+c.Start()
+
+// 被添加的函数会在自己的goroutine中异步执行
+... 
+
+// 也可以向已经启动的Cron添加新的函数
+c.AddFunc("@daily", func() { fmt.Println("每天执行") })
+
+// 检查cron任务下次和上次的执行时间
+inspect(c.Entries()) 
+
+// 停止调度器(已经启动的任务不会被停止)
+c.Stop()
+

使用反射执行未知数量的 channel

当你不确定需要多少个 channel 去处理时,你只能选择在运行时去创建未知数量的 channel,这个时候就要使用反射了。

我们使用 reflect.Select(cases []reflect.SelectCase)(chosen int,recv reflect.Value,recvok bool) 去实现 select

package main
+
+import (
+	"fmt"
+	"reflect"
+)
+
+// 使用反射创建的select
+func main() {
+	ch1 := make(chan int, 10)
+	ch2 := make(chan int, 10)
+
+	var cases = createSelectCase(ch1, ch2)
+
+	for i := 0; i < 10; i++ {
+		// chosen:channel 切片的索引
+		// recv:收到的值
+		// ok:是否是通道上发送的值(而不是因为通道关闭而接收到的零值)
+		// Select最多支持 65536 个channel
+		chosen, recv, ok := reflect.Select(cases)
+
+		if recv.IsValid() { // 收
+			fmt.Println("recv:", cases[chosen].Dir, recv, ok)
+		} else { // 发
+			fmt.Println("send:", cases[chosen].Dir)
+		}
+	}
+}
+
+func createSelectCase(chs ...chan int) []reflect.SelectCase {
+	var cases []reflect.SelectCase
+
+	// 创建 收 channel
+	for _, ch := range chs {
+		cases = append(cases, reflect.SelectCase{
+			Dir:  reflect.SelectRecv,  // 发 or 收
+			Chan: reflect.ValueOf(ch), // 具体 Channel
+		})
+	}
+	// 创建 发 channel
+	for i, ch := range chs {
+		cases = append(cases, reflect.SelectCase{
+			Dir:  reflect.SelectSend,  // 发 or 收
+			Chan: reflect.ValueOf(ch), // 具体 channel
+			Send: reflect.ValueOf(i),  // 发送的值
+		})
+	}
+	return cases
+}
+
+
+

数据交流---生产者/消费者模式

著名的 worker pool,如果使用 channel 去实现的话,就是一个标准的生产者消费者模式

为了组成这个结构,需要一个存储任务的数据结构,那么这里肯定是使用一个 channel

基本原理就是,一边往 channel 中发送数据,一边从 channel 中取数据,然后使用固定数量的 goroutine 去消费 channel 中的数据,刚好形成一个完整的生产者消费者模式,这就是复用 goroutine 的模式。

具体的额外操作还有控制 worker 数量,任务放入 woker 池,以及从 woker 池取出任务这个操作

package main
+
+import (
+	"context"
+	"fmt"
+	"time"
+)
+
+func main() {
+	ch := make(chan int)
+	ctx, cal := context.WithTimeout(context.TODO(), time.Second*2)
+	defer cal()
+	for i := 0; i < 10; i++ {
+		go func(i int) {
+			for {
+				select {
+				case c := <-ch:
+					fmt.Println(i, ":", c)
+					time.Sleep(time.Second)
+				case <-ctx.Done():
+					fmt.Println(i, "退出")
+					return
+				}
+			}
+		}(i)
+	}
+	// 往 channel 中 发送数据
+	go func() {
+		for {
+			ch <- 89
+		}
+
+	}()
+
+	time.Sleep(time.Second * 20)
+}
+
+

这就是基本的生产者消费者模式,以及 channel 去数据交流的最基础的原理,下面我们来实现一个真正的可用的 worker pool

一个真正可用的 worker pool 不仅需要一个任务 channel,还需要存储任务 channel 的 woker pool,而这个 woker pool 是一个 chan chan 类型

type worker struct {
+   wokerPool chan *worker
+   jobChannel chan Job
+}
+

之所以需要一个 chan chan 原因也很简单,如果只有一个 chan,那么 10 几个固定数目的 goroutine 将会互斥的读取一个 channel 的数据,如果是 chan chan 则互不打扰各自读取自己的 channel 即可。

在这个项目中,我们会将读取的 channel 和处理的 channel 分开,读写分离,进行解耦, +读取数据的 channel 只需要一个即可,处理的 channel 可以有多个

func(w *worker)run(){
+  go func(){
+   for {
+    // 将 单个channel(可以简单的这么认为为一个 channel) 
+    // 放入 wokerPool 中
+    // 如果 这个 单个woker(这里代表单个channel)不再使用的话
+    w.workerPool <- w
+    select{
+      case job := <- w.jobChannel: // 从单个channel中读取数据并处理
+        job()
+        return        
+    }
+     }
+  }()
+}
+
+
+

接下来我们需要一个任务分发的函数,也就是从读取的单个 channel 中读取数据,然后发送给多个处理任务的 channel

type dispatcher struct {
+   workerPool chan *worker
+   OneChannel chan Job
+}
+func(dispatcher *dispatcher)run(){
+  for {
+    select {
+      case job := <- dispatcher.OneChannel: // 从单个channel中读取数据
+          // 从 wokerPool 中获取一个worker
+          work :=<- disatcher.workerPool
+          // 给这个worker发送任务
+          work.jobChannel <- job
+    }
+
+  }
+}
+
+func RunDispatcher(workerPool chan *workerPool){
+   // 启动固定数目的goroutine去消费任务,并且每一个goroutine拥有一个独立的channel
+   for i:= 0;i<cap(workerPool);i++ {
+      worker := newWorker(workerPool)
+      worker.run()
+   }
+  // 启动分发器
+   go  dispatcher.run()
+}
+

大致的运行规律就是这些,其它的完整功能请查看这里:https://github.com/shgopher/grpool

传递信号/通知

当使用 channel 去传递信号的用法

例如使用一个 channel 去充当信号量,当 channel 没有被关闭的时候,那么就会一直阻塞,一旦 closed,那么就能读取到数据,自然就完成了信号的传递,这种用法非常常见,比如:

func age(){
+  ch := make(chan struct{})
+  go func(){
+    //...
+    close(ch)
+  }()
+  <- ch
+}
+

这里使用的 channel 就是充当了一个传递信号的功能。一旦信号传递过来,整个函数就可以继续运行下去了。

或者我们也可以传递一个空的结构体而不是直接 close(ch):

func age(){
+  ch := make(chan struct{})
+  go func(){
+    //...
+    ch <- struct{}{}
+  }()
+  <- ch
+}
+

收发同时进行的信号

你也可能见过这种表现方式,当 channel 充当信号的时候,发和收同时进行,看一个例子:

func main() {
+	go func() {
+		for {
+			select {
+			case <-w.stop: // B
+				w.stop <- struct{}{} // C
+				return
+			}
+		}
+	}()
+
+	// 另一段代码
+	w.stop <- struct{}{} // A
+	<-w.stop             // D
+}
+

先说结论,这种用法提供了两个意思,提供信号+判断是否运行完毕

A 代码等 B 准备好之后,发送了信号; +B 代码接受到了信号,这算完成了第一个含义,提供信号

C 代码等 D 代码准备完毕,发送了信号,D 接受完毕,这个表示 A 通知 B 干的事儿,圆满完成,这里是表示判断是否执行完毕

使用计时器去解决长等待问题

一般来说,我们都是希望程序执行完毕之后自己主动退出,比如:

func main() {
+  ch := make(chan struct{})
+  go func(){
+    time.sleep(time.Second)
+    close(ch)
+  }()
+  <- ch
+}
+

很合理,一般执行完毕之后,就会通知主 goroutine 优雅退出,但是如果执行的时间过长,用户很有可能就会失去信息,这个时候就需要我们再设置一个超时时间更加优雅的退出

func main() {
+	ch := make(chan struct{})
+	go func() {
+		time.Sleep(time.Second * 100)
+		close(ch)
+	}()
+	select {
+	case <-ch:
+	case <-time.After(time.Second):
+		fmt.Println("处理超时")
+	}
+}
+

shutdown 和信号传递结合优雅退出

Shutdown 方法通常与信号处理结合使用,以便在接收到系统退出信号 (如 SIGTERM 或 SIGINT) 时优雅地关闭服务器。这样可以确保所有正在进行的操作都能完成,而不是突然中断,从而提供更好的用户体验

这个方法允许你安全地停止服务器,而不会中断正在处理的请求

Shutdown 方法的工作原理如下:

  • 关闭监听器:首先,它会关闭所有打开的监听器,这意味着服务器将不再接受新的连接请求。

  • 处理现有连接:然后,它会等待所有现有的连接 (如 HTTP 请求) 完成处理。这包括那些正在处理中的请求,以及那些已经完成但尚未关闭的连接。

  • 上下文管理:Shutdown 方法接受一个 context.Context 参数,这个上下文可以用来设置超时。如果上下文在服务器关闭完成之前被取消 (例如,因为超时),Shutdown 方法会返回一个错误。

  • 不处理长连接:需要注意的是,Shutdown 方法不会尝试关闭那些长时间保持连接的连接,如 WebSockets。对于这类连接,你需要在服务器上注册一个关闭通知函数,以便在需要时手动通知这些连接关闭。

  • 服务器不可重用:一旦调用了 Shutdown,服务器就不能再被重用了。尝试再次调用 Serve、ListenAndServe 或 ListenAndServeTLS 方法将返回 ErrServerClosed 错误。

下面我们看一个案例:

有两个 listenAndServe 服务,其中一个是 debug 服务一个是正式服务

func main(){
+   done := make(chan error,2)
+   stop := make(chan struct{})
+
+   // 使用方去控制服务的关闭
+   // 使用方控制并发的操作
+   go func(){
+   	done <- serveDubug(stop)
+   }()
+   go func(){
+   	done <- serveApp(stop)
+   }()
+
+   var stopped bool
+
+   for i := range cap(done){
+   	if err := <- done; err != nil{
+   		fmt.Println(err)
+   	}
+   	if !stopped {
+   		stopped = true
+   		close(stop)
+   	}
+   }
+}
+
+// 省略
+//func serveDubug(){serve()}
+//func serveApp(){serve()}
+
+func serve(addr string,handler http.Handler,stop <-chan struct{})error {
+   s := http.Server{
+   	Addr: addr,
+   	Handler: handler,
+   }
+   // 信号传递和关闭服务结合
+   // 这里不能使用 os.Exit()
+   // os.exit 会立刻全部停止,不优雅,比较暴力
+   // log.Fetal 这个函数通常不要使用,因为它的底层调用的就是os.Exit()
+   // 如果要是使用这个 log.Fetal 函数,只用在程序的起始位置,比如main的开头,或者init函数中
+   go func(){
+   	<- stop
+   	s.Shutdown(contex.Background())
+   }()
+   return s.ListenAndServe()
+}
+

信号传递之 or-do 模式

channel 信号传递之 or-do 模式跟 go 并发原语的 singleflight 比较类似

区别是,channel 的 or-do 是多个信号传递,只有一个 channel 获取信号之后就可以停止其它 channel 了,并不需要将这个信号传递给其它的 channel,singleflight 是需要分享情报的。

下面有两种实现的方式,递归和反射,递归比较适合递归树较低的场景,反射就适合了递归树更高的场景,因为如果递归过深的话很容易出现内存溢出的情况,如果你认为你项目中的 goroutine 也就几十那么递归就可以了,毕竟反射可是很浪费执行时间的,如果 goroutine 成千上万,选用反射即可。

使用递归的方法:


+func Ordo(chs ...<-chan any) <-chan any {
+	// 递归退出条件
+	switch len(chs) {
+	case 0:
+		return nil
+	case 1:
+		return chs[0]
+	}
+	or := make(chan any)
+	go func() {
+		defer close(or)
+		switch len(chs) {
+		case 2:
+			select {
+			case <-chs[0]:
+			case <-chs[1]:
+			}
+		default:
+			mi := len(chs) / 2
+			select {
+			case <-Ordo(chs[:mi]...):
+			case <-Ordo(chs[mi:]...):
+			}
+		}
+	}()
+	return or
+}
+

使用反射的方式:


+func Ordo1(chs ...<-chan any) <-chan any {
+	// 递归退出条件
+	switch len(chs) {
+	case 0:
+		return nil
+	case 1:
+		return chs[0]
+	}
+	or := make(chan any)
+	
+  go func() {
+		defer close(or)
+		var cases []reflect.SelectCase
+		for _, v := range chs {
+			cases = append(cases, reflect.SelectCase{
+				Dir:  reflect.SelectRecv,
+				Chan: reflect.ValueOf(v),
+			})
+
+			reflect.Select(cases)
+		}
+	}()
+	return or
+}
+

开始测试:

package main
+
+import "time"
+
+func main() {
+  // output: 7
+  start := time.Now()
+	<-Ordo(sig(time.Second*7), sig(time.Second*10), sig(time.Second*13))
+	fmt.Println(time.Since(start))
+}
+
+func sig(t time.Duration) <-chan any {
+	ch := make(chan interface{})
+	go func() {
+		defer close(ch)
+		time.Sleep(t)
+	}()
+	return ch
+}
+
+

测试证明这两种方法均可实现 or-do 信号通知模式,不过就我看来,反射的这种方法,完全无脑,也不用考虑递归,也不必去考虑递归导致的内存溢出,从实现逻辑上,非常的清晰,只需要组成一个 select 随便 case 即可,非常清晰,我首推这种实现方法

我们不仅可以使用 sync.Mutext 去实现互斥锁,也可以使用 channel 去做锁,锁本质上来说,其实就是一种信号量,标准的 pv 操作,p 减少数据,获取到锁,v 增加数据释放掉锁,锁是一种二进制信号量

刚好,channel 的收发就符合信号量的指征

type lock struct {
+   ch chan struct{}
+}
+func(l *lock)lock(){
+   <- l.ch 
+}
+func(l *lock)unlock(){
+   l.ch <- struct{}{}
+}
+func (l *lock) tryLock() bool {
+	select {
+	case <-l.ch:
+		return true
+	default:
+	}
+	return false
+}
+
+func (l *lock) tryLockeTimeout(timeout time.Duration) bool {
+	select {
+	case <-l.ch:
+		return true
+	case <-time.After(timeout):
+	}
+  return false
+	
+}
+func NewLock() *lock{
+  l:= &lock{
+    ch: make(chan struct{},1),
+  }
+  l.ch <- struct{}{}
+  return l
+}
+

在初始阶段,给予一个拥有一个缓存的 channel 一个数据,获取锁就是将这个数据取出来,释放锁就是将这个数据放回 channel,这样就实现了信号量以及互斥锁

那么让我们试一下我们自己实现的互斥锁:

package main
+
+import (
+	"fmt"
+	"time"
+)
+
+func main() {
+	l := NewLock()
+	value := 0
+	for i := 0; i < 120; i++ {
+
+		go func() {
+			l.lock()
+			value++
+			l.unlock()
+		}()
+	}
+	time.Sleep(time.Second)
+	fmt.Println(value)
+
+}
+// 120
+

任务编排

fan-in

核心思想就是 “多个进,单个出”,这是数字电路的一个概念,场景是,多个 channel 发送数据给一个处理函数,此函数返回一个 channel 去承载众多信息,我们只需要监听最后输出的 channel 就可以达到监听众多信息的目的。

基本样貌如下:

func fanin(chs ...<-chan any) <-chan any {
+}
+

从这个基本构型也能看出来,我们要在函数中处理多个 channel,这跟上文 channel 传递信号的 or-do 模式类似,所以免不了我们要使用反射或者递归的方式去处理函数,首先我们使用递归的方式去实现这个功能

递归版 fan-in

func fanin(chs ...chan any) chan any {
+	switch len(chs) {
+	case 0:
+		c := make(chan any)
+		close(c)
+		return c
+	case 1:
+		return chs[0]
+	case 2:
+		return merge(chs[0], chs[1])
+	default:
+		m := len(chs) / 2
+		return merge(fanin(chs[:m]...), fanin(chs[m:]...))
+	}
+}
+
+func merge(ch1, ch2 chan any) chan any {
+	c := make(chan any)
+	go func() {
+		defer close(c)
+		for ch1 != nil || ch2 != nil {
+			select {
+			case v1, ok := <-ch1:
+				if !ok {
+					ch1 = nil
+					continue
+				}
+				c <- v1
+			case v2, ok := <-ch2:
+				if !ok {
+					ch2 = nil
+					continue
+				}
+				c <- v2
+			}
+		}
+
+	}()
+	return c
+}
+

反射版 fan-int

func fanintflect(chs ...chan any) chan any {
+	out := make(chan any)
+	go func() {
+		defer close(out)
+
+		var cases []reflect.SelectCase
+
+		for _, vl := range chs {
+			cases = append(cases, reflect.SelectCase{
+				Dir:  reflect.SelectRecv,
+				Chan: reflect.ValueOf(vl),
+			})
+		}
+
+		for len(cases) > 0 {
+			i, v, ok := reflect.Select(cases)
+			// 从 cases 取数据,一旦取完,cases 会取消已经取完的 case
+			if !ok {
+				cases = append(cases[:i], cases[i+1:]...)
+				continue
+			}
+			// out 发送数据
+			out <- v.Interface()
+		}
+	}()
+	return out
+}
+

反射版本这里有个小问题,从 cases 中取出数据,往 out 中添加,这个操作只能一个 case 一个 case 的取,但是你如果观察上文的递归实现模式,你会发现它开启了很多 goroutine 所以是并发的去取数据往 out 中送。

fan-out

fan out 跟 fan in 模式是相反的,fan out 指的是拥有一个输入源,多个输出源,实际上这是一种广播机制,读取一个数据,广播给众多接受者。

下面看一下代码:

func fanout(value chan any, out []chan any, async bool) {
+	go func() {
+		defer func() {
+			for _, v := range out {
+				close(v)
+			}
+		}()
+		// 对一个nil的通道进行 for range 遍历会导致阻塞(block)。
+		for v := range value {
+			for _, vi := range out {
+				vi := vi // if go version is lower then 1.22
+				if async {
+					go func() {
+						vi <- v
+					}()
+				} else {
+					vi <- v
+				}
+			}
+		}
+	}()
+}
+

实际上这段代码有 goroutine 泄露的风险,如果 out 一直不被消费,value 的数据越来越多,那么这么多等待发送信息的 goroutine 就会发生泄露问题

解决方案有下面几种:

  • 使用信号量去控制最多的 goroutine 数量
  • 给每一个 goroutine 设置超时时间
  • 控制发送的 value channel 的发送频率
  • 使用 worker pool 去限制并发数量

那么让我们用代码去实现这些改进的方案:

方案一:使用信号量控制

var (
+  maxWorkers = runtime.GOMAXPROCS(0)
+	sema       = semaphore.NewWeighted(int64(maxWorkers))
+)
+// 在 value传递结束之后,记得close value
+func fanout(value chan any, out []chan any, async bool) {
+	go func() {
+		defer func() {
+			for _, v := range out {
+				close(v)
+			}
+		}()
+		// 对一个nil的通道进行 for range 遍历会导致阻塞(block)。
+		for v := range value {
+			for _, vi := range out {
+				vi := vi // if go version is lower then 1.22
+				if async {
+          ctx := context.Background()
+					if err := sema.Acquire(ctx, 1); err != nil {
+						break
+					}
+					go func() {
+						defer sema.Release(1)
+						vi <- v
+					}()
+				} else {
+					vi <- v
+				}
+			}
+		}
+	}()
+}
+

方案二:使用超时时间

func fanout(value chan any, out []chan any, async bool) {
+	go func() {
+		defer func() {
+			for _, v := range out {
+				close(v)
+			}
+		}()
+		// 对一个nil的通道进行 for range 遍历会导致阻塞(block)。
+		for v := range value {
+			for _, vi := range out {
+				vi := vi // if go version is lower then 1.22
+				if async {
+					go func() {
+            select{
+              case vi <- v:
+              case time.After(time.Second):
+              return
+            }
+						
+					}()
+				} else {
+					vi <- v
+				}
+			}
+		}
+	}()
+}
+

方案三:控制发送 value 的发送频率

go func() {
+		for {
+			select {
+
+			case <-time.After(time.Second * 3):
+				fmt.Println("关闭了吗")
+				close(value)
+				return
+			case value <- "%":
+				time.Sleep(time.Second >> 5)
+				fmt.Println("发送了吗")
+
+			}
+		}
+	}()
+

方案四:使用 worker 池复用 goroutine 去控制并发的 goroutine 数量

// github.com/shgopher/grpool
+func fanout(value chan any, out []chan any, async bool) {
+	pool := grpool.NewPool(100, 50)
+	go func() {
+		defer pool.Release()
+		defer func() {
+			for _, v := range out {
+				close(v)
+			}
+		}()
+		// 对一个nil的通道进行 for range 遍历会导致阻塞(block)。
+		for v := range value {
+			for _, vi := range out {
+				if async {
+					pool.JobQueue <- func() {
+						vi <- v
+					}
+				} else {
+					vi <- v
+				}
+			}
+		}
+	}()
+}
+
+

按照我的工作经验,使用工作池和信号量控制 goroutine 数量的方法最为常用,他们都是保证最多同时存在 n 个 goroutine,这样就避免了 goroutine 泄漏问题

map-reduce

我们在函数那个章节 (opens new window)介绍过 map-reduce 模式,不过并没有牵涉到并发,这里我们介绍一下并发版的 map-reduce 模式 +map

func mapChan(in <-chan any,fn func(any)any)<-chan {
+  out := make(chan any)
+  if in == nil {
+    close(out)
+    return out
+  }
+
+  go func() {
+    defer close(out)
+    for v := range in {
+      out <- fn(v)
+    }
+  }()
+  return out
+}
+

reduce

func reduceChan(in <-chan,fn func(r,v any)any)any {
+  if in == nil {
+    return nil
+  }
+
+  out <- in
+
+  for v := range in {
+    out = fn(out,v)
+  }    
+  return out
+}
+

pipeline-流水线模式

pipeline 模式的核心思想就是顺序,单线模式,数据从头到尾,顺序执行,一个阶段的输出是下一阶段的输入。

var a A
+a.get().then().Download()
+

这就是 pipeline 的一个简单掩饰,它的底层可能是这样实现的

type A struct{}
+func (a *A)get()*A{return a}
+func (a *A)then()*A{return a}
+func (a *A)Download()*A{return a}
+

那么在并发的场景里,如何使 goroutine 实现 pipeline 模式呢?

这显然跟一般的流水线实现方法不同。

我们将 channel 比作一个 token,所谓令牌,只要我们控制获取令牌的顺序,那么就可以控制持有这些令牌的 goroutine 顺序。

下面我们使用 4 个 goroutine 依次打印 “a b c d”,300 次,我们使用流水线的方式实现:

package main
+
+import (
+	"fmt"
+)
+
+type Token struct{}
+
+var (
+	GNumber int
+	Number  int
+	sign    = make(chan struct{})
+)
+
+func world(id int) {
+	switch id {
+	case 0:
+		fmt.Println("a")
+	case 1:
+		fmt.Println("b")
+	case 2:
+		fmt.Println("c")
+  case 3:
+    fmt.Println("d")
+  }
+}
+func worker(id int, in chan Token, out chan Token, number int,fn func(int)) {
+	for number > 0 {
+		token := <-in
+		fn(id)
+    
+    // 这段代码是为了保证最后一个 goroutine 执行完毕退出 
+		if id == (GNumber-1) && number == 1 {
+			close(sign)
+			break
+		}
+
+		out <- token
+		number--
+	}
+}
+func RunPipeline(GNumber int,Number int,chs[]chan Token,fn func(int)) {
+  
+	for i := range GNumber {
+    // 核心代码: 
+    // 传入的in chan 和 out chan 刚好前后顺序
+    // 这样 token 才能前后传递
+		go worker(i, chs[i], chs[(i+1)%GNumber], Number,fn)
+	}
+	chs[0] <- struct{}{}
+	<-sign
+
+}
+func main() {
+  GNumber = 4
+	Number = 300
+  chs := []chan Token{make(chan Token), make(chan Token), make(chan Token), make(chan Token)}
+  RunPipeline(GNumber,Number,chs,world)
+}
+

stream-流模式

将 channel 当做流式管道,提供多种功能,比如筛选元素,跳过元素等

将数据转化为流,其中 channel 就是流,众多数据这里是 ...any

创建流

func stream(done chan struct{}, values ...any) chan any {
+	c := make(chan any)
+	go func() {
+		defer close(c)
+		for _, v := range values {
+			select {
+			case <-done:
+				return
+			case s <- v:
+			}
+		}
+	}()
+	return c
+} 
+

根据流,我们可以有以下的处理

  • takeN:只取流中的前 n 个数据
  • takeFn:筛选流中的数据,只保留满足条件的数据
  • takeWhile:只取前面满足条件的数据,一旦不满足,就不再取了
  • skipN:跳过流中的前 n 个数据
  • skipFn:跳过满足条件的所有数据
  • skipWhile:跳过前面满足条件的数据,一旦不满足条件了,当前这个元素和以后的元素都会输出

takeN

func TakeN(done chan struct{},in chan any,num int)chan any{
+	c := make(chan any)
+	go func(){
+		defer close(c)
+		for i := 0; i < num; i++ {
+			select {
+				case <-done:
+					return 
+				case v,ok := <- in:
+					if ok  {
+						c <-v
+					}					
+			}
+		}
+	}()
+	return c
+}
+

takeFn

func TakeFn(done chan struct{},in chan any,fn func(any)bool)chan any{
+	c := make(chan any)
+	go func(){
+		defer close(c)
+		for v := range in {
+			if fn(v){
+			select {
+				case <-done:
+					return
+				case c <- v:					
+			  }
+			}
+		}
+	}()
+	return c
+}
+

takeWhile

func takeWhile(done chan struct{}, in chan any, fn func(any) bool) chan any {
+    c := make(chan any)
+    go func() {
+        defer close(c)
+        for v := range in {
+            if !fn(v) {
+                return
+            }
+            select {
+            case <-done:
+                return
+            case c <- v:
+            }
+        }
+    }()
+    return c 
+}
+

skipN

func skipN(done chan struct{}, in chan any, n int) chan any {
+    c := make(chan any)
+    go func() {
+        defer close(c)
+        i := 0
+        for v := range in {
+            if i < n {
+                i++
+                continue
+            }
+            select {
+            case <-done:
+                return
+            case c <- v:
+            }
+        }
+    }()
+    return c
+}
+

skipFn

func skipFn(done chan struct{}, in chan any, fn func(any) bool) chan any {
+    c := make(chan any)
+    go func() {
+        defer close(c)
+        for v := range in {
+            if !fn(v) {
+                select {
+                case <-done:
+                    return
+                case c <- v:
+                }
+            }
+        }
+    }()
+    return c
+}
+

skipWhile


+func skipWhile(done chan struct{}, in chan any, fn func(any) bool) chan any {
+    c := make(chan any)
+    go func() {
+        defer close(c)
+        skip := true
+        for v := range in {
+            if skip && fn(v) {
+                continue
+            }
+            skip = false
+            select {
+            case <-done:
+                return
+            case c <- v:
+            }
+        }
+    }()
+    return c
+}
+

测试 stream

func TestStream(t *testing.T) {
+
+  done := make(chan struct{})
+  defer close(done)
+
+  in := stream(done, 1, 2, 3, 4, 5)
+
+  // takeN
+  out := takeN(done, in, 3)
+  want := []int{1, 2, 3}
+	
+	// reflect.DeepEqual 更适用于判断复杂类型的变量,
+	// 像数组、切片、map、结构体等是否相等,
+	// 简单类型可以用==运算符判断。
+  if got := drain(out); !reflect.DeepEqual(got, want) {
+    t.Errorf("takeN = %v, want %v", got, want)
+  }
+
+  // takeFn
+  fn := func(v any) bool {
+    n := v.(int)
+    return n%2 == 0
+  }
+  out = takeFn(done, in, fn)
+  want = []int{2, 4}
+  if got := drain(out); !reflect.DeepEqual(got, want) {
+    t.Errorf("takeFn = %v, want %v", got, want) 
+  }
+
+  // takeWhile
+  out = takeWhile(done, in, func(v any) bool {
+    return v.(int) < 4
+  })
+  want = []int{1, 2, 3}
+  if got := drain(out); !reflect.DeepEqual(got, want) {
+    t.Errorf("takeWhile = %v, want %v", got, want)
+  }
+
+  // skipN 
+  out = skipN(done, in, 2)
+  want = []int{3, 4, 5}
+  if got := drain(out); !reflect.DeepEqual(got, want) {
+    t.Errorf("skipN = %v, want %v", got, want)
+  }
+
+  // skipFn
+  out = skipFn(done, in, func(v any) bool {
+    return v.(int)%2 == 0
+  })
+  want = []int{1, 3, 5}
+  if got := drain(out); !reflect.DeepEqual(got, want) {
+    t.Errorf("skipFn = %v, want %v", got, want)
+  }
+
+  // skipWhile
+  out = skipWhile(done, in, func(v any) bool {
+    return v.(int) < 3
+  })
+  want = []int{3, 4, 5}
+  if got := drain(out); !reflect.DeepEqual(got, want) {
+    t.Errorf("skipWhile = %v, want %v", got, want) 
+  }
+}
+
+func drain(in <-chan any) []any {
+  out := make([]any, 0)
+  for v := range in {
+    out = append(out, v)
+  }
+  return out
+}
+

pipeline 流水线模式和 stream 流模式的对比

流水线模式 (Pipeline Pattern) 和流模式 (Stream Pattern) 都是将任务分解成多个阶段来处理,但两者还是有一些区别:

  • 数据流动方向不同 +
    • 流水线模式是一条主线,数据从头流到尾,每个阶段处理完输出给下一个阶段。
    • 流模式是多条分支,数据可以从多个来源流入,经过处理流向多个去处。
  • 执行方式不同 +
    • 流水线模式强调阶段间串行执行一个阶段的输出是下一阶段的输入
    • 流模式可以并行执行不同阶段可以同时操作不同数据
  • 连接方式不同 +
    • 流水线模式中,每个阶段靠固定管道连接,顺序不能改变。
    • 流模式中,流可以更灵活自由地连接。
  • 扩展性不同 +
    • 流水线模式扩展一个阶段可能影响整个流水线。
    • 流模式扩展一个阶段对其他阶段影响很小。

总结:

  • 流水线模式注重将任务划分多个线性执行的固定阶段。
  • 流模式注重构建灵活的流处理流程,流可以并行运动。

所以两者侧重点不同,在使用上也有区别。

channel 注意事项

发生下面事项一定会触发 panic:

  • 向已经关闭的 channel 中发送数据
  • 关闭一个 nil channel
  • 关闭一个已经被关闭的 channel

goroutine 泄露

func age(){
+  ch := make(chan int)
+  go func(){
+    time.Sleep(time.Second*10)
+    ch <- 12
+    }()
+    select{
+      case <-ch:
+        fmt.Println("ch 被关闭了")
+      case <-time.After(time.Second):
+    }
+}
+

当 time.After(time.Second) 执行完毕以后,上面那个 goroutine 因为无法接收数据,所以就会一直阻塞在发送数据那个地方,所以这个代码中,goroutine 就会泄露了。

解决之道就是将容量设置为 1 即可:

func age(){
+  ch := make(chan int,1)
+  go func(){
+    time.Sleep(time.Second*10)
+    ch <- 12
+    }()
+    select{
+      case <-ch:
+        fmt.Println("ch 被关闭了")
+      case <-time.After(time.Second):
+    }
+}
+

当设置为 1 的时候,即使没有接受者了,发送这个地方的代码也能执行完毕,所以这个 goroutine 是不会泄露了。

这里插一句,main goroutine 只要退出,其它 goroutine 不管有没有执行完毕也会退出,所以如果这种代码在 main 函数中出现,那么是不会发生 goroutine 泄露问题的,因为:

main 函数结束以后,其它 goroutine 自动结束

channle 和运行时调度如何交互

channel 和 Go 运行时调度器的交互方式如下:

  1. 当 goroutine 向 channel 发送数据时,会调用 runtime.chansend 函数。该函数会判断 channel 是否已满,如果满了则让 goroutine 进入阻塞等待。

  2. 当 goroutine 从 channel 接收数据时,会调用 runtime.chanrecv 函数。该函数会判断 channel 是否为空,如果为空则让 goroutine 进入阻塞等待。

  3. 运行时调度器通过维护等待队列来管理被阻塞的 goroutine。当 channel 可读写时,会从等待队列中弹出 goroutine 继续运行。

  4. 运行时调度器以非常高频率运行,会在 goroutine 之间快速切换。这给用户的感觉就是 goroutine 并发运行。

  5. channel 的发送接收动作必须匹配,否则程序会死锁。调度器试图重排发送接收顺序来避免死锁。

总结来说,channel 的阻塞是建立在运行时调度器的基础上。调度器管理被阻塞的 goroutine 队列,在条件允许时恢复 goroutine 继续运行。channel 的使用保证了数据的同步传递。

如果去掉 channel,goroutine 就可以真实的并行了吗

如果没有 channel 的话,goroutine 可以真正并行执行,不会被阻塞。

channel 的发送接收机制,会在对端未准备好时造成 goroutine 阻塞。这就会引起 goroutine 之间的同步关系。

而如果没有 channel:

  1. goroutine 之间不存在同步依赖,可以任意交叉执行。

  2. 程序需要自己维护竞争和同步逻辑,较难处理好并发问题。

  3. goroutine 无法通过 channel 通信,必须使用共享变量,增加竞争风险。

  4. 程序整体并发效果可能更好,但复杂度和错误风险都更高。

  5. 丧失了 channel 带来的可组合性、表达能力等优势。

所以 channel 的引入让编程变复杂,但收获是可靠性和可维护性。需要根据实际情况权衡 channel 的引入带来的收益。

如果非要最大程度发挥并行,确实可以考虑去掉 channel,但成功的并发程序往往不是依靠纯并行来实现的。

这需要根据具体情形来权衡 channel 的引入带来的收益和复杂度。

channel 底层实际上是互斥锁,阻塞机制也符合互斥锁对于 goroutine 的阻塞规则吗

channel 的发送接收在底层会使用互斥锁来实现同步。所以它继承了互斥锁一些内在的特性:

  1. 阻塞的 goroutine 会形成 FIFO (先进先出) 的等待队列,这确保了公平性。

  2. runtime 会优先唤醒等待时间最长的 goroutine,防止饥饿。

  3. 发送和接收有时会采用短暂的自旋等待锁,避免过早休眠线程。

  4. 发送方和接收方争用同一个锁,这会引起优先级反转等问题。

但是

channel 的实现要比直接使用锁更复杂,它内部做了很多优化:

  1. 非阻塞发送和接收会被优先执行,避免不必要的阻塞。

  2. 尽量避免线程休眠,减少锁的争用。

  3. 多核心会并行匹配发送和接收等。

  4. 可调节的缓冲区大小可根据场景优化吞吐量。

综上,channel 继承了互斥锁的一些内在特性,但它的实现要复杂很多,做了大量优化来减少锁争用的影响。

所以使用 channel 的信号量模式去实现的互斥锁并不适合,因为它比 go 本身的互斥锁更加复杂

issues

channel 是并发银弹吗?

在 go 语言中,绝大多数情况下都是是使用 channel 更有优势,比如上文提到的那几种场景,例如数据的传递以及任务编排,需要跟 select 或者 context 结合,那么这都是 channel 的适用场景

不过如果是对于共享资源的并发访问,使用传统的互斥锁更有优势一些

如果只是线程安全的对于某个变量的数据变更,使用原子包显然是更加合适的选择

有无 buffer 的 channel 区别

go 语言中经常会出现一个 bug,就是死锁,很多都很没有设置 channel 缓存有关,有的时候给 channel 设置一个缓存往往可以规避很多的 panic 风险

channel close 后,read write close 的区别

  • read:正常值+零值
  • close:panic
  • write:panic

channle 底层是什么

一个内部有锁的循环队列

channel 在 Go 语言中的底层实现主要涉及以下几个方面:

  1. 数据结构:channel 在内部维护一个消息队列,用于存储缓冲区的数据 (有缓存的话),同时有发送方、接收方的指针。

  2. 同步机制:使用互斥锁 (Mutex) 和条件变量 (Cond) 实现同步,保证并发安全。

  3. 调度机制:通过运行时调度器管理 goroutine 的阻塞和唤醒,维护等待队列。

  4. 内存管理:与运行时内存管理紧密结合,缓冲区数据的内存分配与释放。

  5. 性能优化:负载均衡、本地队列等方式提高性能。

具体而言,channel 的数据结构 hchan 中包含以下字段:

  • buf:循环队列,维护缓冲区
  • sendx/recvx:发送和接收指针
  • lock:互斥锁
  • sendsema/recvsema:发送和接收的条件变量

发送接收时需要获取互斥锁,阻塞时等待条件变量。调度器 basis 上维护等待队列。

编程题,使用三个 goroutine 打印 abc 100 次

上文提到的 channel 任务编排中的 pipeline 流水线模式完美解决这个问题。

package main
+
+import (
+	"fmt"
+)
+
+type Token struct{}
+
+var (
+	GNumber int
+	Number  int
+	sign    = make(chan struct{})
+)
+
+func world(id int) {
+	switch id {
+	case 0:
+		fmt.Println("a")
+	case 1:
+		fmt.Println("b")
+	case 2:
+		fmt.Println("c")
+  }
+}
+func worker(id int, in chan Token, out chan Token, number int,fn func(int)) {
+	for number > 0 {
+		token := <-in
+		fn(id)
+		if id == (GNumber-1) && number == 1 {
+			close(sign)
+			break
+		} 
+		out <- token
+		number--
+	}
+}
+func RunPipeline(GNumber int,Number int,chs[]chan Token,fn func(int)) {
+  
+	for i := range GNumber {
+		go worker(i, chs[i], chs[(i+1)%GNumber], Number,fn)
+	}
+	chs[0] <- struct{}{}
+	<-sign
+
+}
+func main() {
+  GNumber = 3
+	Number = 100
+  chs := []chan Token{make(chan Token), make(chan Token), make(chan Token), make(chan Token)}
+  RunPipeline(GNumber,Number,chs,world)
+}
+

包括之前的水生成工厂那道题,也可以使用 pieline 模式来解决,替换掉之前使用的 waitgroup 和信号量,以及循环栅栏

参考资料

  • https://betterprogramming.pub/common-goroutine-leaks-that-you-should-avoid-fe12d12d6ee
  • https://github.com/fortytw2/leaktest
  • https://cloud.tencent.com/developer/article/1921580
+ + + diff --git "a/\345\271\266\345\217\221/context/index.html" "b/\345\271\266\345\217\221/context/index.html" new file mode 100644 index 000000000..bb742f514 --- /dev/null +++ "b/\345\271\266\345\217\221/context/index.html" @@ -0,0 +1,208 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

context

context 应该被翻译为上下文,但是 go 语言中的 context 更多的作用是取消子 goroutine,传递信息这个上下文本来的含义,在 go 的 context 反而不是重点

context 本身是一个接口类型,它拥有四个方法:

  • Deadline()(deadline time.Time,ok bool):返回一个代表此上下文完成工作的时间,如果没有截止时间,返回 false
  • Done() <-chan struct {}:代表了完结
  • Err() error:如果 context 已经 done 了返回一个 error 错误,错误值分别为:Canceled,DeadlineExceeded,如果没有 done,error 返回 nil
  • Value(key any) any:context 中存储的键值对

context 作为上下文,需要一个最顶层的 context 接口类型,你可以使用 context.Background() 或者 context.TODO() 去充当这个顶端,这两者是一个意思,用哪个都可以,这是 go 提供的已经实现了 context 接口类型的对象,它的底层是一个结构体

context 有一些编程范式:

  • 将 cotext 设置为参数的第一个,例如 func age(ctx context.Context,a string)
  • 不要使用 nil 作为上下文参数,如果想要空的顶端上下文,使用 context.Background,虽然 background 底层实现接口的时候,也是内容为空,但是它的确是实现了接口
  • context 只能作为函数的临时传递对象,不能持久化它,使用数据库保存,等持久化方式都是不可取的
  • 使用 withValue 方法的时候,key 值不要使用 string,如果起冲突,使用自建的类型,例如 type A struct{}
  • 尽量不要定义输出的 key 值

使用 context

标准库中提供了多个 context 接口类型实例:

  • WithCancel
  • WithCancelCause
  • WithDeadline
  • WithDeadlineCause
  • WithTimeout
  • WithTimeoutCause

其中,带有 Cause 的函数跟不带的函数基本意思相同,但是多了一个 cause 的内容,它是指的是取消的原因

withValue

WithValue 基于 parent Context 生成一个新的 Context,保存了一个 key-value 键值 +对。它常常用来传递上下文。

context 在查询 key 值的时候还支持链式查找,如果没有发现数据就往 parent context 中查询

ctx = context.TODO()
+ctx = context.WithValue(ctx, "key1", "0001")
+ctx = context.WithValue(ctx, "key2", "0001")
+ctx = context.WithValue(ctx, "key3", "0001")
+ctx = context.WithValue(ctx, "key4", "0004")
+fmt.Println(ctx.Value("key1"))
+

WithCancel

withCancel 返回父 context 中的 ctx 实例副本,它相当于父 context 的子 context,并且在父 context 被取消时,子 context 也会被取消。

func withCancel(parent Context) *cancelCtx {
+	if parent == nil {
+		panic("cannot create context from nil parent")
+	}
+	c := &cancelCtx{}
+  // 向上寻找
+	c.propagateCancel(parent, c)
+	return c
+}
+

propagateCancel 部分代码:

func (c *cancelCtx) propagateCancel(parent Context, child canceler) {
+	c.Context = parent
+
+	done := parent.Done()
+	if done == nil {
+		return // parent is never canceled
+	}
+
+	select {
+	case <-done:
+		// 如果父done了,那么子ctx一定也会出发cancel
+		child.cancel(false, parent.Err(), Cause(parent))
+		return
+	default:
+	}
+
+	if p, ok := parentCancelCtx(parent); ok {
+		// parent is a *cancelCtx, or derives from one.
+		p.mu.Lock()
+		if p.err != nil {
+			// 如果父发生了cancel,那么子ctx也要触发cancel
+			child.cancel(false, p.err, p.cause)
+		} else {
+			if p.children == nil {
+				p.children = make(map[canceler]struct{})
+			}
+      // 将子ctx添加到父ctx中
+			p.children[child] = struct{}{}
+		}
+		p.mu.Unlock()
+		return
+	}
+  ...
+  go func() {
+		select {
+      // 父 done被触发,那么子ctx就会被触发cancel操作
+		case <- parent.Done():
+			child.cancel(false, parent.Err(), Cause(parent))
+		case <-child.Done():
+		}
+	}()
+}
+
+type canceler interface {
+	cancel(removeFromParent bool, err, cause error)
+	Done() <-chan struct{}
+}
+

propagateCancel 将 c 向上传播,顺着 parent 的路径一直向上查找,直到找到 parentCancelCtx,如果不为空,就把自己加入到这个 parentCancelCtx 的 children 切片中,然后就可以在父 ctx 取消的时候,通知自己也被取消

当这个 cancelCtx 的 cancel 函数被调用的时候,parent 的 Done 被 close 的时候,或者父 ctx 触发了 cancel 的时候,这个子 ctx 会被触发 cancel 动作

cancel 是向下传递的,如果一个 WithCancel 生成的 Context 被 cancel 时,如果它的子 Context (也有可能是孙,或者更低),就会被 cancel,但是不会向上传递。parent Context 不会因为子 Context 被 cancel 而 cancel。

package main
+
+import (
+	"context"
+	"fmt"
+	"time"
+)
+
+func main() {
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+
+	go func() {
+		for {
+			select {
+			case <-ctx.Done():
+				fmt.Println("done")
+			}
+		}
+	}
+  time.Sleep(time.Second * 10)
+}
+

WithCancel 返回 parent context 的一个副本,它自然就是子 context,当父 context 被 cancel 的时候,子 context 也会被 cancel

withTimeout withDeadline

这两个只是添加了到期时间,一个是超时时间,一个是截止时间,一旦超过时间后,自动 close 这个 done 这个 channel

综上所述,done 这个 channel 被 close 有三个原因:

  • 截止时间到了
  • cancel 函数被调用了
  • parent context 的 done close 了,然后子 ctx 也要触发 cancel 方法
  • parent context cancel 了触发子 ctx cancel 方法

关于第三条,解释一下:(第四条类似)

package main
+
+import (
+	"context"
+	"fmt"
+	"time"
+)
+
+func main() {
+	// 创建一个父context,设置deadline为3秒
+	parentCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+
+	// 创建一个子context,deadline继承父context
+	childCtx, cancel := context.WithCancel(parentCtx)
+	defer cancel() // 注意这里需要调用cancel
+
+	go doWork(childCtx)
+
+	time.Sleep(14 * time.Second)
+}
+
+func doWork(ctx context.Context) {
+	for {
+		select {
+		case <-ctx.Done():
+		   // 工作代码
+			fmt.Println("over")
+			return
+	//default:
+	//	fmt.Println("default")
+		}
+	}
+}
+
+

在使用 select 监听 Context 的 Done 通道时,最好不要使用 default 分支。

原因有以下几点:

  • default 分支会导致无法准确检测到 Context cancellation 的信号,如我们之前分析的那样
  • 使用 default 时需要仔细设计 case 分支的阻塞时间,比较 tricky
  • 不使用 default 可以确保每次 select 都会阻塞,从而能捕捉到外部的取消通知
  • 默认情况下,不使用 default 也可以使代码更简洁

带有 cause 的函数

我们看一个例子

package main
+
+import (
+	"context"
+	"errors"
+	"fmt"
+)
+
+func main() {
+	var myError = errors.New("myError")
+	ctx, cancel := context.WithCancelCause(context.TODO())
+	cancel(myError)
+	ctx.Err()                       
+	fmt.Println(context.Cause(ctx)) // returns myError
+}
+

但我们调用 cancel 函数的时候,内部参数是一个 error 类型,调用 context.Cause(ctx) 返回的就是它的取消原因,那么这里的话就是 MyError

在 WithTimeout 中 cancel() 函数的存在意义是什么?

cancel 函数的作用是可以手动提前取消 Context,使其 Done channel 关闭,不用等待 timeout 的时间或者 deadline 的时间

所以 cancel 函数相当于手动取消的意思,并不是说有了 timeout deadline 之后,cancel 函数就没有存在的意义了。

按照 go 的语法,即便是 timeout 触发了 <- done 操作,你仍然需要手动的去在最后调用 cancel 函数,否则就会报错

WithTimeout 在超时时会自动 cancel context,但是 cancel 函数还是需要调用,以释放/重置 Context 内部的 timer,如果不调用 cancel,timer 不会被释放,持续运行并重复 cancel 导致 context leak,占用更多资源。

ctx, cancel := context.WithTimeout(parentCtx, 2*time.Second)
+
+go doWork(ctx) 
+
+// 1秒后决定取消任务
+time.Sleep(1*time.Second)  
+cancel()
+

issues

contex.Contex 如何实现并发安全的?

Go 语言中 context 实现并发安全的主要手段是通过原子操作和 Mutex 来保证状态的原子性

根据底层代码可知,当不同的 goroutine 获取 ctx 的时候,每次操作都会加上互斥锁,来保证数据的非竞争性,线程的安全,例如

if p, ok := parentCancelCtx(parent); ok {
+		// parent is a *cancelCtx, or derives from one.
+		p.mu.Lock()
+		if p.err != nil {
+			// 如果父发生了cancel,那么子ctx也要触发cancel
+			child.cancel(false, p.err, p.cause)
+		} else {
+			if p.children == nil {
+				p.children = make(map[canceler]struct{})
+			}
+      // 将子ctx添加到父ctx中
+			p.children[child] = struct{}{}
+		}
+		p.mu.Unlock()
+		return
+	}
+
+ + + diff --git "a/\345\271\266\345\217\221/index.html" "b/\345\271\266\345\217\221/index.html" new file mode 100644 index 000000000..788de0761 --- /dev/null +++ "b/\345\271\266\345\217\221/index.html" @@ -0,0 +1,50 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + + + + + diff --git "a/\345\271\266\345\217\221/\345\206\205\345\255\230\346\250\241\345\236\213/index.html" "b/\345\271\266\345\217\221/\345\206\205\345\255\230\346\250\241\345\236\213/index.html" new file mode 100644 index 000000000..83d5d2a88 --- /dev/null +++ "b/\345\271\266\345\217\221/\345\206\205\345\255\230\346\250\241\345\236\213/index.html" @@ -0,0 +1,201 @@ + + + + + + 内存模型 | GOFamily - go 程序员宝典 + + + + + + + + +

内存模型

go 官方介绍 go 内存模型的时候说:探究在什么条件下,A goroutine 在读取一个变量的值的时,能够看到其它 goroutine 对这个变量进行的写的结果。

我们为什么需要内存模型?

CPU 指令可能会被重排序,并且存在多级缓存,例如 Go 语言中的多级内存模型。不同的 CPU 架构 (比如 x86 和 ARM) 也会对指令顺序产生影响,编译器优化也可能改变指令顺序。

因此,编程语言需要一个内存规范,也就是内存模型,来规定程序中的内存访问和同步的语义,保证在不同的硬件和编译器下,程序的执行结果保持一致

介绍一些 go 语言中的基本操作

除了基本的读和写之外都是同步操作,包括以下列举的,并且更多以这些基础操作衍生出的操作。

  • 读 +
    • 基础的读 +
      • 对超过机器 word 大小的值读,可以看作是拆成 word 大小的几个读的无序进行
    • 原子包的读
    • 使用 sync.Mux 的读
    • 使用 channel 形式的读
  • 写 +
    • 基础的写 +
      • 变量的初始化就是一个写操作
      • 对超过机器 word 大小的值写,可以看作是拆成 word 大小的几个写的无序进行
    • 原子包的写
    • sync.mux 的写
    • 向 channel 发送数据
  • atomic 的 compare and swap 操作,兼顾了写和读

我们讨论 go 内存模型的时候,主要聚焦下面四种细节的差异性

  • 操作的种类: +
    • 普通的读或者写
    • 同步操作,比如利用 sync 包进行同步,利用 channel 在不同的 goroutine 中间进行同步,利用 atomic 包进行同步等。
  • 这个操作者在程序中的位置
  • 被操作者在内存位置,或者是被访问的变量的位置
  • 被操作者值的类型

内存模型存存在的意义

  • 给程序员一个最根本的法则 --- 你只要遵循这个法则,那么在进行多 goroutine 数据访问的时候,做串行控制 (使用同步操作) 一定会成功。

  • 给编译器优化开发者一个法则,只要遵循这个法则,就能不出错的做出编译器级别的优化。

具体表现

  • 探究可见性
  • happens- before:x 操作一定在 y 操作之前执行完毕,这里说的并不是时间,而是 “执行完毕” 这个结论,我们可以放心的在 y 中使用 x 的执行结果。主要突出的是这个完成性。

三条定律

  • 在一个 goroutine 中,程序执行的顺序一定是符合代码写的顺序的。但是不同的 goroutine 中这个定理失效。

  • 同步操作的时候,从 a goroutine 看到 b goroutine 的执行顺序,必须符合同步操作的顺序,这里举个例子,举例:b 的同步操作是什么顺序,那么 a 读取的就是什么顺序。比如 b 中有三个 Channel,他们执行的顺序是先 a 发 b 收,b 发,c 收,那么在 a 中读取的顺序跟这个同步的操作顺序是一致的,如果不是同步操作就可能不一致。

  • 对于非同步操作的普通读和写,如果 b 操作了 a,那么必须成立

    • b 发生在 a 之前

我们可以使用 go build -race 来发现数据竞争。

接下来我们对于内存模型的具体表现进行更具体的介绍。

重排和可见性

由于指令的重排,代码不一定会按照你写的顺序执行。

这里给出一个案例:有两个 goroutine 分别是 g1,和 g2,g1 读某个变量的数据,g2 写这个变量的数据,当 g1 读取到 g2 写的新数据之后,你也不一定能读取到在 goroutine g2 中排列在这个写数据操作之前的操作:

package main
+
+import (
+	"fmt"
+)
+
+var (
+	a    = 1
+	b    = 2
+	done bool
+)
+
+func main() {
+
+	go func() {
+		a = 3
+		b = 4
+		done = true
+	}()
+	for !done {
+	}
+	fmt.Println(b)
+	fmt.Println(a) // 可能观察到的就是输出的 4 3,但是无法保证必须是4 3 ,能观察到 != 保证
+}
+
+

下面这种情况,a goroutine 无法观察到 b goroutine 是否完成了写操作,有可能造成 a goroutine 一直被卡的现象:

package main
+
+import (
+	"fmt"
+)
+
+type Result struct {
+	data string
+}
+
+var (
+	r *Result
+)
+
+func set() {
+	d := new(Result)
+	d.data = "hi there" // 这里无法确认一定会运行
+	r = d // 这里无法确认观察到
+}
+
+func main() {
+	go set()
+	for r == nil {
+	}
+	fmt.Println(r.data)
+}
+
+

there is no guarantee that the write to done will ever be observed by main,since there are no synchronization events between the two threads。The loop in main is not guaranteed to finish。“

这一句是 go blog 中对于这代代码的描述,重点是说,这俩线程中,没有存在同步原语,所以无法做到从 main goroutine 去观测到 r 的状态,也就是说,在 goroutine 中只要存在了同步原语,那么就可以把两个 goroutine 当作正常的语句来看。

这里有两点无法确认,第一:主 goroutine 无法确认 r 的状态,这里指的就是可观测性,因为这里是非同步操作,所以主 goroutine 的读无法观测到 go set() 这里 r = d 这里的写,注意是无法完全确认,通常来说这段代码是可以运行的,我说的是通常,第二无法保证 d.data 一定会运行,所以有可能输出的是空字符串,这里说的就是因为编译优化导致的指令重排。

同步操作的 happens-before

go 不直接提供 cpu 屏障,来保证编译器或者 cpu 保证顺序性,而是使用不同架构的内存屏障指令来实现统一的并发原语。

单个 goroutine

上文说到,在一个 goroutine 中,happens- before 其实就等于代码书写的顺序,这一点是严格成立的

init 函数

go 语言的初始化是在单一的 goroutine 中执行的,也就是 main goroutine。

p 包导入了 q 包,那么 q 的 init 函数一定 happens before p 的任何初始化代码

这里有点像树,导入的过程,以及初始化的过程,会形成一个多叉树,从最底层的树开始进行初始化,逐步的忘上层走,先进后出,栈一样的感觉,并且相同的包只会导入一次,进而也只会初始化一次,比如 q 包被第三层导入了,在第五层也导入了,那么这个包在从底层往上走的过程中,只会在第五层先全局变量后 init 函数的初始化一次。这导入的包全部初始化一遍之后,才开始进行 main 包的 mian 函数,然后进而去运行接下来的逻辑。

var(
+  a = c + b
+
+  b = f()
+  
+  c = f()
+
+  d = 3 
+)
+
+func f() int{
+  d ++ 
+  return d 
+}
+

初始化的顺序是这样的,首先初始化 a,因为 a = c + b,那么系统开始初始化 c 和 b,c 和 b 等于一个函数 f,那么就开始初始化这个函数,函数里面是 d 那么开始初始化 d,所以 b = 3+1 c = 3+1+1 a = 9,这个时候 d 已经++ 两次了,所以最终的初始化以后 a = 9 b = 4 c = 5 d = 5

包级别的变量按照代码顺序初始化,同一个包的众多文件会按照文件名的排列顺序进行初始化。

goroutine

启动 goroutine 的 go 语言执行,一定 happens - before 此 goroutine 内的代码执行。退出可就没有任何 happens- before 的理论了,所以退出我们通常要部署同步语句。

var a string
+func f(){
+  fmt.Println(a)
+}
+func main(){
+  a = "hi"
+  go f()
+}
+

从单个 goroutine 的 happens- before 的关系来看,a = “hi” 一定 happens- before go f (),从新 goroutine 的开辟来说,go f () 一定 happens- before fmt.Println 所以这个代码中,hi 一定能被输出。

channel

go 语言有一句经典名言,不要使用共享内存来通信 (sync.Mutex) 而应该采用通信的方式来共享内存,这个后者说的就是 channel,channel 是同步操作的首选。

  • 往 channel 中发数据,happens - before 从这个 channel 接收数据完成。注意这里说的是接收完成,没说发数据 happens- before 这个 channel 接受数据开始的时候。
var c = make(chan int ,10)
+var a string 
+
+func f() {
+  //这里之所以a = hi 发生在 c <-0 之前,就是因为这个goroutine和main goroutine 存在同步原语,
+  //只要存在,那么代码就会按照程序员写的顺序执行,并不会进行重排。
+  a = "hi" 
+  c <-0  // 这里因为有了同步操作,所以 a = “hi” 在main goroutine看 也是 a= “hi” happens- before c <- 0
+}
+func main(){
+  go f()
+  <-c // 这里只要收到数据了,那么 c <- 0 肯定已经运行了,并且发送完毕了。
+  print(a) 
+}
+
  • close 一个 channel 一定 happens- before 从关闭的 channel 中读取一个零值。我们都知道可以从一个 closed 的一个 channel 中读取零值是可以的,这里说明的是读零值这个过程一定在关闭这个操作之后。

  • 一个 unbuffered 的 channle,读的准备好一定 happens-before 发准备好,意思是说,只有收数据准备好了,才能发,否则就会一直卡在发那个地方。

  • 一个容量大于 0 为 m 的 channel,第 n 个接收,一定 happens- before 第 n+m 的发送,意思是说,如果容量满了,必须先拿出来一个才能往里面再塞进去一个。比如:n=1 m=2,第一个接收一定 happens- before 第三个发送,因为容量一共是 2,要想往里面塞进去第三个,必须拿出来一个并且拿出来这个操作完毕了,才能发送第三个。

Mutex/RWMutex

  • 第 n 次的 unlock 一定 happends- before 第 n+1 次 lock 方法的返回 +意思是说,只有先解锁才能再次加锁。
  • 读写锁虽然有两把锁,但是不能同时使用,必须等待一个锁解锁后,另一个锁才能上锁,比如读锁解锁后才能再次上读锁,或者才能上写锁。

互斥锁可以由 a goroutine 上锁,b goroutine 解锁

var mu sync.Mutex
+var s string
+
+func f(){
+  s = "hi"
+  mu.Unlock() // 这里拥有了一个同步原语,所以 s = hi 一定发生在 mu.unlock() 之前。(相当于在一段锁的内部)
+}
+func main() {
+  mu.Lock()
+  go f()
+  mu.Lock() // 这里的lock 一定发生在  另一个goroutine 中f 的 mu.Unlock() 之后。
+  print(s)
+}
+

WaitGroup

  • wait 方法等到计数值归零才能返回 (运行完毕)

Once

  • 对于 once.Do(f) f 一定会在 do 方法返回之前执行。
var s string
+var once sync.Once
+
+func f(){
+  time.Sleep(time.Second)
+  s = "hi"
+}
+func main(){
+  once.Do(f)
+  print(s) // 这里 f 的执行完毕 一定在 Do()返回之前,所以一定能输出 s = hi
+}
+

atomic

按照 atomic load / store 顺序来保证 happens - before 不过 go 官方并没有严格定义,直到目前为止并没有严格定义。


+func main() {
+  var a, b int32 = 0, 0
+
+  go func() {
+    atomic.StoreInt32(&a, 1)
+    atomic.StoreInt32(&b, 1)
+  }()
+
+  for atomic.LoadInt32(&b) == 0{
+    runtime.Gosched()
+  }
+    fmt.Println(atomic.LoadInt32(&a))
+}
+

issues

使用channel实现一个互斥锁

我们利用 channel 发送数据 happens- before 收到数据完毕这个特性来实现互斥锁。

type Locker struct {
+	ch  chan struct{}
+}
+// 初始状态是已经有一个
+func NewLocker()*Locker{
+	ch := make(chan struct{},1)
+	ch <- struct{}{}
+	return &Locker{
+		ch: ch,
+	}
+}
+// lock 从缓存是1编程0,从chan中取出来数据
+func (l *Locker) Lock()  {
+	<- l.ch
+}
+// unlock 将缓存从0变成1,向ch中放入数据。
+func (l *Locker) Unlock() {
+	l.ch <- struct{}{}
+}
+

一个小小的经验:channel 拥有 buffer 比没有 buffer 常用很多。

完结。

参考资料

  • https://go.dev/ref/mem
  • https://time.geekbang.org/column/article/307469
+ + + diff --git "a/\345\271\266\345\217\221/\345\220\214\346\255\245\345\216\237\350\257\255/index.html" "b/\345\271\266\345\217\221/\345\220\214\346\255\245\345\216\237\350\257\255/index.html" new file mode 100644 index 000000000..5b4885821 --- /dev/null +++ "b/\345\271\266\345\217\221/\345\220\214\346\255\245\345\216\237\350\257\255/index.html" @@ -0,0 +1,791 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

同步原语

传统同步原语是 go 提供的相对底层的同步机制,它更加灵活,但是同时也更加复杂,如果可能的话,我们应该尽量使用 csp 的并发模型,使用 channel 去代替传统同步原语。

本单元讲的传统同步原语,channel 和 contex 也属于同步原语,他们属于 csp 的并发模型,所以他们会单独为列。

下面讲一下这么多同步原语 (也叫并发原语) 的基本功能:

  • sync.Mutex, sync.RWMutex 共享资源,避免数据竞争 (data race)
  • sync.Waitgroup, channel 任务编排,各个 goroutine 所代表的任务的前后顺序关系
  • channel 传递消息

这是基本的划分,当然这个划分还不严谨,但是你只需要知道,这些属于最常见的同步原语,以及他们最常见的功能。

sync.Mutex

下面介绍的众多并发原语,甚至下一章的 channel,都使用了这个核心内容,它就是 sync.Mutex,它是所有同步原语包含 channel 的底层核心。

Mutex 也就是互斥锁,它主要是为了解决多线程下数据的竞争问题,所以互斥锁是同步原语的最底层最核心的组件

让我们看几个场景

  1. 计数器,多个线程去更新一个计数器的时候,如果不加锁,就会出现数据的错误,本来你只加上了 1,正当你读的时候你发现别的线程也加上了 1,所以导致读取的错误

  2. 秒杀服务,这也是一个常见的场景,如果不加锁,就会导致超卖的情况出现

  3. 往一个 buffer 中传入数据,如果不加锁,数据的顺序就会发生乱序

临界区

临界区的概念是指在多线程的时候,临界区域的内容会被不同的线程去获取和释放,这个区域会发生数据的争夺问题。

这个区域因为会发生争夺,所以会被保护起来,可以这么说,临界区是多线程中整体数据中的共享部分

所以临界区是要保护的区域,一次只能让一个线程去使用

所以 sync.Mutex 就是用来保护临界区的,它可以保证临界区的互斥

共享资源通常是某个变量,例如临界区是变量 count,临界区操作是 count++,只要在临界区前面获取 +锁,在离开临界区的时候释放锁,就能解决 data race 的问题。

sync.Mutex 基础操作

我们现看一下它的基础使用功能:

func age(){
+  var mu sync.Mutex
+  for i := 0; i <10; i++ {
+    go func(){
+      mu.Lock()
+      defer mu.Unlock()
+    }()
+  }
+}
+
+// 跨goroutine 加解锁
+func age1() {
+	var mu sync.Mutex
+	mu.Lock()
+	defer mu.Unlock()
+	go func() {
+		defer mu.Unlock()
+		time.Sleep(1000)
+		fmt.Println("hi there")
+	}()
+	mu.Lock()
+}
+

sync.Mutex 即为互斥锁,规则是:

  • 锁的加锁和解锁可以跨 goroutine 使用,比如 a goroutine 加锁,在 b goroutine 将锁解开。
  • 只有现解锁才能继续上锁,happens-before 就是:n 次解锁一定 happens-before n+1 次加锁

sync.Locker 接口

type Locker interface{
+  Lock()
+  Unlock()
+}
+

Mutex 就实现了这个 Locker 接口

Locker 定义了锁的基本方法,加锁 + 解锁

什么是 data race 的本质

我们常说 data race 的情况,是多线程同时对于某块内存进行数据的变更,那么问题来了,这个地方谈到的同时,是真的物理层面的同时还是近似同时?

关于 data race 中的 “同时” 通常是指逻辑上的同时或近似同时,而不是物理层面严格的同一时刻。

主要原因有以下几点:

  • 现代 CPU 中,同一时刻真正执行指令的只有一个核心。不同核心之间以及同一核心的不同执行周期之间,不存在物理层面严格的同步。
  • 即使在单核 CPU 上,由于指令流水线、内存缓存、分支预测等机制,实际执行顺序也可能与代码顺序不一致,很难定义物理层面严格的同步。
  • 不同线程之间进行切换的时间间隔非常小 (几十到几百纳秒),对程序逻辑而言可以视为同时进行。
  • 要构成 data race,不同线程对同一地址的访问之间间隔不能太长,必须在一个指令/操作的启始和完成之间,所以也符合逻辑上的近似同时。

所以,data race 中的 “同时” 就是指逻辑上近似同时,或者无法确定准确执行顺序的情况,而不是物理层面严格同一时刻。这种近似同时从程序角度就是可能造成冲突,需要进行同步处理。

检测 data race 的工具

并发问题不是一定能肉眼看出来的,如果只是基础的,容易看出来的也就罢了,但是很多隐藏的 data race 问题必须使用专业的工具去鉴别,go 语言提供了 -race 功能,在编译,测试,run 的时候,会自动检测到 data race 问题,并且给出详细的错误信息。

 go run -race main.go
+

我们看一个例子

func main() {
+	value := 0
+	for i := 0; i < 10000; i++ {
+		 go func(){
+			value ++
+		 }()
+	}
+	time.Sleep(1000)
+	fmt.Println(value)
+}
+

本能的你会以为能输出 10000,但是结果确实 9000 多,而且还不一定,这是为啥呢?

因为你以为 ++ 操作是原子操作,其实并不是。

++ 操作分为三个步骤

  • 获取 value 值
  • 值+1
  • 将新值写回

这其实是三个步骤,10000 个线程,假如同时有 10 个去读了这个 value,在他们看来都是初始值是 0,然后他们+1,然后写回去了结果 value 是 1,相当于 10 个 goroutine 都去写,本来应该是 10,但是赋值回去都变成了 1

这个时候,如果你使用 run -race 就能检测出来

go1 go run -race main.go 
+==================
+WARNING: DATA RACE
+Read at 0x00c00010a018 by goroutine 8:
+  main.main.func1()
+      /Users/shgopher/Desktop/1/go1/main.go:23 +0x2c
+
+Previous write at 0x00c00010a018 by goroutine 6:
+  main.main.func1()
+      /Users/shgopher/Desktop/1/go1/main.go:23 +0x3c
+
+Goroutine 8 (running) created at:
+  main.main()
+      /Users/shgopher/Desktop/1/go1/main.go:22 +0x48
+
+Goroutine 6 (finished) created at:
+  main.main()
+      /Users/shgopher/Desktop/1/go1/main.go:22 +0x48
+==================
+9957
+Found 1 data race(s)
+exit status 66
+

多线程多某个区域的内存进行同时 (或者近似同时) 操作,这就是数据竞争

使用这个内置工具有个很大的缺陷,就是只有在数据真的执行中发生了数据竞争才能被发现,并且,使用-race 会影响编译的程序执行速度

如果我们使用 go tool compile -race -S x.go 的时候就发现使用-race 之后,编译的时候 go 编译器往代码中加入了很多运行时监控代码,这些运行时的监控代码会影响性能

	0x001c 00028 (a.go:3)	PCDATA	$1, $0
+	0x001c 00028 (a.go:3)	CALL	runtime.racefuncenter(SB)
+	0x0020 00032 (a.go:4)	MOVD	$type:int(SB), R0
+	0x0028 00040 (a.go:4)	CALL	runtime.newobject(SB)
+

runtime.xx 的代码就是添加的运行时监控

小知识,使用 go tool compile 的时候不要加入第三方库,标准库也不行,编译工具只能编译本文件,跟 go build 那种能查找依赖树的方式不同

将互斥锁嵌入到结构体中

type MyAge struct {
+  mu sync.Mutex
+  value int
+}
+

或者直接嵌入

type MyAge struct {
+  sync.Mutex
+  value int
+}
+
func main() {
+	var age MyAge
+	for i := 0; i < 10000; i++ {
+		go func(){
+			age.Lock()
+			defer age.Unlock()
+			age.value ++
+		}()
+	}
+	time.Sleep(1000)
+	fmt.Println(age.value)
+}
+

sync.Mutex 互斥锁的实现原理

go 语言互斥锁的实现非常简单,只有这一个结构体就是核心:

type Mutex struct {
+  state int32
+  sema uint32
+}
+

在阅读下面互斥锁的几个阶段之前,建议先读一下 G:M:P 模型

互斥锁演变的四个阶段一:简单实现

// 最初的代码
+type Mutex struct {
+  key int32 // 锁是否被持有的标志,1 被持有,0 没有被持有
+  sema uint32 // 锁的具体状态,此信号量具有高级语意,用来控制goroutine的状态
+}
+// compare and swap 操作:val 和 old 进行对比,如果相同,使用new去替代 val的值
+func cas(val *int32, old,new int32)bool{}
+// val 数据添加一个 delta 值,返回新的值
+func xadd(val *int32, delta int32)(new int32){
+  for {
+    v := *val
+    if cas(val,v,v+delta) {
+      return v+delta
+    }
+  }
+  panic("unreached")
+}
+
+func (m *mutex)Lock() {
+  if xadd(&m.key,1) == 1 { // 标识+1 ,如果等于1 获取到锁
+    return 
+  }
+  // 这里就是说,当key >1 的时候,我们通知goroutine休眠等待锁
+  // 只有key 等于 1 才能算获取锁
+  semacquire(&m.sema)
+}
+func (m *mutex)Unlock() {
+  if xadd(&m.key,-1) == 0 { // 标识-1 ,如果等于0 表示没有其它等待者
+    return
+  }
+  // 这个函数是汇编语言写的,上面那个 semacquire 也是
+  semrelease(&m.sema) // 唤醒等待锁的其它的goroutine中的一个
+}
+

可以看到,这种简单的实现,完全没有考虑任何的情况,仅仅是简单的加锁和解锁,不考虑 goroutne 的任何情况,就是随机的,随机的获取锁然后解锁。

注意 go 语言的锁可以 a 加 b 解,所以一定要谁加锁就谁解锁,不然就无法构成互斥锁这个概念了。

互斥锁演变的四个阶段二:优先新 goroutine

在这个演变中,go 的互斥锁调度会优先将锁权分给新创建的 goroutine

const (
+  // 1
+  mutexLock = 1 << iota
+  // 2
+  mutexWoken
+  // 2
+  mutexWaiterShift = iota
+)
+type Mutex struct {
+  state int32
+  sema uint32
+}
+
+

state 的内容变成了三个:

  • 第一个字段:mutexWaiters 阻塞等待的数量
  • 第二个字段:mutexWoken 唤醒标记
  • 第三个字段:mutexLocked 持有锁的标记

我们先前知道,如果想要获取锁的 goroutine 没有机会获取到锁,就会进行休眠,但是在锁释放唤醒之后,它并不能像先前一样直接获取到锁,还是要和正在请求锁 goroutine 进行竞争。这会给后来请求锁的 goroutine 一个机会,也让 CPU 中正在执行的 goroutine 有更多的机会获取到锁,在一定程度上提高了程序的性能。

在这次演变中,go 的调度器会将新的 goroutine 给赋予锁,因为新的 goroutine 就是正在 cpu 执行片段中执行的 goroutine,这个时候将锁给他们无疑是效率最高的。

互斥锁演变的四个阶段三:多给机会,优先新 goroutine 以及被唤醒的 goroutine

如果新来的 goroutine 或者是被唤醒的 goroutine 首次获取不 +到锁,它们就会通过自旋 (spin,通过循环不断尝试) 的方式,尝试检查锁是否被释放。在尝试一定的自旋次数后,再执行原来的逻辑。

互斥锁演变的四个阶段四:在第三个阶段的基础上引入饥饿模式

为什么会加入饥饿模式呢?就是因为如果都优先给新的 goroutine,那么等待队列中的 goroutine 就永远不会被执行,所以引入了饥饿模式,优先执行等待中的 goroutine +然后新的 gorontine 就被添加到了等待队列中的队尾,这个时期称之为饥饿模式,因为新的 goroutine 基本上都要在 cpu 的时间片段中执行,所以在这个模式下,执行效率反而是底下的,因为正在执行的 goroutine 被强行放到队尾了。

当等待的队首的 goroutine 等待时间超过 1ms 就会进入这个模式

当队首的 goroutine 等待时间小于 1ms 或者已经执行到队尾了,那么这个模式就会从饥饿模式改为正常的模式

sync.Mutex 易错的几种场景

Lock/Unlock 不是成对出现

因为 go 语言中,互斥锁是无法获取 goroutine 的信息的,所以存在 a 锁 b 解的情况,即:a goroutine 上了锁,b goroutine 给解开了。

如果你不是为了实现锁,是为了任务编排,那么可以这么做。

如果是为了锁,请不要这么做,因为这么做的后果就是这将不能形成锁这个概念

或者说当你使用了 lock 的时候忘记 unlock 了,那么最终都会导致系统走向失败

Copy 已经使用的 Mutex

go 语言的 mutex 使用 state 字段去表示锁的含义,所以当你 copy 一个锁的时候,实际上已经 copy 了这个锁的状态,这将导致错误的结果

go 语言的同步原语众多,使用的底层都是 mutex (包括 channel),所以说,不仅仅是 mutex 不能使用 copy,其它的同步原语都不能。

重入

所谓重入,就是多次上锁,注意这里是拥有锁的这个线程去请求这把锁

go 语言不支持重入,系统会 panic,这种重入锁无法实现也跟 go 语言的互斥锁没有记录使用它的 goroutine 有关系

那么如果 go 语言也实现一个重入锁,核心就是将持有锁的 goroutine 的 id 记录下来

死锁

当多线程的情况下,多个线程陷入了争抢资源的情况,当他们都陷入了停滞状态,或者阻塞状态的时候,就会发生死锁,deaadlock

一般来讲,当你发现系统多个线程都堵死的时候,就会发生死锁情况了,但是通常发生死锁是发生在满足这四个情况下

  • 互斥:资源具有排他性,只能有一个 goroutine 访问
  • 持有和等待:goroutine 持有资源,并还在请求其它资源
  • 不可剥夺:资源只有被它持有的 goroutine 释放
  • 环路等待:发生了环路等待事件,下面这个案例可以解释这个理论

举一个案例

package main
+
+import(
+  "sync"
+  "fmt"
+  "time"
+)
+
+func main(){
+  var wg sync.WaitGroup
+  var mu1 sync.Mutex
+  var mu2 sync.Mutex
+  wg.Add(2)
+  go func(){
+    defer wg.Done()
+    mu1.Lock()
+    defer mu1.Unlock()
+    time.Sleep(1000)
+    mu2.Lock()
+    defer mu2.Unlock()
+  }()
+  go func(){
+    defer wg.Done()
+    mu2.Lock()
+    defer mu2.Unlock()
+    time.Sleep(1000)
+    mu1.Lock()
+    defer mu1.Unlock()
+  }()
+  wg.Wait()
+}
+

在这个案例中,mu1 和 mu2 代表两个资源,两个 goroutine 在争夺这两个资源,下面我们盘点一下上文说的四个理论知识:

  • 互斥性:资源只能被一个 goroutine 持有
  • 持有和等待,一个 goroutine 获取了一把锁,还想获取第二把
  • 不可剥夺,持有锁的 goroutine 释放锁后,其他 goroutine 不能再获取该锁
  • 环路等待,两个 goroutine 陷入了环路这个概念总,第一个先持有 mu1,第二个 goroutine 先持有 mu2,他们又分别要获取另一个锁,所以陷入了环路等待中

h

所以这个案例中,发生了死锁

fatal error: all goroutines are asleep - deadlock!
+
+goroutine 1 [semacquire]:
+sync.runtime_Semacquire(0xc0000a4020?)
+	/usr/local/go-faketime/src/runtime/sema.go:62 +0x25
+sync.(*WaitGroup).Wait(0x0?)
+	/usr/local/go-faketime/src/sync/waitgroup.go:116 +0x48
+main.main()
+	/tmp/sandbox1712910389/prog.go:31 +0x125
+
+goroutine 17 [sync.Mutex.Lock]:
+sync.runtime_SemacquireMutex(0x1?, 0x58?, 0x459218?)
+	/usr/local/go-faketime/src/runtime/sema.go:77 +0x25
+sync.(*Mutex).lockSlow(0xc0000a2028)
+	/usr/local/go-faketime/src/sync/mutex.go:171 +0x15d
+sync.(*Mutex).Lock(...)
+	/usr/local/go-faketime/src/sync/mutex.go:90
+main.main.func1()
+	/tmp/sandbox1712910389/prog.go:20 +0xd1
+created by main.main in goroutine 1
+	/tmp/sandbox1712910389/prog.go:15 +0xb9
+
+goroutine 18 [sync.Mutex.Lock]:
+sync.runtime_SemacquireMutex(0x1?, 0x58?, 0x459218?)
+	/usr/local/go-faketime/src/runtime/sema.go:77 +0x25
+sync.(*Mutex).lockSlow(0xc0000a2020)
+	/usr/local/go-faketime/src/sync/mutex.go:171 +0x15d
+sync.(*Mutex).Lock(...)
+	/usr/local/go-faketime/src/sync/mutex.go:90
+main.main.func2()
+	/tmp/sandbox1712910389/prog.go:28 +0xd1
+created by main.main in goroutine 1
+	/tmp/sandbox1712910389/prog.go:23 +0x119
+
+

sync.Mutex 扩展

这里主要讲解如何对 sync.Mutex 进行功能扩展

基本的原理就是将 sync.Mutex 嵌入到一个新的接口体中,然后重载 Lock 和 Unlock 的方法进行改造

改造一个重入锁

type RecursiveMutex struct {
+  sync.Mutex
+  owner int64 // goroutine id
+  recursion int64 //当前goroutine重入次数
+}
+

lock 操作

func(m *RecursiveMutex)Lock(){
+  // 第三方包,目的是获取正在获取锁的lock 操作的 runtime id
+  // github.com/petermattis/goid
+  gid := goid.Get()
+
+  // 判断当前goroutine是否是要重入的goroutine
+  if atomic.LoadInt64(&m.owner) == gid {
+    // 重入指标+1
+    atomic.AddInt64(&m.recursion, 1)
+    return
+  }
+  m.Mutex.Lock()
+  // 获取得到锁的goroutine的id
+  atomic.StoreInt64(&m.owner, gid)
+  atomic.StoreInt64(&m.recursion, 1)
+
+}
+

unlock 操作

func(m *RecursiveMutex) Unlock(){
+  gid := goid.Get()
+
+  // 非持有锁的goroutine去释放锁直接panic
+  if atomic.LoadInt64(&m.owner) == gid {
+    panic("wrong",m.owner,gid)
+  }
+  // 重入指标-1
+  atomic.StoreInt64(&m.recursion,&m.recursion -1)
+
+  if atomic.LoadInt64(&m.recursion) != 0 { // 持有的goroutine还没有全部unlock
+    return 
+  }
+  // 将 重入指标设置为-1
+  atomic.StoreInt64(&m.recursion,-1)
+  m.Mutex.Unlock()
+}
+

sync.RWMutex

上文我们提到了互斥锁,互斥锁是真的锁,不论是读还是写都只能有一个 goroutine 去操作,所以说互斥锁才是真的锁,那么我们这里讲的读写锁是什么含义呢?

读写锁:允许多个 goroutine 同时去读一个数据,但是这个时候是不允许写的;只允许一个 goroutine 去写一个数据,并且不允许其它 goroutine 去读这个数据。

所以,读写锁跟互斥锁相比,把读的权限给增大了,但是写的权限不变。

当我们遇到一个读多写少的场景,那么使用读写锁的效率要比互斥锁的效率高的多。

读写锁拥有以下几个方法:

  • Lock/Unlock:写操作时加的锁
  • Rlock/RUnlock:读操作时加的锁
  • Rlocker:为读操作返回一个 Locker 接口的对象

RWMutex 跟 mutex 一样,初始值都是未加锁的状态,当然他们都是有状态的结构体,所以也不能复制锁,因为初始值就是未加锁,所以直接声明即可。

总结一下读写锁的几个注意事项

  • 上文提到了,不可复制
  • lock 和 unlock 要成对出现;Rlock 和 RUnlock 也要成对出现

下面我们看一下,使用读写锁造成的死锁问题

// 这个写法出问题的原因是:读锁还没结束就开启了写锁
+func main() {
+	var mu sync.RWMutex
+
+	mu.RLock()
+	mu.Lock()
+	mu.Unlock()
+	mu.RUnlock()
+
+}
+

出现死锁的原因就是出现了环形等待,读锁等待写锁解锁,写锁等待读锁解锁

sync.WaitGroup

它的作用是任务的编排

waitgroup 一共有三个方法:

  • Add(delta int):增加 delta 个任务
  • Done():任务完成,减少一个任务
  • Wait():阻塞等待,直到所有任务完成
func main() {
+  var wg sync.WaitGroup
+  wg.Add(2)
+  go func(){
+    defer wg.Done()
+    time.Sleep(1000)
+  }()
+  go get(&wg)
+  wg.Wait()
+}
+
+func get(wg *sync.WaitGroup) {
+  defer wg.Done()
+  time.Sleep(1000)
+}
+

常见错误

  • add 的时候数值传入负值
  • done 的次数超过 add 中的次数
  • 在 add 之前调用了 wait
  • 当前一个 waitgroup 还没有完结就开始重用了 waitgroup,也就是说,必须等到上一轮的 wait 执行完毕了才能开启新的一轮

最后这个错误我们看一个案例:

func main(){
+  var wg sync.WaitGroup
+  wg.Add(1)
+  go func(){
+    time.Sleep(1000)
+    wg.Done()
+    wg.Add(1) // AA
+  }()
+  wg.Wait()
+}
+

在这个案例中,AA 处的 add 行为有可能发生在主 goroutine 之后,那么相当于一轮未结束又开启了新的一轮,就会发生 panic 行为

当然了,我不是说 add 只能调用一次,但是 add 虽然能调用多次,但是不能发生在 wait 之后

正确的多次调用 add 的案例:

func main(){
+  var wg sync.WaitGroup
+
+  for i:=0;i<10;i++{
+    wg.Add(1)
+    go func(){
+      defer wg.Done()
+      time.Sleep(1000)
+    }()
+  }
+  wg.Wait()
+}
+

注意,我们这里每次循环都调用了一次 add,但是 add 的调用始终发生在 wait 之前,这还是属于同一轮的多次 add 调用,这符合 waigroup 的规定

SingleFlight

著名缓存库 groupcache (opens new window) 就使用了 singleflight 去通过缓存来减少后端查询数据库的请求

在处理多个 goroutine 同时调用同一个函数的时候,只让一个 goroutine 去调用这个函数,等到这个 goroutine 返回结果的时候,再把结果返回给这几个同时调用的 goroutine

在面对多个 goroutine 并发去读一个数据的时候,使用 SingleFlight 可以大大降低请求量,从 n 的请求量降低到 1,比如在秒杀的场景下,n 个 goroutine 去请求数据,那么我们使用 SingleFlight 就能大大提高读的性能

SingleFlight 提供了三个公开方法:

  • func (g *Group) Do(key string,fn func() (interface {},error)) (v interface {},err error,shared bool)

  • func (g *Group) DoChan(key string,fn func() (interface {},error)) <-chan Result

  • func (g *Group) Forget(key string)

  • Do:Do 执行并返回函数的结果,同样 key 值下的只能有一个 goroutine 持有的 do 方法会执行,其它的都会等待,直到唯一一个执行完毕有了结果,那么大家 (指都执行 do 的众多 goroutine) 都有了结果

  • DoChan:跟 do 类似,返回值是一个 channel

  • Forget:告诉这个 group,忘记 key,之后,这个 key 请求回执行 f 函数,而不是等待前一个未完成的 fn 函数的结果

      sf := &sync.SingleFlight{} 
    +
    +  sf.Do("param", func() interface{} {
    +      // ...function logic
    +      return result 
    +  })
    +  	sf.Do("param", func() interface{} {
    +  		//...
    +  		return result
    +  	})
    +
    +  	// 对于同一个key 下(这里是param)这些动作只执行一次,后续注册的函数会直接返回第一个函数的结果。
    +  	//
    +
    +
    +  // 后续想重新执行
    +  sf.Forget("param") 
    +
    +  sf.Do("param", func() interface{} {
    +      // 这次会再次执行函数逻辑
    +  })
    +

下面我们看一下 DoChan 的基本使用方法:

package main
+
+import (
+	"fmt"
+
+	"golang.org/x/sync/singleflight"
+)
+
+func main() {
+	g := new(singleflight.Group)
+
+	block := make(chan struct{})
+
+	res1c := g.DoChan("key", func() (interface{}, error) {
+
+		<-block
+
+		return "func 1", nil
+
+	})
+
+	res2c := g.DoChan("key", func() (interface{}, error) {
+
+		<-block
+
+		return "func 2", nil
+
+	})
+
+	close(block)
+
+	res1 := <-res1c
+
+	res2 := <-res2c
+
+	// 使用相同的key执行的函数共享结果
+
+	fmt.Println("Shared:", res2.Shared)
+
+	// 只有第一个函数被执行:它使用"key"被注册和启动,在第二个函数被注册相同的key之前就完成了执行。
+
+	fmt.Println("Equal results:", res1.Val.(string) == res2.Val.(string))
+
+	fmt.Println("Result:", res1.Val)
+}
+
+
Shared: true
+Equal results: true
+Result: func 1
+

g.DoChan / g.Do 都会在内部会启动一个新的 goroutine 来执行传入的函数。

g.DoChan 的签名如下:

func (g *Group) DoChan(key string, fn func() (interface{}, error)) <-chan Result
+

它接受一个 key 和一个函数 fn,并返回一个 Result 类型的 channel。

在 g.DoChan 内部,会首先根据 key 在内部 map 中查找是否已经有相同 key 的函数在执行:

  • 如果存在,则直接返回已有的 Result channel
  • 如果不存在,则启动一个新的 goroutine 执行 fn 函数,并将 Result 发送到返回的 channel 中

所以 g.DoChan 会确保对于相同的 key,最多只有一个 goroutine 在执行 fn,后续的调用会直接复用已有的结果。

这就实现了 key 去重重复执行的语义。

所以,g.DoChan 会隐式地启动 goroutine 来运行函数,这是它实现并发和协调的基础。

我们在处理缓存击穿的问题时,通常采用 singleflight 会有比较好的适用,所谓缓存击穿就是大量请求在请求一个 key 值时,key 值失效了,大量数据开始请求数据库,使用 singleflight 时,大量数据只需要一次请求,完美解决了缓存击穿问题

缓存击穿:指一个 key 非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个 key 在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像弹丸穿透目标一样。当某个 key 在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存,会对数据库造成压力。

缓存穿透:指查询一个数据库一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义。

缓存雪崩:指在某一个时间段,缓存集中过期失效。所有访问都落到数据库上,对数据库造成巨大压力。这通常因为缓存服务器宕机或缓存时效过短导致,可通过服务器保护或增大缓存过期时间来避免。

cyclicBarrier

github.com/marusama/cyclicbarrier

允许一组 goroutine 彼此等待,到达一个共同的执行点。同时,因为它可 +以被重复使用,所以叫循环栅栏

基本用法就是 Await 方法,等待所有的参与者到达,到达了就往下走,然后开始新的循环

那么看起来很像 waitgroup,那么为什么不使用 wg 呢?

在一种场景下 wg 通常很难使用,也就是循环这个含义,因为你需要在 wait 之后再次调用 add 方法充值,然后继续 done wait,麻烦,并且万一在继续 add 的时候发生了并发问题就跟灾难了

  • WaitGroup 更适合用在 “一个 goroutine 等待一组 goroutine 到达同一个执行点” 的场景中,或者是不需要重用的场景中。

  • CyclicBarrier 更适合用在 “固定数量的 goroutine 等待同一个执行点” 的场景中,而且在放行 goroutine 之后,CyclicBarrier 可以重复利用,不像 WaitGroup 重用的时候,必须小心翼翼避免 panic。

// 数字代表执行任务的 goroutine 的个数
+b1 := cyclicbarrier.New(10) 
+// 或者带有方法的循环栅栏
+b2 := cyclicbarrier.NewWithAction(10, func() error { return nil }) 
+
+b.Await(ctx)    // await other parties
+// 将循环栅栏重置
+b.Reset()       // reset the barrier
+

我们可以发现循环栅栏会一直循环的执行,虽然它有 await 方法,但是每次到这个 await 都会继续执行下一轮的循环,那么该如何跳出循环呢?

通常我们会配合 sync.WaitGroup 一起执行

errgroup

将一个通用的父任务,拆成几个小任务并发执行的场景

  • WithContex 表示创建一个 group 实例,并且创建一个 withcancel 的上下文 context,一旦子任务返回错误,或者 wait 调用返回,context 就会被 cancel
  • Go Go(f func()error) 传入的子任务,一旦 error,就会促发 withcancel 的 cancel 操作
  • Wait 等待所有任务都完成后,wait 才会执行

sync.Once

once 用来执行仅发生一次的动作,常用与单例模式,对象初始化的行为,并且经常在 init 函数中使用

sync.Once 仅仅暴漏了一个 do 方法,而且多次调用 do,仅有第一次的无返回值的 f 函数可以执行,即便 f 不同:

var once sync.Once
+
+func init() {
+  once.Do(func() {
+    // 仅执行一次
+  })
+  // 这次不会执行
+  once.Do(func() {
+    
+  })
+}
+

讨论线程安全的 map 在多线程中的使用

go 语言中的 map 并不是并发安全的,一个 map 如果不加锁的去处理数据的时候就会出现 panic 的情况,比如:

package main
+
+import (
+	"fmt"
+	"sync"
+	"time"
+)
+
+func main() {
+	var m = make(map[int]int, 10) // 初始化一个map
+	go func() {
+		for i := 0; i < 100000; i++ {
+			m[1] = 1 //设置key
+		}
+	}()
+	go func() {
+		for i := 0; i < 100000; i++ {
+			fmt.Println(m[2]) //访问这个map
+		}
+	}()
+	time.Sleep(1000000)
+}
+

这种写法就会发生 panic,原因是 go 语言不支持并发读写 map,必须加锁

其实我们如果分析这段代码,并没有说对同一个 key 值进行读写,也没有涉及到扩容的问题,但是仍然会 panic,go 在操作 map 时会进行 data race 的检测,只要检测有,就会直接 panic

直接加锁

我们可以人为的加锁,这样就可以避免 data race 的行为

package main
+
+import (
+	"fmt"
+	"sync"
+	"time"
+)
+
+func main() {
+	var mu sync.Mutex
+	var m = make(map[int]int, 10) // 初始化一个map
+	go func() {
+		for i := 0; i < 100000; i++ {
+			mu.Lock()
+			m[1] = 1 //设置key
+			mu.Unlock()
+		}
+	}()
+	go func() {
+		for i := 0; i < 100000; i++ {
+			mu.Lock()
+			fmt.Println(m[2]) //访问这个map
+			mu.Unlock()
+		}
+	}()
+	time.Sleep(1000000)
+}
+

如果数据量比较低的话,这么做毫无问题,如果数据量较大,或者每次操作都比较耗时,读写公用一锁就比较浪费了。

那么可以使用读写锁吗?当然可以啦,我们使用读写锁可以更优秀的去解决这个问题

package main
+
+import (
+	"fmt"
+	"sync"
+	"time"
+)
+
+func main() {
+	var mu sync.RWMutex
+	var m = make(map[int]int, 10) // 初始化一个map
+	go func() {
+		for i := 0; i < 100000; i++ {
+			mu.Lock()
+			m[1] = 1 //设置key
+			mu.Unlock()
+		}
+	}()
+	go func() {
+		for i := 0; i < 100000; i++ {
+			mu.RLock()
+			fmt.Println(m[2]) //访问这个map
+			mu.RUnlock()
+		}
+	}()
+	time.Sleep(1000000)
+}
+

RWMutex 在同时有读写需求时,会优先获取写锁,读锁需要等待

如果当前有读锁,则后续的写锁请求会被阻塞,但读锁可以继续获取,

如果当前有写锁,则后续的读锁和写锁请求都会被阻塞。

所以,如果读多写少,使用读写锁是非常方便的,假如读和写都异常的高,那么读和写其实是不能同时进行的,如果读贼多,写就可能被阻塞等待了。

细颗粒度并发安全 Map

我们都知道,锁对于性能的影响是特别大的,尤其是线程非常多的时候,那么多线程公用一个锁,各种等待,能不影响效率吗,那么怎么做能提高效率呢?

降低锁的颗粒度就可以提高效率,换言之就是本来 1000 个线程公用一个,现在,我们把数据分为 10 份,100 个线程用一个锁,性能就能大范围的提高

我们可以使用 https://github.com/orcaman/concurrent-map 这个分片儿锁去替代互斥锁

分片儿锁的基本原理就是将一个大的 map 的内容,变成 10 个或者是更多个 map 的内容,我们可以这么做:

本身需要一个 map 的数据结构,我们改成一个 slice,slice 含有 10 个 map 的数据结构 +我们还需要一个定位分片的算法,基本上都是使用一个哈希算法先定位分片,然后后续就跟一般的互斥锁一致了。 +用法如下:

// Create a new map.
+	m := cmap.New[string]()
+
+	// Sets item within map, sets "bar" under key "foo"
+	m.Set("foo", "bar")
+
+	// Retrieve item from map.
+	bar, ok := m.Get("foo")
+
+	// Removes item under key "foo"
+	m.Remove("foo")
+

sync.Map

这是 go 官方提供的一个线程安全的 map,先说使用场景,只写一次,大量读的场景。

sync.Map 跟分片锁不同,分片锁是直接降低颗粒度,sync.Map 它的基本原理是读写分离,用空间换时间。通过一个只读的数据结构来提高读取速度。

当读少写多的时候,它的效率甚至比直接使用互斥锁还低,总之如果不是写少读多的场景,千万不要用,这个包的使用率挺低的。

sync.Pool

sync.Pool 是一个对象池,如果我们有一些重复使用的,并且需要频繁的申请和释放的临时对象,那么可以用这个对象池来提高性能。不过这个池子里的对象有可能会被垃圾回收,所以非常重要的数据不能使用这种方法

我来描述一种场景,我们有数据需要被 goroutine 去处理,但是谁处理都行,不 care 是哪位,那么我们就可以创建很多的 goroutine,然后放入到 goroutine 池中 (就跟外包一样。。。)

sync.Pool 有两个注意事项,首先,它线程安全,其次,不能复制 sync.Pool,如果你复制一个 sync.Pool,实际上得到的只是一个指针的拷贝,并不会复制本地池子,所以多个拷贝的 sync.Pool 指针指向的是同一个本地池子,达不到复用的目的。应该定义一个全局的 sync.Pool 实例,不同的 goroutine 都使用这个实例,才能达到对象复用和减少内存分配的目的

pool 包拥有三个方法

  • New,pool 的 new 方法后面跟的是创建的内容,一旦 pool 里面空了,就会调用这个 new 方法,当然你也可以不指定这个参数,那么创建的就是 nil 了
  • Get 从 pool 中获取一个对象
  • Set 将对象送回 pool 中

下面举一个演示的例子:

var buffer = sync.Pool{
+  New: func() any {
+    return new(bytes.Buffer)
+  },
+}
+
+func GetBuffer()*bytes.Buffer {
+  return buffer.Get().(*bytes.Buffer)
+}
+func PutBuffer(buf *bytes.Buffer){
+  // Reset会将缓冲区重置为空,但它会保留底层存储以供将来写入使用
+  buf.Reset()
+  buffers.Put(buf)
+}
+

这段代码是会有内存泄露的风险的,原因也很简单,buf.Reset() 并不会删除底层 slice 的容量,它会保存底层数据结构中的容量,所以 gc 的时候这个 buffer 就非常有可能不会被回收,造成内存泄露

改正方法也很简单,底层数据太大了直接丢弃 gc 就 ok 了,小的放到池子里

func PutBuffer(buf *bytes.Buffer){
+  // Reset会将缓冲区重置为空,但它会保留底层存储以供将来写入使用
+  buf.Reset()
+  if buf.Cap() > maxSize{
+    return 
+  }
+  buffers.Put(buf)
+}
+

pool 内存浪费

当我们需求的内存比池子中的内存小很多的时候,就会造成内存浪费,解决方法就是多造几个池子,比如小池子,中池子,大池子,使用这种方案合理的使用内存

var(
+  readerPool   sync.Pool
+  reader2kPool sync.Pool
+  reader4kPool sync.Pool
+)
+

推荐一个三方库,特点是能更加高效的发挥系统性能节约内存以及避免内存泄露问题等,他可以动态的去调节池子的 dafault size 和 max size

https://github.com/valyala/bytebufferpool

连接池也是一个很常见的需求,但是通常不会使用 pool,因为 pool 会被 gc,所以并不靠谱,通常我们使用 map 或者 slice 去实现一个需要真的长时间稳定连接的连接池比如:

  • http client 池
  • tcp 连接池,推荐三方库:https://github.com/fatih/pool
  • 数据库连接池
  • memcached client 连接池

worker pool

worker pool 其实就是 goroutine 的池子,虽然 go 语言的 goroutine 非常的轻量化,但是如果几十万上百万的 goroutine 被创建还是会出问题的

因为 goroutine 是一种要求长期存在的资源,所以一般不使用 pool,而是使用 channel 去作为池子,毕竟 channel 自带线程安全

Worker pool 的目的是为了重用 goroutine。但它重用的不是 goroutine 本身,而是通过 goroutine 执行任务的能力。

如果直接在 channel 中传递 goroutine,那么每个 goroutine 只能执行唯一的一个任务,执行完就退出了。这并没有实现重用。

而 worker pool 的设计是:

创建固定数量的 goroutine 作为 workers。 +workers 在 loop 中持续从任务 channel 接收任务并执行。 +向任务 channel 不断发送不同的任务。 +这样,每个 worker(goroutine) 就可以执行多个任务,实现重用。

所以 channel 中的内容不是 goroutine,而是任务信息,用于指导 goroutine 执行什么任务。goroutine 从 channel 收任务,利用自身的执行能力反复执行不同的任务。

这才是 worker pool 设计的核心思想 - 通过固定数量的 goroutine 反复执行不同的任务,以重用 goroutine 实现高效调度。

所以,worker pool 的目的是重用 goroutine 的执行能力,而不是重用 goroutine 本身。这是通过 channel+goroutine 的组合实现的,channel 用于传递任务信息,goroutine 负责执行任务

让我们简单的实现一个方案:

// 任务类型
+type Task struct {
+  f func() // 任务函数
+}
+
+// 执行任务
+func (t *Task) Execute() {
+  t.f() 
+}
+
+// 单个worker池 
+type Worker struct {
+  TaskChan chan *Task
+}
+
+func (w *Worker) Start() {
+  for {
+    task := <-w.TaskChan
+    task.Execute() 
+  }
+}
+
+// 为worker 池 分配 goroutine ,这得结合下面的 Newpool 才能看出来
+type Pool struct {
+  TaskChan chan *Task
+  Workers []*Worker
+} 
+
+// 创建worker池
+func NewPool(numWorkers int) *Pool {
+  
+  taskChan := make(chan *Task)
+
+  // 初始化workers
+  var workers []*Worker
+  for i := 0; i < numWorkers; i++ {
+    worker := &Worker{
+      TaskChan: taskChan,
+    }
+    workers = append(workers, worker)
+    go worker.Start()
+  }
+
+  return &Pool{
+    TaskChan: taskChan,
+    Workers: workers,
+  }
+}
+
+// 分发任务
+func (p *Pool) Schedule(task *Task) {
+  p.TaskChan <- task
+}
+

使用方式

func main(){
+// 定义任务
+task1 := &Task{ 
+  f: func() {
+    // do something
+  }}
+
+pool := NewPool(10) 
+
+// 分发任务
+pool.Schedule(task1)
+}
+

提供一些个优秀的 worker pool 方案

  • gammazero/workerpool
  • ivpusic/grpool
  • dpaks/goworkers
  • https://github.com/alitto/pond

semaphore

信号量 (英语:semaphore) 又称为信号标,是一个同步对象,用于保持在 0 至指定最大值之间的一个计数值。

在系统中,给予每一个进程一个信号量,代表每个进程目前的状态,未得到控制权的进程会在特定地方被强迫停下来,等待可以继续进行的信号到来

根据信号量的不同可以分为计数信号量和二进制信号量,前者使用一个整数作为信号量,后者使用一个二进制 0 1 作为信号量

信号量拥有两个操作:

  • p 操作会减少信号量的数值
  • v 操作会增加信号量的数值

其中二进制信号量是特殊的信号量,它就是互斥锁的功能

go 语言在 x/sync 中提供了一个 weighted 的包,它就是提供的信号量的功能

  • Acquire p 操作,减少信号量的数值,表示获取了资源 -1
  • Release v 操作,增加信号量的数值,表示释放了资源 +1
  • TryAcquire 尝试获取资源,如果获取成功,则返回 true,否则返回 false +它类似于 trylock 锁,也就是失败直接返回 false,并不会阻塞

注意,信号量为了简洁的设计要求,pv 操作使用 +1 和 -1 这种非常简单的递增递减设计,是有意为之的。这种简单性使得信号量在各种同步情况下都很容易理解和正确使用。更复杂的操作可能会引入难以发现的 bug 或误用场景。

让我们使用信号量来实现一个 worker pool

package main
+
+import (
+	"context"
+	"fmt"
+	"log"
+	"runtime"
+	"time"
+)
+
+var (
+  // 最大的worker数量
+	maxWorkers = runtime.GOMAXPROCS(0)
+	sema       = semaphore.NewWeighted(int64(maxWorkers)) //信号量
+	task       = make([]int, maxWorkers*4)                // 任务数,是worker的四倍
+)
+
+func main() {
+	ctx := context.Background()
+	for i := range task {
+		// 如果没有worker可用,会阻塞在这里,直到某个worker被释放
+		if err := sema.Acquire(ctx, 1); err != nil {
+			break
+		}
+		// 启动worker goroutine
+		go func(i int) {
+			defer sema.Release(1)
+			time.Sleep(100 * time.Millisecond) // 模拟一个耗时操作
+			task[i] = i + 1
+		}(i)
+	}
+	// 请求所有的worker,这样能确保前面的worker都执行完
+	if err := sema.Acquire(ctx, int64(maxWorkers)); err != nil {
+		log.Printf("获取所有的worker失败: %v", err)
+	}
+	fmt.Println(task)
+}
+

使用信号量时的注意事项

  • 请求了资源,忘记了释放
  • 释放了从未请求的资源
  • 长时间持有一个资源但是不使用它
  • 不持有一个资源,但是直接使用了它

使用 channel 去实现一个信号量

使用一个缓存为 n 的 channel 去实现一个信号量

package main
+
+import "sync"
+
+// Semaphore 数据结构,并且还实现了Locker接口
+type semaphore struct {
+	ch chan struct{}
+}
+
+// 创建一个新的信号量
+func NewSemaphore(capacity int) sync.Locker {
+	if capacity <= 0 {
+		capacity = 1 // 容量为1就变成了一个互斥锁
+	}
+	return &semaphore{ch: make(chan struct{}, capacity)}
+}
+
+// 请求一个资源
+func (s *semaphore) Lock() {
+	s.ch <- struct{}{}
+}
+
+// 释放资源
+func (s *semaphore) Unlock() {
+	<-s.ch
+}
+
+

issues

问题一:有互斥锁就一定有临界区吗?

互斥锁的存在不等于必须存在临界区。

构成一个合理的临界区,需要满足:

  • 有真正需要互斥访问的共享资源 (比如共享内存,变量等)
  • 通过加锁,在访问该资源前后形成互斥的代码区域
  • 确保同一时间只有一个线程/goroutine 可以进入该互斥区域

所以互斥锁只是手段之一,用于保证临界区互斥性的需要。

如果没有需要保护的共享资源,或者互斥逻辑不严密,那么使用再多的锁也不等于形成了临界区

问题二:如果 Mutex 已经被一个 goroutine 获取了锁,其它等待中的 goroutine 们只能一直等待。那么,等这个锁释放后,等待中的 goroutine 中哪一个会优先获取 Mutex 呢?

上文中的锁的饥饿模式和正常模式可以解释这个问题。

如果是正常的模式下,就是按照正常队列 FIFO 的顺序去获取锁,除非这个时候有新的 goroutine 生成,那么这个 goroutine 会优先获取锁。

但是如果一个队头的 goroutine 等待时间过长超过了 1ms,那么它就会将互斥锁的模式变成饥饿模式,自动获取锁

问题三:Mutext 的底层中,使用 state 和 sema 来表示锁的状态,sema 是信号量,为什么在有信号量表示锁的状态之后还需要一个 state 表示锁是否上锁呢?

  • state 作为一个 boolean 变量,可以非常简单直观地表示锁的基本状态。
  • sema 是一个整数计数器,可以灵活地表示多种状态,如等待队列长度等。
  • 将两者分开,可以清晰地分离基本锁状态和高级同步语义,符合分而治之的设计思想。
  • sema 可直接 reused 现成的信号量实现代码,state 又足够轻量不需要复杂机制。
  • 将两者组合可以充分发挥各自的优势,实现一个功能完备但设计简单的 mutex。
  • 如果全部只依赖 sema 来表示所有状态,实现可能会更复杂,语义也不够清晰。

问题四:使用循环栅栏,信号量去完成经典并发题:水的制造工厂

//并发趣题:一氧化二氢制造工厂
+//题目是这样的:
+//有一个名叫大自然的搬运工的工厂,生产一种叫做一氧化二氢的神秘液体。这种液体的分子是由一个氧原子和两个氢原子组成的,也就是水。
+//这个工厂有多条生产线,每条生产线负责生产氧原子或者是氢原子,每条生产线由一个 goroutine 负责。
+//这些生产线会通过一个栅栏,只有一个氧原子生产线和两个氢原子生产线都准备好,才能生成出一个水分子,
+//否则所有的生产线都会处于等待状态。也就是说,一个水分子必须由三个不同的生产线提供原子,而且水分子是一个一个按照顺序产生的,
+//每生产一个水分子,就会打印出 HHO、HOH、OHH 三种形式的其中一种。HHH、OOH、OHO、HOO、OOO 都是不允许的。
+//生产线中氢原子的生产线为 2N 条,氧原子的生产线为 N 条。
+
+package main
+
+import (
+	"context"
+	"fmt"
+	"github.com/marusama/cyclicbarrier"
+	"golang.org/x/sync/semaphore"
+	"math/rand"
+	"sort"
+	"sync"
+	"time"
+)
+
+// h2o 水的组成,其中我们需要俩h一个o所以我们给定他们信号量,来对他们的任务进行控制。
+type H2O struct {
+	// 控制的h的信号量
+	seaH *semaphore.Weighted
+	// 控制O的信号量
+	seaO *semaphore.Weighted
+	// 栅栏,这里也就是重复的使用栅栏,也就是 重复栅栏。
+	cyc cyclicbarrier.CyclicBarrier
+}
+
+func NewH2O() *H2O {
+	return &H2O{
+		// h 两个
+		seaH: semaphore.NewWeighted(2),
+		// o 需要一个
+		seaO: semaphore.NewWeighted(1),
+		// 我们要控制的循环栅栏就是3个,因为一共需要三个嘛。
+		cyc: cyclicbarrier.New(3),
+	}
+}
+
+// 处理h
+func (o *H2O) dealH(outH func()) {
+	// 将这个信号量给拿出来1,因为h充盈来2,所以会有俩线程做这个动作
+	o.seaH.Acquire(context.Background(), 1)
+	// 输出 h
+	outH()
+	// wait的意思就是不等到三个线程,我就不走
+	o.cyc.Await(context.Background())
+	// 走动完毕后再把资源塞进去。
+	o.seaH.Release(1)
+}
+
+// 处理 o
+func (o *H2O) dealO(outO func()) {
+	// 氧气将信号量中的信号取出来,
+	o.seaO.Acquire(context.Background(), 1)
+	// 输出o
+	outO()
+	// 等待三个线程跟上一个函数一样意思,也不用担心用两次不行,随便用。这个函数调用几次都OK。
+	o.cyc.Await(context.Background())
+	// 释放掉。
+	o.seaO.Release(1)
+}
+func main() {
+	// channel 传递信息。
+	var ch chan string
+	var outO = func() {
+		ch <- "O"
+	}
+	var outH = func() {
+		ch <- "H"
+	}
+	// 一共有 300个channel需要。
+	ch = make(chan string, 300)
+	// wg是为了控制这300个线程,栅栏是为了控制生成水的这个控制器,两者的作用不同哦。
+	wg := new(sync.WaitGroup)
+	wg.Add(300)
+	h := NewH2O()
+	for i := 0; i < 100; i++ {
+		go func() {
+			defer wg.Done()
+			time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
+			h.dealO(outO)
+		}()
+	}
+	for i := 0; i < 200; i++ {
+		go func() {
+			defer wg.Done()
+			time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
+			h.dealH(outH)
+		}()
+	}
+	wg.Wait()
+	if len(ch) != 300 {
+		fmt.Println(len(ch))
+		panic("❌")
+	}
+	s := make([]string, 3)
+	for i := 0; i < 100; i++ {
+		s[0] = <-ch
+		s[1] = <-ch
+		s[2] = <-ch
+		// 这里,对于hho进行排序了,不然也不一定是hho这个顺序
+		sort.Strings(s)
+		result := s[0] + s[1] + s[2]
+		fmt.Println(s)
+		if result != "HHO" {
+			fmt.Println("错误 ❌ :", result)
+		}
+	}
+}
+

参考资料

  • https://mp.weixin.qq.com/s/iPpWd8vjyaN2sJFwxzN9Bg
  • https://draveness.me/golang/docs/part3-runtime/ch06-concurrency/golang-sync-primitives/
  • https://time.geekbang.org/column/intro/100061801
  • https://colobu.com/2018/12/18/dive-into-sync-mutex/
  • 《go 语言精进之路》
+ + + diff --git "a/\345\271\266\345\217\221/\345\271\266\345\217\221\344\274\230\345\214\226/index.html" "b/\345\271\266\345\217\221/\345\271\266\345\217\221\344\274\230\345\214\226/index.html" new file mode 100644 index 000000000..b139d40f2 --- /dev/null +++ "b/\345\271\266\345\217\221/\345\271\266\345\217\221\344\274\230\345\214\226/index.html" @@ -0,0 +1,263 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

go 语言实战项目并发优化

上文我们已经讲解了 goroutine,channel,并发原语,atomic,context,以及 go 的内存模型,内容还是比较多的,我们需要通过实战的项目的优化演进过程来更好的理解并发的最佳实践

能不并发就不并发

并发是一个双刃剑,一方面它可以加速程序,另一方面它也增加了程序的复杂度,所以需要在并发和不并发之间做取舍,如果你发现你的程序在不使用并发的时候也能满足你的需求,答应我,不要使用并发,累活脏活自己干,不要委派给另一个 goroutine 去做所谓的并发工作。

// bad
+func main() {
+  http.HandleFunc("/", func(w http.ResponseWriter,r *http.Request,)) {
+    fmt.Fprintln(w,"hello wrold!")
+  }
+  go func(){
+    if err := http.ListenAndServe(":8080",nil);err != nil {
+      log.Fatal(err)
+    }
+  }()
+
+  select{}
+}
+

在这段代码中,委派一个 goroutine 去启动一个监听服务,又使用 select {} 去阻塞主 goroutine 的运行

确实,这可以满足需求

但是,委派一个后台 goroutine 去执行监听服务没有带来任何有利的收益,反而增加了代码的复杂度,所以我们应该取消委派,主 goroutine 去执行监听即可

// better
+
+func main(){
+  http.HandleFunc("/", func(w http.ResponseWriter,r *http.Request,)) {
+    fmt.Fprintln(w,"hello wrold!")
+  }
+  
+  if err := http.ListenAndServe(":8080",nil);err != nil {
+    log.Fatal(err)
+  }
+}
+

优先使用 channel + context 的方法去优雅关闭

核心就是把众多启动的 goroutine 改成 worker pool + context 的模型

在 channel 中我们讲过 worker pool 的实现,为什么一定要优先使用 worker pool?因为很多时候你启动了很多的 goroutine,不知不觉就会造成混乱,以及 goroutine 的泄露问题,我们使用 worker pool 的方式,可以很好的控制 goroutine 的并发数量,以及优雅的关闭 goroutine

看一个优秀的例子:


+type Tracker struct {
+	ch   chan string // 作为工作池子让worker消费
+	stop chan struct{}
+}
+
+func NewTracker() *Tracker {
+	return &Tracker{
+		ch:   make(chan string, 10),
+		stop: make(chan struct{}, 2),
+	}
+}
+func (t *Tracker) Event(ctx context.Context, data string) error {
+	select {
+	case t.ch <- data:
+		return nil
+	case <-ctx.Done():
+		return ctx.Err()
+	}
+}
+func (t *Tracker) Run() {
+	for data := range t.ch {
+		// 模拟消费
+		time.Sleep(time.Second * 5)
+		fmt.Println(data)
+	}
+	// run 数据结束之后就可以发送信号了
+	t.stop <- struct{}{}
+}
+
+// shutDown 通过关闭ch 让 run中的range结束,进而再发送stop信号,让shutdown函数退出
+func (t *Tracker) Shutdown(ctx context.Context) {
+	close(t.ch)
+	select {
+	case <-t.stop:
+	case <-ctx.Done():  
+	}
+}
+
func main() {
+	tr := NewTracker()
+	// 开启两个消费者
+	go tr.Run()
+	go tr.Run()
+	
+  tr.Event(context.Background(), "test")
+	tr.Event(context.Background(), "test2")
+  tr.Event(context.Background(), "test3")
+	tr.Event(context.Background(), "test4")
+  tr.Event(context.Background(), "tes5")
+	tr.Event(context.Background(), "test6")
+
+	time.Sleep(10 * time.Second)
+	ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second))
+	defer cancel()
+	tr.Shutdown(ctx)
+}
+

使用方去决定是否并发

func ListDirectory(dir string) chan string
+

这是一个返回目录下所有文件路径的函数,它返回的是一个 channel,很明显,这跟题目所说的让使用方去决定是否并发是相违背的

因为在这个函数体内部,已经完成了一个 goroutine 的创建,但是作为使用方,我们并不能说一定要使用并发,而且你也无法控制发送的停止,假设你在这个函数内部使用一个 close 作为关闭的信号,也是有问题的,比如我想恢复读取,那么你已经 close 了,该如何继续开启呢?

还有一个问题,这个函数返回的是一个 channel,但是并没有返回 error,我们如果去判断 channel 的状态,并不能分辨是读取完毕 channel 被 close 还是出现 error,然后 channel 被关闭了,这就是二象性的问题,你不能设置二象性这种状态的函数,就跟刚才说的那样,你无法获悉真实的最精准的状态。

也就是说你的发送过程不够透明,调用方无法决定暂停,恢复读取这些行为

再提一个需求,我只需要读取的数据中的某些符合规定的路径,那么你如果作为一个黑箱的函数,完全无法做到定制化对不对

那么为什么不能设计成一个普通函数,然后在调用方再决定是否并发这个行为模式呢?

我们看一下 go 语言标准库中的用法

filepath.Walk(root string,fn filepath.WalkFunc) error
+

很明显这是一个实现了功能的普通函数,那么让我们分别看看它的普通模式和并发模式

// 普通模式
+
+err := filepath.Walk(".", func(path string, info fs.FileInfo, err error) error {
+		if err != nil {
+			fmt.Printf("prevent panic by handling failure accessing a path %q: %v\n", path, err)
+			return err
+		}
+		if info.IsDir() && info.Name() == subDirToSkip {
+			fmt.Printf("skipping a dir without errors: %+v \n", info.Name()) // 可以看到我们返回的err是不同的err
+			return filepath.SkipDir
+		}
+    // 定制需求
+    if path != "xxx"{
+      fmt.Printf("visited file or dir: %q\n", path)
+    }
+		return nil
+	})
+
+	if err != nil {
+		fmt.Printf("error walking the path %q: %v\n", tmpDir, err)
+		return
+	}
+

那么再让我们看一下并发模式

// 并发模式
+go func() {
+		defer close(value)
+		err <- filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
+			if err != nil {
+				return err
+			}
+			// if the file is noe regular, it mean the file is done,you should return
+			if !info.Mode().IsRegular() {
+				return nil
+			}
+			value <- path
+			return nil
+		})
+	}()
+

解释一下这个 Walk 的函数本质,它传入一个路径和一个函数,这个函数拥有的参数是一个要输出的路径,一个文件信息和一个错误类型,返回一个错误类型,参数函数被执行的时候 walk 底层会将真实 path 值作为实际参数传入这个函数里,所以我们可以在 path 中,指定一个 channel 去作为流的方式导出数据。

// walk 的底层源码
+for _, name := range names {
+		filename := Join(path, name)
+		fileInfo, err := lstat(filename)
+		if err != nil {
+
+      // 这里将读取到的path值传入了 walkFn 函数里,来完成实际参数的赋值行为
+			if err := walkFn(filename, fileInfo, err); err != nil && err != SkipDir {
+				return err
+			}
+		} else {
+			err = walk(filename, fileInfo, walkFn)
+			if err != nil {
+				if !fileInfo.IsDir() || err != SkipDir {
+					return err
+				}
+			}
+		}
+}
+

可以看到这个函数的实现完成了基本的功能,既可以并发,又可以不并发

必须让发送方决定 channel 的关闭

如果不能让发送方决定 channel 的关闭,而是不控制或者是接收方去控制,那么数据的丢失就不可避免了

package main
+
+import (
+    "fmt"
+    "sync"
+)
+
+func main() {
+    ch := make(chan int)
+    wg := sync.WaitGroup{}
+
+    // 发送者
+    wg.Add(1) 
+    go func() {
+        defer wg.Done()
+        for i := 0; i < 5; i++ {
+            ch <- i
+        }
+        close(ch) // 发送方关闭channel
+    }()
+
+    // 接收者
+    wg.Add(1)
+    go func() {
+        defer wg.Done() 
+        for {
+            if data, ok := <-ch; ok {
+                fmt.Println(data)
+            } else {
+                break // channel已关闭
+            }
+        }
+    }()
+    
+    wg.Wait() 
+}
+

那么让我看看一下几个反例

// 反例1:接收方关闭channel
+func main() {
+  ch := make(chan int)
+
+  go func() {
+    for i := 0; i < 5; i++ {
+      ch <- i 
+    }
+  }()
+
+  for {
+    fmt.Println(<-ch)
+      // 当你接收方去关闭channel的时候
+      // 发送方如果不知道的话,向一个close的channel发送数据会panic的
+      if xx {
+         close(ch) // 接收方关闭channel
+         break
+      }
+  }
+}
+
// 反例2:没有关闭channel
+func main() {
+  ch := make(chan int)
+  
+  go func() {
+    for i := 0; i < 5; i++ {
+      ch <- i
+    }
+  }()
+
+  for {
+    // 这里 ok 永远都不会false,所以 这里会死循环
+    if data, ok := <-ch; ok {
+      fmt.Println(data)
+    } else {
+      break
+    }
+  } 
+}
+
+ + + diff --git "a/\345\271\266\345\217\221/\345\271\266\345\217\221\346\250\241\345\236\213/index.html" "b/\345\271\266\345\217\221/\345\271\266\345\217\221\346\250\241\345\236\213/index.html" new file mode 100644 index 000000000..164d2d37c --- /dev/null +++ "b/\345\271\266\345\217\221/\345\271\266\345\217\221\346\250\241\345\236\213/index.html" @@ -0,0 +1,165 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

go 并发模型

并发和并行的关系

并发是问题域的一种概念,它强调处理多个同时 (或者近似同时) 发生的事件。

并行是方法域的一种概念,将问题分解为多个部分,同时并行执行来加速解决问题。

并发可以不是同时进行的,但是并行强调的就是必须同时进行。

rob pike:并发不是并行,并发关乎结构,并行关乎执行

并发但不并行:一位老师,在听一个学生朗读的时候,她可以暂停学生的朗读,然后回答学生的问题,再次开始学生的朗读,虽然她一次只能干一件事 (所以不满足并行),但可以处理近乎同时发生的***多个事件***

并行但不是并发:让全班同学制作贺卡,全班同学每个学生制作五枚,全班同学同时开始做 (满足并行),但不是并发,因为只有一个事件 (不满足并发提出的同时处理多个事件)

并发和并行:两位老师,一个老师提问,一个老师解决学生的问题,这就是满足了并行和并发:同时 (这里是近似同时) 处理多个事件:提问和解决问题 (满足了并发),两个老师同时开始这满足了物理层面的同时进行 (满足了并行)

可以看出来,并发强调多个事件,并行强调物理层面的同时执行

获得真正的并行,必须在具有多个物理处理器的计算机上运行程序,针对单个处理器的计算机,只能实现并发执行

go 在执行并发任务时,如果所在物理机器为多核,那么并行的数量就等于 runtime.GOMAXPROCS 的值,如果机器为单核,那么只能执行并发操作了

在使用中,***并发且并行***是最常见的行为,首先,我们通常不会只执行一个事件,并且所用机器不太可能是单核

多线程并发模型

使用共享内存的方式去完成并发就是多线程并发模型,它的核心就是使用锁的方法,让某个线程单独拥有某块内存,其他线程只能访问该内存,从而实现了并发。

go 语言中的锁就是 sync.Mutex,这也是 go 语言实现多线程并发的核心,一共有:

  • sync.Mutex 互斥锁,可以同时对一个资源进行读写操作,但是只能有一个线程可以对该资源进行写操作。
  • sync.RWMutex 读写锁,可以同时对一个资源进行读操作,但是只能有一个写操作
  • sync.Cond 条件变量,可以让一个线程等待另一个线程满足某个条件
  • sync.Once 单例模式,保证某个资源只被初始化一次
  • sync.Pool 资源池,可以让一个资源在内存中被复用,避免了重复创建资源的开销
  • sync.Map 线程安全的 map,可以让多个线程安全的对 map 进行读写操作
  • sync.Pool 资源池,可以让一个资源在内存中被复用,避免了重复创建资源的开销
  • sync.WaitGroup 等待组,可以让多个线程等待,直到某个线程完成某个任务
  • golang.org/x/sync/errgroup 为处理公共任务的子任务的 goroutine 组提供同步、错误传播和上下文取消
  • golang.org/x/sync/semaphore 提供了一个加权信号量实现。
  • golang.org/x/sync/singleflight 提供了重复函数调用抑制机制,中文叫栅栏机制
  • golang.org/x/sync/syncmap 提供了一个并发映射实现

csp

go 语言推荐的并发模型使用的就是 csp 模型,csp 的核心思想就是讲各个任务等同于进程,进程顺序执行互不牵连,进程可以收发信息,使用通道的方式进行信息的通信。

所以如果使用 channel 的方式进行通信就是使用的 csp 并发模型

CSP 模型中的进程通信原语包括:

  • 发送消息:一个进程可以通过发送消息到另一个进程来与之进行通信。
  • 接收消息:一个进程可以通过接收消息来获取另一个进程发送的消息。
  • 原子发送-接收:一个进程可以通过原子发送-接收操作来发送消息并等待接收消息,这相当于发送和接收两个操作的组合。

这些进程通信的都是通过内置的 channel 对象去实现的。

了解 goroutine 的基本信息

这里我们现简单的了解一些基本的使用 goroutne 的方法,后面的 channel 篇和并发原语 context atomic 定时器会进行更加详细的介绍。

我们知道 go 使用了用户线程也就是 goroutine 去替代了传统的线程,所以在 go 语言中我们能操作的线程就是 goroutine,我们无法去触及真实的线程,线程和 goroutine 之间的关系是 go 语言运行时的调度器去调度的。

goroutine 是一种轻量的可以被大量创建的用户态线程。

创建 goroutine

使用 go 关键字加上函数去创建一个 goroutine,当然后面跟方法也可以。

func age() {
+	// 注意后面跟的是一个函数的运行,这跟 defer 一致
+	c := make(chan int, 1)
+	go func() {
+		time.Sleep(10000)
+		c <- 12
+	}()
+	d := <-c
+	fmt.Println(d)
+}
+

我们可以看到,这里使用了 csp 的并发模型,下面我们看一下使用传统的共享内存的并发模式

func age() {
+	// 注意后面跟的是一个函数的运行,这跟 defer 一致
+	var mu sync.Mutex
+	for i := 0; i < 10; i++ {
+		go func(i int) {
+			mu.Lock()
+			defer mu.Unlock()
+			fmt.Println(i)
+		}(i)
+	}
+	time.Sleep(200)
+}
+

异步函数决定权交给函数调用方

我们看一个场景:

我们要读取一个目录下的路径,首先我们可以这么写函数:

func ListDirectory(dir string)([]string,error)
+

这是一个同步函数,我们传入的是目录的地址,返回的是值和错误,只不过我们需要阻塞的等待所有的路径全部扫描完成才能返回

如果我们不想让业务阻塞到这里,可以改造成异步函数:

func ListDirectoryAsync(dir string)chan string{
+	go func(){
+		//
+	}()
+	return c
+}
+

将数据传递给 channel,只需要不断的去读取 channel 就可以变成非阻塞的业务。

不过这里我们发现还是会有一些问题,比如,如果我读取到了想要的数据想结束这个函数,该如何操作呢?读取过程中我如何分辨是读取完成了 close 掉了这个 channel 还是出现了错误 close 这个 channel 呢,所以这个函数还是需要改造

我们可以将这个函数设置成一个同步函数,让调用者来决定是否异步的启动新 goroutine 去调用这个函数,这给了程序更大的灵活性

***将异步执行函数的异步权交给调用方***是更好的设计思想,

因为如果这个函数内部启动了一个 goroutine,但是它并没有提供给你详细的退出机制,那么非常容易出现 goroutine 的泄漏问题

func ListDirectory(dir string, fn func(path string, info os.FileInfo, err error)) {
+	info, err := os.Lstat(dir)
+	if err != nil {
+		err = fn(root, nil, err)
+	} else {
+		err = walk(root, info, fn)
+	}
+	if err == SkipDir || err == SkipAll {
+		return nil
+	}
+	return err
+}
+
+// 同步调用
+func retrieveData(root string) (value []string, err error) {
+	// 使用一个切片来存储结果
+	var result []string
+
+	// 调用ListDirectory,这里不再使用goroutine
+	err = ListDirectory(root, func(path string, info os.FileInfo, err error) error {
+		if err != nil {
+			return err
+		}
+		// 如果文件不是普通文件,直接返回nil
+		if !info.Mode().IsRegular() {
+			return nil
+		}
+		// 将路径添加到结果切片中
+		result = append(result, path)
+		return nil
+	})
+
+	// 如果没有错误,返回结果切片
+	if err == nil {
+		return result, nil
+	}
+	// 如果有错误,返回错误
+	return nil, err
+}
+
+// 异步调用调用:
+
+func retrieveData(root string) (value chan string, err chan error) {
+	err = make(chan error, 1)
+	value = make(chan string)
+	go func() {
+		defer close(value)
+		// 调用时再决定是同步还是异步
+		err <- ListDirectory(root, func(path string, info os.FileInfo, err error) error {
+			if err != nil {
+				return err
+			}
+			// if the file is noe regular, it mean the file is done,you should return
+			if !info.Mode().IsRegular() {
+				return nil
+			}
+			value <- path
+			return nil
+		})
+	}()
+	return
+}
+
+

可以看上文,同步操作也可以,我们也可以使用 go func 的方式异步执行它,因为要传入一个函数,所以如果我们使用了异步,就在函数中使用 channel 来传递结果,如果我们是同步,那么就不再使用 channel,使用一个切片即可

goroutine 退出

goroutine 使用代价很低,因为它并不是操作系统的线程,创建成本非常低,go 推荐可以多多使用 goroutine

goroutine 退出有两种方式:

  • 主动退出:使用 return 或者 panic 关键字退出
  • 非主动退出:使用 sync.WaitGroup,context 等方法,当所有的 goroutine 都退出后,等待组会自动退出
func main() {
+	var wg sync.WaitGroup
+	wg.Add(1)
+	go func() {
+    defer wg.Done()
+	}()
+    wg.Wait()
+}
+

go goroutine 执行完毕就会直接退出,

程序的退出跟主 goroutine 有关,只要主 goroutine 不退出程序就会正常执行下去,反之,主 goroutine 如果退出了,其它的 goroutine 即便没有执行完毕,整个程序还是会结束。

func main() {
+	go age()
+}
+func age() {
+	time.Sleep(10000)
+	fmt.Println("hi there")
+}
+

这个程序将无法保证能输出 hi there

要想让 age 输出正常的值,必须保证主 goroutine 不能退出,比如使用 sync.WaitGroup,比如直接让主 goroutine 休眠

func main() {
+	go age()
+  time.Sleep(20000)
+}
+func age() {
+	time.Sleep(10000)
+	fmt.Println("hi there")
+}
+

使用 goroutine 时的注意事项

当我们使用 goroutine 时,下面几点是注意事项:

  1. 确定一个 goroutine 什么时候会结束
  2. 是否有手段能停止 goroutine

只有搞清楚这两个问题,使用 goroutine 的时候才不会有泄露的风险,所以这两点是使用 gorotine 的安全哲学

  • 确定一个 goroutine 什么时候会结束
  • 是否有手段能停止 goroutine

下面让我们正式学习 go 并发语言,channel 等相关知识!

参考资料

  • https://mp.weixin.qq.com/s/TvHY2i1FX1zS_WHdCvK-wA
  • https://book.douban.com/subject/26337939/
  • https://book.douban.com/subject/35720728/ 315 页 - 317 页
  • 极客时间《go 进阶训练营》
+ + + diff --git "a/\347\274\226\350\257\221\345\231\250/gc/index.html" "b/\347\274\226\350\257\221\345\231\250/gc/index.html" new file mode 100644 index 000000000..d001751fd --- /dev/null +++ "b/\347\274\226\350\257\221\345\231\250/gc/index.html" @@ -0,0 +1,201 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

Go 语言官方编译器 gc

前端组件

1。Lexer (词法分析器)

Lexer 是编译器前端的第一个重要组件。它的主要职责是对源代码进行词法分析,将源代码分割成一个个词法单元 (tokens)。这些词法单元包括关键字、标识符、运算符、字面量等基本单元。

在 Go 语言中,Lexer 的实现在 src/go/scanner 包中。它使用了一种高效的 DFA (确定有限状态自动机) 算法进行词法分析。同时,它还支持 Unicode 编码,可以对包含多语种字符的源代码进行正确扫描。

2。Parser (语法分析器)

Parser 接收 Lexer 输出的词法单元,根据语言的语法规则构造抽象语法树 (AST)。AST 是源代码的树状表示形式,反映了代码的层次结构和语义信息。

Go 的语法分析器实现在 src/go/parser 包中。它采用 LL(1) 自顶向下分析算法,支持对 Go 语言所有语法结构的解析。Parser 会进行语法检查,如果发现语法错误,会报告相应的错误信息。

3。Type Checker (类型检查器)

Type Checker 对 AST 进一步进行语义分析,尤其是类型信息的收集和一致性检查。它会为所有节点附加类型信息,检查变量使用是否正确,函数调用参数是否匹配等。如果发现类型错误,会输出相应的错误信息。

Type Checker 的实现位于 src/go/types 包中。除了类型检查,它还可以解析导入路径、收集方法信息等。

中端组件

1。SSA (静态单赋值形式)

SSA 是一种将程序的控制流展开的中间表示形式。Go 编译器在 Type Checker 之后,会将 AST 转换为 SSA 形式,以方便后续的优化分析和转换。

SSA 的实现位于 src/cmd/compile/internal/ssa 包。在 SSA 中,每个变量只会被赋值一次,所有复杂的控制流都被展开成基本块的形式。这种表示形式方便了大量的编译器优化,如值传播、常量折叠等。

2。Optimizer (优化器)

Go 编译器内置了多种优化器模块,用于对 SSA 形式的程序进行各种形式的优化转换。主要优化器包括:

  • 逃逸分析器 (src/cmd/compile/internal/escape)
  • 内联器 (src/cmd/compile/internal/inliner)
  • 死码消除 (src/cmd/compile/internal/deadcode)
  • 常量传播和折叠 (src/cmd/compile/internal/opt)
  • 控制流优化 (src/cmd/compile/internal/opt)

其中,逃逸分析是 Go 编译器最重要的优化环节。它可以分析变量是否会逃逸到堆上,进而决定是否可以优化为堆分配或栈分配。这对于 Go 这样的带有垃圾回收的语言而言至关重要,能极大减少内存分配和垃圾回收的压力。

后端组件

1。Code Generator (代码生成器)

代码生成器会将优化后的 SSA 表示形式转换为对应的机器码指令序列。

Go 编译器采用了自定义的高效代码生成器,其实现位于 src/cmd/compile/internal/gc 中。它不仅支持多种常用硬件架构 (x86、ARM、RISC-V 等),还支持硬件特性加速。例如在 ARM64 架构上支持了利用 SVE 向量指令集进行矢量化优化。

2。Assembler (汇编器)

汇编器将前端生成的机器码经过进一步处理,生成目标平台的二进制代码。Go 编译器目前使用 GNU 汇编器进行汇编。

3。Linker (链接器)

链接器将全部目标文件 (object files) 以及需要的系统库文件链接合并,生成最终的可执行目标文件。可执行文件是完全独立无需外部依赖的自包含程序文件。

Go 语言自带小型高效的链接器,实现位于 src/cmd/link 包中。它支持静态链接和动态链接两种方式,默认采用静态链接生成全自包含的可执行文件。

4。Runtime

Runtime 库提供了垃圾回收、goroutine 调度、处理系统调用等运行时支持。Go 语言运行时高度优化,垃圾回收器采用了三色标记-压缩算法,并行和并发的处理提高了效率。

Goroutine 调度器使用了 M:N 调度模型,将 goroutine 和系统线程进行高效多路复用。这使得 Go 语言可以轻松创建大量并发任务,并拥有优秀的并发性能。

其他特性

除了高效完整的编译器支持,Go 编译器还具备以下特性:

  1. 多核并行编译:gc 编译器利用并发支持,使用可用的所有 CPU 核心并行编译源文件,提高整体编译速度。

  2. 增量编译:只编译被修改的源文件和依赖它们的源文件,避免对无关代码进行重新编译。

  3. 类型检查缓存:将类型检查结果缓存,以加速后续编译过程。

  4. Go 编写:gc 编译器本身就是使用 Go 编写的,这使得它可以自举并确保与 Go 语言保持高度契合。

Go 编译器与 Go 语言及其工具链源码一并开源发布,方便社区贡献者阅读理解和改进编译器实现。

总结

gc 是 Go 语言默认的官方编译器,它提供了完整的前端、优化器和后端支持,能够将 Go 语言源代码编译为高效的机器码。通过采用先进的编译器技术和算法,如 SSA 表示、逃逸分析等,gc 编译器可以生成高度优化的执行程序。

与此同时,gc 编译器也提供了强大的运行时支持,包括垃圾回收、goroutine 调度等核心功能。这使得 gc 不仅仅是一个简单的编译器,更是 Go 语言全栈的编译和执行解决方案。

Go 开发团队一直在持续改进完善 gc,以支持更多最新的语言特性和平台,满足日益增长的性能和兼容性要求。gc 编译器的持续进化有力地支撑了 Go 语言工程级应用的快速发展。

真实案例的分析

好的,我们来补充一些实际的例子,帮助理解编译器的工作原理。

编译器前端示例:

Lexer 示例

假设有以下 Go 源代码:

package main
+
+import "fmt"
+
+func main() {
+    fmt.Println("Hello, World!")
+}
+

Lexer 会将该源代码分割为以下词法单元:

package
+main
+import
+"fmt"
+func
+main
+(
+)
+{
+fmt
+.
+Println
+(
+"Hello, World!"
+)
+}
+

Parser 示例

经过词法分析后,Parser 会将这些词法单元构造成抽象语法树 (AST):

// FileNode
+File{
+    Package: main
+    Imports: []*ImportSpec{
+        &ImportSpec{
+            Path: &BasicLit{
+                Value: "fmt"
+            }
+        }
+    }
+    Decls: []Decl{
+        &FuncDecl{
+            Name: &Ident{Name: "main"}
+            Body: &BlockStmt{
+                List: []Stmt{
+                    &ExprStmt{
+                        X: &CallExpr{
+                            Fun: &SelectorExpr{
+                                X: &Ident{Name: "fmt"}
+                                Sel: &Ident{Name: "Println"}
+                            }
+                            Args: []Expr{
+                                &BasicLit{
+                                    Value: "Hello, World!"
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+

Type Checker 示例

Type Checker 会对 AST 进行类型分析,并附加类型信息:

// File Node with Type Information
+File{
+    Package: main
+    Imports: []*ImportSpec{
+        &ImportSpec{
+            Path: &BasicLit{
+                Value: "fmt"
+            }
+        }
+    }
+    Decls: []Decl{
+        &FuncDecl{
+            Name: &Ident{Name: "main", Type: func()}
+            Body: &BlockStmt{
+                List: []Stmt{
+                    &ExprStmt{
+                        X: &CallExpr{
+                            Fun: &SelectorExpr{
+                                X: &Ident{Name: "fmt", Type: *fmt}
+                                Sel: &Ident{Name: "Println", Type: func(a ...interface{}) (n int, err error)}
+                            }
+                            Args: []Expr{
+                                &BasicLit{
+                                    Value: "Hello, World!",
+                                    Type: string
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+

编译器中端示例:

SSA 示例

在 SSA 中,代码被转换为基本块的形式,控制流完全展开:

# Example SSA representation
+func main():
+bb0: 
+    t0 = new [1]string { /* str */ }
+    t1 = &t0[0]
+    *t1 = "Hello, World!"
+    t2 = staticbytes(&<string  Value>)
+    t3 = &t2[0]
+    t4 = slice t2[:]
+    t5 = make([]interface{}, 1)
+    t6 = &t5[0]
+    t7 = &t4
+    t8 = *t7
+    *t6 = t8
+    t9 = fmt.Println(&t5...)
+    return
+
+# Optimized SSA representation
+func main():
+bb0:
+    t0 = staticbytes(&<string  Value="Hello, World!">)
+    t1 = &t0[0]  
+    t2 = slice t0[:]
+    t3 = make([]interface{}, 1)
+    t4 = &t3[0]
+    t5 = &t2
+    t6 = *t5
+    *t4 = t6
+    t7 = fmt.Println(&t3...)
+    return
+

可以看到,在优化后的 SSA 表示中,一些冗余的内存分配和字符串构造操作都被优化掉了。

编译器后端示例:

Code Generator 示例

amd64 平台上,优化后的 fmt.Println("Hello, World!") 会被生成如下汇编代码:

0x001b  TEXT    "".main(SB), ABIInternal, $24-0
+        MOVQ    $go.string."Hello, World!"(SB), AX      // 加载字符串常量
+        MOVQ    AX, (SP)                                // 将字符串常量传入参数区
+        MOVQ    $1, 8(SP)                               // 设置 slice 长度
+        MOVQ    $1, 16(SP)                              // 设置 slice 容量
+        CALL    runtime.convTstring(SB)                 // 调用 convTstring
+        MOVQ    8(SP), AX                               // 加载转换后的 slice
+        MOVQ    AX, (SP)                                // 将 slice 作为参数
+        CALL    fmt.Println(SB)                         // 调用 fmt.Println
+        MOVQ    24(SP), BP                              // 恢复 BP
+        ADDQ    $24, SP                                 // 调整栈指针
+        RET                                             // 返回
+

可以看到,Go 编译器后端生成了紧凑高效的汇编指令,包括字符串常量加载、参数传递、函数调用等操作。

Linker 示例

假设我们编译了以下两个 Go 源文件:

// file1.go
+package main
+
+import "fmt"
+
+func main() {
+    fmt.Println("Hello")
+    sayHi()
+}
+
+// file2.go  
+package main
+
+func sayHi() {
+    fmt.Println("Hi")
+}
+

Go 编译器会先将它们分别编译为目标文件 file1.ofile2.o。链接器 (cmd/link) 会将这两个目标文件以及需要的运行时库链接合并,生成最终的可执行文件 main

该过程类似于:

$ go tool compile -o file1.o file1.go
+$ go tool compile -o file2.o file2.go  
+$ go tool link -o main file1.o file2.o
+

最终输出的 main 可执行文件中,包含了 main 包定义、fmt.Println 导入符号以及 sayHi 函数实现等所有需要的代码和元数据。

通过这些实例,我们可以更好地理解 Go 编译器各组件的具体工作方式,以及它们是如何高效协作将 Go 源代码转换为机器码的。这种透明度和可阅读性也是 Go 编译器的一大优势。

参考资料

  • https://go.dev/src/cmd/compile/README
  • https://golang.org/doc/ssa
  • https://go101.org/article/compiler.html
  • https://dmitri.shuralyov.com/idiomatic-go#compiler
+ + + diff --git "a/\347\274\226\350\257\221\345\231\250/gccgo/index.html" "b/\347\274\226\350\257\221\345\231\250/gccgo/index.html" new file mode 100644 index 000000000..96f93c4a5 --- /dev/null +++ "b/\347\274\226\350\257\221\345\231\250/gccgo/index.html" @@ -0,0 +1,53 @@ + + + + + + gccgo | GOFamily - go 程序员宝典 + + + + + + + + +

gccgo

gccgo 是 Gо 语言到 C 语言的编译器,它将 Gо 语言源代码编译成 C 语言源代码。

gccgo 本身只是一个编译器前端,负责解析 Gо 语言代码并生成 C 语言代码。要生成最终的可执行文件,还需要一个编译器后端。

gccgo 常见的后端有:

gcc - GNU 编译器,可以将 gccgo 生成的 C 语言代码进一步编译成机器代码,生成最终的可执行文件。 +clang - LLVM 编译器,同样可以将 C 语言编译成机器代码。 +tcc - Tiny C Compiler,一个小型快速的 C 语言编译器。 +所以简单来说,gccgo 编译器的常见后端是 gcc 和 clang。完整的编译流程是:

gccgo 前端 → C 语言代码 → gcc/clang 后端 → 机器代码 (可执行文件)

开发者可以根据需要选择不同的编译器后端,来编译 gccgo 生成的 C 语言代码。

+ + + diff --git "a/\347\274\226\350\257\221\345\231\250/index.html" "b/\347\274\226\350\257\221\345\231\250/index.html" new file mode 100644 index 000000000..f173846bb --- /dev/null +++ "b/\347\274\226\350\257\221\345\231\250/index.html" @@ -0,0 +1,65 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

go 编译器

编译器概述

默认编译器 gc 介绍 +编译原理和过程 +编译器组件工具链 +编译优化技术

编译器特性

编译速度快 +生成自包含可执行文件 +内存安全保证 +跨平台支持 +模块化支持

编译器实战

常见编译错误和解决方法 +编译参数和定制 +增量编译 +hook 编译过程 +交叉编译

编译器源码

gc 编译器源码解析 +编译器前端实现 +编译器后端实现

其他编译器

gccgo +gollvm +编译器比较

+ + + diff --git "a/\347\274\226\350\257\221\345\231\250/llvm/index.html" "b/\347\274\226\350\257\221\345\231\250/llvm/index.html" new file mode 100644 index 000000000..548671edc --- /dev/null +++ "b/\347\274\226\350\257\221\345\231\250/llvm/index.html" @@ -0,0 +1,54 @@ + + + + + + GOFamily - go 程序员宝典 + + + + + + + + +

llvm

LLVM 可以通过以下两种主要方式为 Go 语言代码提供编译支持:

gollvm

gollvm 项目实现了将 Go 代码编译为 LLVM IR 的 frontend。它可以直接生成 LLVM IR,然后通过 LLVM 进行后端代码生成。

主要步骤是:

  • gollvm 解析 Go 代码,生成对应的 LLVM IR
  • 执行 LLVM 优化流水线进行优化
  • LLVM 后端生成目标平台的机器码
  • 嵌入 LLVM pass

可以通过修改 Go Compiler 工具链,在编译过程中调用 LLVM Pass 执行优化。

主要步骤是:

  • Go Compiler 前端生成 initial IR
  • 将 IR 传递给 LLVM Pass 执行优化
  • LLVM Pass 输出优化后的 IR
  • Go Compiler 后端根据 IR 生成机器码

这种方式需要 invasive 修改 Go 编译器,较为复杂。

总结:

gollvm 通过完全 External 的方式引入 LLVM,而嵌入 LLVM Pass 需要修改 Go Compiler。

gollvm 方式集成更简单,但是需要保证 IR 的转换正确性;嵌入 Pass 可以利用 Go Compiler 的 Context 信息进行更准确的优化。

根据需求和成本进行抉择。

llvm -golang

llvm-golang 是另一个用于 Go 语言编译的 LLVM 集成项目。

它与 gollvm 的主要区别有:

实现方式不同

  • gollvm 是独立的 Go frontend,将 Go 代码编译成 LLVM IR
  • llvm-golang 直接使用 LLVM 对 Go 代码进行编译 +编译入口不同
  • gollvm 通过调用 gollvm 命令,传入 Go 源文件
  • llvm-golang 编译时调用 clang 命令,并使用-femulate-llvm-golang 参数 +编译过程不同
  • gollvm 编译生成完整的 LLVM IR 然后优化
  • llvm-golang 是逐函数生成 LLVM IR 并编译 +项目状态不同
  • gollvm 活跃维护,可正常使用
  • llvm-golang 最后更新在5年前,未维护 +总之,llvm-golang 是直接使用 LLVM 编译 Go 的早期尝试,但维护情况不佳。gollvm 作为独立 frontend 集成 LLVM,是更可靠的解决方案。

但 llvm-golang 的直接编译方式也具有借鉴意义,合理结合两种方式可以获得更好的 LLVM 集成效果。

+ + + diff --git "a/\347\274\226\350\257\221\345\231\250/\345\256\236\347\216\260\344\270\200\344\270\252\347\274\226\347\250\213\350\257\255\350\250\200/index.html" "b/\347\274\226\350\257\221\345\231\250/\345\256\236\347\216\260\344\270\200\344\270\252\347\274\226\347\250\213\350\257\255\350\250\200/index.html" new file mode 100644 index 000000000..faeb102fd --- /dev/null +++ "b/\347\274\226\350\257\221\345\231\250/\345\256\236\347\216\260\344\270\200\344\270\252\347\274\226\347\250\213\350\257\255\350\250\200/index.html" @@ -0,0 +1,50 @@ + + + + + + 实现一个编程语言 lulu | GOFamily - go 程序员宝典 + + + + + + + + + + + +