- รีลีสนี้เน้นที่ เมธอดแบบเจเนริก พร้อมปรับปรุง struct literal และ type inference รวมถึงเพิ่มฟีเจอร์ที่ใช้งานได้จริงทั่วทั้ง standard library, runtime และ toolchain
- การจัดสรรหน่วยความจำเฉพาะตามขนาด ลดต้นทุนการจัดสรรบางส่วนที่น้อยกว่า 80 ไบต์ได้สูงสุด 30% โดยโปรแกรมที่เน้นการจัดสรรคาดว่าจะได้ประสิทธิภาพเพิ่มขึ้นราว 1% และไบนารีใหญ่ขึ้นประมาณ 60KB
encoding/json/v2พร้อมใช้งานอย่างเป็นทางการ และencoding/jsonเดิมก็ใช้ v2 ภายในด้วย พร้อมเพิ่มแพ็กเกจ UUID มาตรฐาน และลายเซ็น ML-DSA ตาม FIPS 204- โปรไฟล์
goroutineleak, label ของ pprof ใน traceback,synctest.Sleepสำหรับเวลาเสมือน และเซิร์ฟเวอร์ทดสอบ HTTP ในหน่วยความจำ ช่วยให้ การวินิจฉัยและการทดสอบ concurrency ง่ายขึ้น - การทำให้ช่อง timer เป็นแบบไม่มีบัฟเฟอร์, การอ่าน response body ของ HTTP จนหมดโดยอัตโนมัติ และการเพิ่มความเข้มงวดข้อจำกัด
//go:linknameอาจกระทบโค้ดเดิม จึงต้อง ตรวจสอบความเข้ากันได้ก่อนย้ายไป Go 1.27
ภาษาและระบบชนิดข้อมูล
- เมธอดแบบเจเนริก ซึ่งเป็นหัวใจของ Go 1.27 สามารถประกาศ type parameter ของตัวเองที่เป็นอิสระจาก receiver ได้
- ก่อนหน้านี้มีเพียงฟังก์ชันระดับบนสุดเท่านั้นที่เป็นเจเนริกได้ ทำให้ operation ที่เป็นของ type ก็ต้องเขียนเป็นฟังก์ชันของแพ็กเกจ
- สามารถนิยาม operation ที่คืนค่า element type ต่างจาก input เป็นเมธอดได้ เช่น
Box[T].Map[U] - อินเทอร์เฟซยังคงประกาศเมธอดที่มี type parameter ไม่ได้ และไม่สามารถ implement อินเทอร์เฟซด้วยเมธอดแบบเจเนริกได้เช่นกัน
- คีย์ของ struct literal สามารถใช้ field selector ที่ถูกต้องได้ ไม่ใช่แค่ชื่อฟิลด์ระดับบนสุด
- สามารถ initialize ฟิลด์ที่ถูก promote จาก embedded struct ได้โดยตรง เช่น
User{ID: 7}
- สามารถ initialize ฟิลด์ที่ถูก promote จาก embedded struct ได้โดยตรง เช่น
- การอนุมานชนิดของฟังก์ชัน ขยายไปถึง conversion และ composite literal
- อนุมาน type argument ของฟังก์ชันเจเนริกจากชนิดฟังก์ชันที่คาดหวัง เช่น
[]func([]int) int{first, last} - ก่อนหน้านี้ต้อง instantiate เอง เช่น
first[int],last[int]
- อนุมาน type argument ของฟังก์ชันเจเนริกจากชนิดฟังก์ชันที่คาดหวัง เช่น
รันไทม์และประสิทธิภาพ
- คอมไพเลอร์เรียก รูทีนจัดสรรเฉพาะตามขนาด เพื่อลดต้นทุนการจัดสรรหน่วยความจำบางส่วนที่น้อยกว่า 80 ไบต์ได้สูงสุด 30%
- คาดว่าประสิทธิภาพโดยรวมของโปรแกรมจริงที่เน้นการจัดสรรจะดีขึ้นประมาณ 1%
- ไม่ต้องแก้โค้ด แต่ไบนารีจะใหญ่ขึ้นประมาณ 60KB ไม่ว่า workload จะเป็นแบบใด
- ปิดได้ด้วย
GOEXPERIMENT=nosizespecializedmallocแต่ตัวเลือกนี้มีแผนจะถูกลบใน Go 1.28
- เพิ่มการ optimize ของคอมไพเลอร์ 3 รายการที่เปิดใช้โดยค่าเริ่มต้น
- known bits data-flow pass ที่ติดตามบิตซึ่งรู้แน่ว่าเป็น 0 หรือ 1 ในค่า เพื่อลบ operation ที่ซ้ำซ้อน
- loop-invariant code motion ที่ย้ายการคำนวณที่ไม่เปลี่ยนระหว่างวนลูปออกไปนอกลูป
- คอมไพล์
switchที่เข้าเงื่อนไขเป็น lookup table รวมถึงfallthrough
GOEXPERIMENT=mapsplitgroupแบบทดลองจะเปลี่ยน layout หน่วยความจำของกลุ่ม map จากKVKVKVKVเป็นKKKKVVVVโดยค่าเริ่มต้นปิดอยู่- linker ย้าย type descriptor และ itab ไปยัง section เฉพาะ
.go.typeและลบtypelinksกับitablinks- เนื่องจาก
reflect.typelinksคืนค่า type แทน offset ไลบรารีที่เข้าถึง symbol นี้ด้วย//go:linknameอาจได้รับผลกระทบ
- เนื่องจาก
การวินิจฉัย goroutine และ profiling
- โมดูลที่
go.modเป็น Go 1.27 ขึ้นไป จะแสดง label ของruntime/pprofใน header ของ goroutine ใน traceback- บริบทอย่าง
{request: 42}ที่ติดผ่านpprof.Doจะปรากฏใน crash dump,SIGQUITและ output ของruntime.Stack - ยังสามารถใช้
GODEBUG=tracebacklabels=0ต่อไปได้ เพื่อไม่ให้ label ที่ละเอียดอ่อนถูกเปิดเผยใน traceback
- บริบทอย่าง
- ตัวตรวจจับ goroutine leak ซึ่งเป็นฟีเจอร์ทดลองใน Go 1.26 ได้เลื่อนเป็นโปรไฟล์
goroutineleakอย่างเป็นทางการ- รัน GC เพื่อค้นหา goroutine ที่ไม่สามารถคืบหน้าต่อได้ตลอดไป เช่น ติดอยู่ที่ channel หรือ mutex แล้วพิมพ์ stack ออกมา
- ในบริการสามารถเก็บผ่าน endpoint
/debug/pprof/goroutineleakได้ โดยไม่ต้องใช้GOEXPERIMENT
การเข้ารหัสและตัวระบุ
- แพ็กเกจ crypto/mldsa ใหม่ implement ลายเซ็น post-quantum ML-DSA ของ FIPS 204
- ชุด parameter 3 แบบ ได้แก่
MLDSA44,MLDSA65,MLDSA87แลกเปลี่ยนระหว่างขนาดคีย์/ลายเซ็นกับระดับความปลอดภัย - เพิ่ม ML-DSA ให้กับคีย์และลายเซ็นของ
crypto/x509รวมถึง signature scheme ของ TLS 1.3 ในcrypto/tls
- ชุด parameter 3 แบบ ได้แก่
- แพ็กเกจ uuid ใหม่ใน standard library สร้างและ parse UUID ตาม RFC 9562 ด้วยเลขสุ่มที่ปลอดภัยเชิงเข้ารหัส
uuid.New()เลือกวิธีที่เหมาะกับการใช้งานทั่วไป ส่วนNewV4()สร้างแบบสุ่มล้วน และNewV7()สร้าง UUID เรียงตามเวลาที่สร้าง- UUID ที่ประกอบจากเลขสุ่มสามารถเปรียบเทียบได้ จึงใช้
==ได้โดยตรง - UUID v7 ที่เรียงตามเวลาเหมาะกับการใช้เป็นคีย์ฐานข้อมูล
JSON และ API ประมวลผลข้อมูล
- encoding/json/v2 และ
encoding/json/jsontextระดับต่ำพร้อมใช้งานโดยไม่ต้องมีGOEXPERIMENT=jsonv2encoding/jsonv1 เดิมก็ใช้ implementation v2 ภายในด้วย- ไม่จำเป็นต้อง migrate เพราะยังคงพฤติกรรมเดิมไว้ ยกเว้นข้อความ error บางส่วน
- หากมีปัญหาความเข้ากันได้ สามารถใช้
GOEXPERIMENT=nojsonv2เพื่อคืน implementation v1 เดิมได้ - เพื่อประสิทธิภาพ v2 จะไม่ sort คีย์ของ map โดยค่าเริ่มต้น ดังนั้นใน test ที่ต้องการ output แบบเสถียรควรกำหนด
json.Deterministic
strings.CutLastและbytes.CutLastแยกค่าจากตัวคั่นตัวสุดท้าย- หากไม่มีตัวคั่น จะคืนค่า input ทั้งหมด, ส่วนท้ายว่าง และ
false
- หากไม่มีตัวคั่น จะคืนค่า input ทั้งหมด, ส่วนท้ายว่าง และ
Hasher[T]แบบเจเนริกของhash/maphashนิยาม กลยุทธ์ hash และ equality ของค่าไว้ด้วยกัน- ค่าที่เท่ากันต้องมี hash เดียวกันเสมอ
- มี
ComparableHasher[T]สำหรับ type ที่เปรียบเทียบได้ และสามารถ implement กลยุทธ์กำหนดเอง เช่น string แบบไม่สนตัวพิมพ์เล็กใหญ่ได้
math/big.Int.Divideคำนวณ quotient และ remainder พร้อมกัน โดยใช้วิธีปัดเศษที่ระบุชัดเจน ได้แก่Trunc,Floor,Round,Ceil- remainder จะเปลี่ยนตามวิธีปัดเศษด้วย ช่วยเสริมข้อจำกัดของ
QuoและModเดิมที่ตัดเข้าหาศูนย์ในโค้ดการเงินและตัวเลข
- remainder จะเปลี่ยนตามวิธีปัดเศษด้วย ช่วยเสริมข้อจำกัดของ
math/rand/v2.(*Rand).Nสร้างเลขสุ่มแบบจำกัดช่วงสำหรับ integer หรือ duration type จาก source*Randของตัวเอง
SIMD ที่พกพาได้
- แพ็กเกจ simd แบบทดลองให้ SIMD API ที่ไม่ขึ้นกับขนาดเวกเตอร์ และเปิดใช้ด้วย
GOEXPERIMENT=simd- ในสภาพแวดล้อมที่รองรับ จะคอมไพล์เป็นคำสั่งเวกเตอร์ของฮาร์ดแวร์จริง มิฉะนั้นจะ fallback เป็น emulation ด้วย Go ล้วน
- จำนวน lane ของ type อย่าง
Float32sไม่ตายตัว แต่ขึ้นกับความกว้างฮาร์ดแวร์ของแต่ละเครื่อง
- standard library ใช้ intrinsic ภายใน
simd/archsimdอยู่แล้วMemHash32,MemHash64,StrHashของ Swiss Table map ถูก implement ใหม่โดยใช้ฐาน SIMD ใหม่
การทดสอบแบบกำหนดผลลัพธ์ได้
testing/synctest.Sleepทำทั้งการเดินเวลาเสมือนและรอให้ goroutine ทั้งหมดนิ่งในครั้งเดียว- ภายใน bubble ของ
synctestเวลาจริงจะไม่เดิน ดังนั้นการรอ 2 วินาทีก็เสร็จทันที - แทนที่โค้ดซ้ำ ๆ ที่เคยใช้
time.Sleepร่วมกับsynctest.Wait
- ภายใน bubble ของ
httptest.NewTestServerรันเซิร์ฟเวอร์ด้วย เครือข่ายเสมือนในหน่วยความจำ แทนพอร์ต TCP จริงsrv.Client()เชื่อมต่อ handler โดยตรงผ่าน pipe ภายใน process- เคลียร์อัตโนมัติด้วย
t.Cleanupจึงไม่ต้องใช้defer srv.Close() - เมื่อใช้ร่วมกับ
testing/synctestจะทดสอบ HTTP round trip ในเวลาเสมือนได้อย่างรวดเร็วและ deterministic
การเปลี่ยนแปลงด้านเครือข่ายและความเข้ากันได้
- channel ที่คืนจาก
time.After,time.NewTimer,time.NewTickerฯลฯ จะเป็น channel แบบ synchronous ไม่มีบัฟเฟอร์ เสมอ- การตั้งค่า GODEBUG
asynctimerchanที่เคยใช้คืนพฤติกรรมแบบมีบัฟเฟอร์ถูกลบแล้ว
- การตั้งค่า GODEBUG
- ใน HTTP/1
http.Response.Body.Closeจะอ่าน body ที่ยังไม่ได้อ่านต่อจนถึงขีดจำกัดแบบระมัดระวัง เพื่อช่วยให้ reuse connection ได้- หากเคยพึ่ง
Closeเพื่อหยุดดาวน์โหลดขนาดใหญ่ก่อนกำหนด ต้องปิดการ reuse ด้วยTransport.DisableKeepAlives
- หากเคยพึ่ง
- เซิร์ฟเวอร์ HTTP/2 เข้าใจ client priority ตาม RFC 9218 และประมวลผล stream priority สูงก่อน
- สามารถตั้ง
Server.DisableClientPriority = trueเพื่อคืนพฤติกรรม round-robin เดิมได้
- สามารถตั้ง
crypto/x509.SystemCertPoolบน Windows และ macOS รองรับSSL_CERT_FILEและSSL_CERT_DIR- หากตั้ง variable ไว้ จะอ่าน root certificate จากดิสก์และใช้ verifier ของ Go แทน platform API
- ปิดได้ด้วย
GODEBUG=x509sslcertoverrideplatform=0
- implementation HTTP/2 ที่เคยอยู่ในไฟล์ generated
h2_bundle.goเดิม 12,226 บรรทัด ถูกแทนที่ด้วยแพ็กเกจจริงnet/http/internal/http2 net/httpเพิ่ม HTTP/3 hook แบบ pluggable ที่ยังอยู่ก่อนขั้นเปิด public และบางส่วนของ test suite รันบน HTTP/3- ยังไม่มี public API ที่เรียกใช้ได้ แต่มีฐานสำหรับ
http.Transportบน QUIC ในอนาคต
- ยังไม่มี public API ที่เรียกใช้ได้ แต่มีฐานสำหรับ
Toolchain
go testรันการตรวจ vetstdversionโดยค่าเริ่มต้น เพื่อหาการใช้ symbol ของ standard library ที่ใหม่กว่าเวอร์ชัน Go ที่ประกาศในgo.mod- สามารถดูเอกสารของเวอร์ชันโมดูลเฉพาะได้ด้วย
go doc pkg@version go doc -exแสดงรายการ example ที่รันได้ และหากระบุชื่อ example โดยตรงจะแสดง source- เพิ่ม analyzer สำหรับ modernization ได้แก่
atomictypes,embedlit,slicesbackward,unsafefuncsให้กับgo fixwaitgroupเปลี่ยนชื่อเป็นwaitgroupgoและfmtappendfถูกลบ
- ในโมดูล Go 1.27 ขึ้นไป
go mod tidyจะรวมบล็อกrequireที่กระจัดกระจายเป็น 2 บล็อกสำหรับ direct และ indirect dependency พร้อมรักษาคอมเมนต์ไว้ go tool trace -http=:6060เมื่อระบุแค่พอร์ต จะ bind เฉพาะ localhost และหากต้องการเข้าถึงจากภายนอกต้องระบุ address- คำสั่ง
goลบการรองรับระบบควบคุมเวอร์ชัน Bazaar (bzr) compile,link,asm,cgo,cover,packรองรับ response file (@file) รูปแบบ GCC เพื่อหลีกเลี่ยงข้อจำกัดความยาว command line
Unicode และขอบเขตความปลอดภัย
- ข้อมูล Unicode ใน standard library อัปเดตจาก Unicode 15 เป็น Unicode 17
- อักขระที่เพิ่มใน Unicode 16 ก็ถูกจัดหมวดหมู่เป็น symbol และ printable character ได้ถูกต้อง
- แก้ปัญหาที่สามารถใช้
os.Root.ReadDirและReaddirหลุดออกไปนอก root ได้ - การใช้
//go:linknameที่ไม่ได้รับอนุญาตทำได้ยากขึ้น- directive
linknamestdระบุ linkname ที่ import ได้เฉพาะ standard library - linker ตรวจการเข้าถึง linkname สำหรับ assembly symbol ด้วย ดังนั้นโค้ดที่พึ่ง symbol ภายในแบบไม่เป็นทางการควรทดสอบล่วงหน้า
- directive
1 ความคิดเห็น
ความเห็นจาก Lobste.rs
อาจเป็นรีลีสที่ดีที่สุดเท่าที่เคยมีมาเลยก็ได้ ในที่สุดก็มี SIMD, uuid, jsonv2 เข้ามาแล้ว
แม้จะออกนอกประเด็นไปหน่อย แต่นี่เป็นตัวอย่างที่ดีของบั๊กที่เกิดขึ้นเมื่อ มองทุกอย่างเป็นสตริง และไม่คำนึงถึงสัญญาและความสัมพันธ์ระหว่างเมธอดของอินเทอร์เฟซ
Equalไม่สนใจตัวพิมพ์เล็กพิมพ์ใหญ่ แต่Hashใช้ค่าที่แปลงเป็นตัวพิมพ์เล็กแล้ว อย่างไรก็ตามEqualFoldกับToLowerไม่ได้กำหนดความสัมพันธ์ของความเท่ากันแบบเดียวกันhttps://github.com/golang/go/issues/52204
"\u017F"(ſ, s แบบยาว) กับ"s"ถือว่าเท่ากันในEqualFoldแต่ผลของการToLowerแต่ละตัวต่างกัน ดังนั้นถ้าใส่"s"แล้วตามด้วย"ſ"แฮชจะต่างกันและถูกแทรกเป็นคนละรายการที่น่าสนใจกว่านั้นคือในเอกสารก็เตือนกรณีนี้ไว้ตรง ๆ เลย
https://github.com/golang/go/…
https://github.com/golang/go/…
ดีใจที่มีคนมารับช่วงต่อจากจุดที่ Anton หยุดไว้
ฉันชอบ Go แต่ก็สงสัยว่า ไวยากรณ์ของ Go ยังสง่างามพอไหม เมื่อยังคงเติมฟีเจอร์แบบนี้เข้าไปทุกรีลีส
func (b Box[T]) Map[U any](f func(T) U) Box[U] {ในทางกลับกัน ความยืดยาวของ Go กับ Zig ก็ดีตรงที่ไม่ต้องเลื่อนไปดูที่อื่นเพื่อหานิยามของตัวรับ และที่ จุดเรียกใช้ ซึ่งอาจสำคัญกว่าส่วนที่นิยาม ทุกภาษาก็ดูแทบไม่ต่างกันและไม่จำเป็นต้องระบุ type อย่างชัดเจน
ห่างจาก Go ไปพักหนึ่งแล้ว เลยคาดหวังกับ jsonv2 มาก แต่ก็สงสัยว่าถ้ามันกลายเป็นค่าเริ่มต้น จะไม่เท่ากับว่า Go ทำลายคำมั่นเรื่องความเข้ากันได้ที่รักษามานานหรือ
encoding/json/v2จึงเป็นสิ่งที่ต้องเลือกใช้เอง ส่วนencoding/jsonเดิมแม้ภายในจะใช้ v2 แต่ก็ยังรับประกันพฤติกรรมแบบเดิมไว้ และใน jsonv2 ก็มีแฟล็กสำหรับจำลองพฤติกรรมยกเว้นของ v1 ด้วย จึงไม่ได้ทำลาย คำมั่นเรื่องความเข้ากันได้