Еволюція в Go: огляд атавізмів
Привіт. Go — це найкраща мова програмування, на мою думку. До того ж високооплачувана. Звісно, такою вона стала завдяки регулярним покращенням. Їх і розглянемо в цій розважально-пізнавальній статті.
Передмова
Якщо ви тільки почали працювати з Go на проєкті, який до вас розробляли багато років, ви зустрінете дивні конструкції. Так розробники намагались замаскувати тодішні проблеми цієї мови програмування.
Їх і розглянемо далі, а також версії Go, в яких вони стали атавізмами.
Робота з мапами
Коли я працював з PHP, там були всі можливі функції для роботи з асоціативними масивами: array_keys, array_values та ще майже сотня інших: php.net/manual/en/ref.array.php.
Відповідно для отримання ключів мапи в PHP є функція array_keys, в JavaScript — Object.keys, а в Go потрібно було кожен раз писати рутинний код, який виглядав так:
package main import ( "fmt" ) func main() { // https://survey.stackoverflow.co/2024/ var keyTerritoryPlaceMap = map[string]int{ "USA": 1, "Germany": 2, "India": 3, "UK": 4, "Ukraine": 5, "Canada": 6, "France": 7, "Poland": 8, "Netherlands": 9, "Brazil": 10, } // Atavism keys := make([]string, 0, len(keyTerritoryPlaceMap)) for key := range keyTerritoryPlaceMap { keys = append(keys, key) } fmt.Println(keys) }
Після появи дженериків у версії 1.18 стала доступною функція maps.Keys:
package main import ( "fmt" "golang.org/x/exp/maps" ) func main() { // ... // Modern keys := maps.Keys(keyTerritoryPlaceMap) fmt.Println(keys) }
Реалізація функції maps.Keys з пакету golang.org/x/exp/maps:
package maps // Keys returns the keys of the map m. // The keys will be in an indeterminate order. func Keys[M ~map[K]V, K comparable, V any](m M) []K { r := make([]K, 0, len(m)) for k := range m { r = append(r, k) } return r }
Робота з max та min
У більшості скриптових мов програмування є функції max та min, в Go ці функції також доступні у пакеті math, але вони працюють тільки з числами типу float64. Тому зустрічав таку конструкцію:package atavism import ( "fmt" "math" ) func main() { var ( a int64 = 1 b int64 = 2 ) // Atavism var ( result = int64(math.Max(float64(a), float64(b))) ) fmt.Println(result) }
У версії Go 1.21 з’явились повноцінні функції max та min:
var ( a int64 = 1 b int64 = 2 c int64 = 3 ) var ( result = max(a, b, c) ) fmt.Println(result)
Оскільки max є вбудованою функцією, її можна використовувати у константах:
const ( a = 1 b = 2 c = 3 result = max(a, b, c) ) fmt.Println(result)
const ( a = "Rust" b = "Go" c = "Scala" result1 = max(a, b, c) result2 = max(len(a), len(b), len(c)) ) fmt.Println(result1, result2)
Цикл for range int
Цикл for став ще розумнішим у версії Go 1.22. Порівняйте самі, наскільки стало зручніше:package main import ( "testing" ) // From beginning func BenchmarkAtavismFor(b *testing.B) { for i := 0; i < b.N; i++ { _ = i } } // After Go 1.22 func BenchmarkModernFor(b *testing.B) { for i := range b.N { _ = i } for range b.N { } for range 5 { } }
Нарешті виправили помилку захоплення змінної циклу
Кожен гофер хоч раз, але зустрічав таку помилку:
package main import ( "fmt" "sync" ) func main() { var values = []string{"one", "two", "three"} var wg = new(sync.WaitGroup) wg.Add(3) for _, value := range values { go func() { fmt.Println(value) wg.Done() }() } wg.Wait() }
go run ./examples/03-01-closure-for-iteration-var-err/main.go
three three three
Й виправляв цю помилку двома способами:
for _, value := range values { // Atavism value := value go func() { fmt.Println(value) wg.Done() }() }
for _, value := range values { go func(value string) { fmt.Println(value) wg.Done() }(value) }
У версії Go 1.22 цю помилку виправили, тому конструкція value := value перетворилась на атавізм.
Хоча розробники за допомогою конструкції value := value частіше виправляли іншу помилку «Захоплення змінної-вказівника циклу», яку описував у статті «50 відтінків Go по-українськи. Аналізуємо помилки» та яка також була виправлена у версії Go 1.22.
16 коментарів
Підписатись на коментаріВідписатись від коментарів Коментарі можуть залишати тільки користувачі з підтвердженими акаунтами.