{"id":377025,"date":"2024-05-25T15:00:23","date_gmt":"2024-05-25T15:00:23","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=377025"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2024-05-25T15:00:23","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=377025","title":{"rendered":"<span>Generic Concurrency \u0432 Go<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/f6c\/e7c\/3c0\/f6ce7c3c077415596e1f55c259992d0c.jpg\" width=\"1280\" height=\"929\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f6c\/e7c\/3c0\/f6ce7c3c077415596e1f55c259992d0c.jpg\" data-blurred=\"true\"\/><\/figure>\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0433\u043e\u0444\u0435\u0440\u044b!<\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0445\u043e\u0447\u0443 \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u043c\u044b\u0441\u043b\u044f\u043c\u0438 \u0438 \u0438\u0434\u0435\u044f\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0443 \u043c\u0435\u043d\u044f \u043d\u0430\u043a\u043e\u043f\u0438\u043b\u0438\u0441\u044c \u0437\u0430 \u0432\u0440\u0435\u043c\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0434\u0436\u0435\u043d\u0435\u0440\u0438\u043a\u0430\u043c\u0438 \u0432 Go, \u0438 \u0432 \u0447\u0430\u0441\u0442\u043d\u043e\u0441\u0442\u0438 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0448\u0430\u0431\u043b\u043e\u043d\u044b \u043c\u043d\u043e\u0433\u043e\u0437\u0430\u0434\u0430\u0447\u043d\u043e\u0441\u0442\u0438 \u043c\u043e\u0433\u0443\u0442 \u0441\u0442\u0430\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u0443\u0434\u043e\u0431\u043d\u044b\u043c\u0438 \u0438 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u043c\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0434\u0436\u0435\u043d\u0435\u0440\u0438\u043a\u043e\u0432.<\/p>\n<h2>TL;DR<\/h2>\n<p>\u0414\u0436\u0435\u043d\u0435\u0440\u0438\u043a\u0438 \u0438 \u0433\u043e\u0440\u0443\u0442\u0438\u043d\u044b (\u0438 \u0438\u0442\u0435\u0440\u0430\u0442\u043e\u0440\u044b \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c) \u2013 \u044d\u0442\u043e \u043e\u0442\u043b\u0438\u0447\u043d\u044b\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u043e\u0431\u0449\u0435\u043c \u0432\u0438\u0434\u0435.<\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0438\u0445 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u043d\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<h2>\u0412\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0438 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u043a\u0430\u043a\u0443\u044e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u0440\u0435\u0448\u0430\u044e\u0442 \u0434\u0436\u0435\u043d\u0435\u0440\u0438\u043a\u0438 \u0438 \u043a\u0430\u043a \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0432\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0438\u0445 \u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u043c\u043e\u0434\u0435\u043b\u044c \u043c\u043d\u043e\u0433\u043e\u0437\u0430\u0434\u0430\u0447\u043d\u043e\u0441\u0442\u0438 \u0432 Go.<\/p>\n<blockquote>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c \u043e\u0431 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0438 (<code>map()<\/code>) \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0439 \u0438\u043b\u0438 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0435\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c,\u00a0<em>\u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435<\/em>\u00a0\u2013 \u044d\u0442\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u043d\u043e\u0432\u043e\u0439 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432, \u0433\u0434\u0435 \u043a\u0430\u0436\u0434\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u043c \u0432\u044b\u0437\u043e\u0432\u0430 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438\u00a0<code>f()<\/code>\u00a0\u0441 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u043c \u0438\u0437 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0439 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438.<\/p>\n<\/blockquote>\n<h3>\u042d\u0440\u0430 Pre-Generics<\/h3>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043f\u0440\u043e\u0441\u0442\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0446\u0435\u043b\u043e\u0447\u0438\u0441\u043b\u0435\u043d\u043d\u044b\u0445 \u0447\u0438\u0441\u0435\u043b (\u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0432 \u043a\u043e\u0434\u0435 Go \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u043d\u0430\u0437\u044b\u0432\u0430\u0442\u044c\u00a0<code>transform()<\/code>, \u0447\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u043f\u0443\u0442\u0430\u043d\u043d\u0438\u0446\u044b \u0441\u043e \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u043c \u0442\u0438\u043f\u043e\u043c\u00a0<code>map<\/code>):<\/p>\n<pre><code class=\"go\">func transform([]int, func(int) int) []int<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">func transform(xs []int, f func(int) int) []int {     ret := make([]int, len(xs))     for i, x := range xs {         ret[i] = f(x)     }     return ret }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0442\u0430\u043a\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<pre><code class=\"go\">\/\/ Output: [1, 4, 9] transform([]int{1, 2, 3}, func(n int) int {     return n * n })<\/code><\/pre>\n<p>\u041f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u043c, \u0447\u0442\u043e \u0442\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u044c \u0446\u0435\u043b\u044b\u0435 \u0447\u0438\u0441\u043b\u0430 \u0432 \u0441\u0442\u0440\u043e\u043a\u0438. \u0427\u0442\u043e\u0431\u044b \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043c\u043e\u0436\u0435\u043c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c\u00a0<code>transform()<\/code>\u00a0<em>\u043d\u0435\u043c\u043d\u043e\u0433\u043e<\/em>\u00a0\u0438\u043d\u0430\u0447\u0435:<\/p>\n<pre><code class=\"go\">func transform([]int, func(int) string) []string<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"go\">\/\/ Output: [\"1\", \"2\", \"3\"] transform([]int{1, 2, 3}, strconv.Itoa) <\/code><\/pre>\n<p>\u041a\u0430\u043a \u043d\u0430\u0441\u0447\u0435\u0442 \u0434\u0440\u0443\u0433\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043f\u0440\u0438\u0437\u043d\u0430\u043a \u0447\u0435\u0442\u043d\u043e\u0441\u0442\u0438 \u0447\u0438\u0441\u043b\u0430? \u041f\u0440\u043e\u0441\u0442\u043e\u00a0<em>\u0435\u0449\u0435 \u043e\u0434\u043d\u0430 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0430\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u043a\u0430<\/em>:<\/p>\n<pre><code class=\"go\">func transform([]int, func(int) bool) []bool<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<pre><code class=\"go\">\/\/ Output: [false, true, false] transform([]int{1, 2, 3}, func(n int) bool {     return n % 2 == 0 })<\/code><\/pre>\n<p><em>\u041e\u0431\u043e\u0431\u0449\u0430\u044f<\/em>\u00a0\u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\u00a0<code>transform()<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0432\u044b\u0448\u0435 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u0441\u043b\u0443\u0447\u0430\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f, \u043c\u043e\u0436\u043d\u043e \u0441\u043a\u0430\u0437\u0430\u0442\u044c, \u0447\u0442\u043e\u00a0<strong>\u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e<\/strong>\u00a0\u043e\u0442 \u0442\u0438\u043f\u043e\u0432, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u043e\u043d\u0430 \u0434\u0435\u043b\u0430\u0435\u0442 \u0440\u043e\u0432\u043d\u043e \u0442\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u0441\u043d\u043e\u0432\u0430 \u0438 \u0441\u043d\u043e\u0432\u0430. \u0415\u0441\u043b\u0438 \u0431\u044b \u043c\u044b \u0445\u043e\u0442\u0435\u043b\u0438 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u0434 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0442\u0438\u043f\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0448\u0430\u0431\u043b\u043e\u043d\u044b\u00a0<code>text\/template<\/code>, \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u0442\u0430\u043a:<\/p>\n<pre><code class=\"go\">func transform_{{ .A }}_{{ .B }}([]{{ .A }}, func({{ .A }}) {{ .B }}) []{{ .B }}  \/\/ transform_int_int([]int, func(int) int) []int \/\/ transform_int_string([]int, func(int) string) []string \/\/ transform_int_bool([]int, func(int) bool) []bool<\/code><\/pre>\n<blockquote>\n<p>\u0420\u0430\u043d\u044c\u0448\u0435 \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0435 \u0448\u0430\u0431\u043b\u043e\u043d\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438\u0441\u044c \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u201cgeneric\u201d \u043a\u043e\u0434\u0430. \u041f\u0440\u043e\u0435\u043a\u0442\u00a0<a href=\"https:\/\/github.com\/cheekybits\/genny\" rel=\"noopener noreferrer nofollow\">genny<\/a>\u00a0\u2013 \u043e\u0434\u0438\u043d \u0438\u0437 \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432.<\/p>\n<\/blockquote>\n<h3>\u042d\u0440\u0430 Generics<\/h3>\n<p>\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u0434\u0436\u0435\u043d\u0435\u0440\u0438\u043a\u0430\u043c, \u0442\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c\u00a0<em>\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438\u0437\u043e\u0432\u0430\u0442\u044c<\/em>\u00a0\u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0438 \u0442\u0438\u043f\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e\u00a0<em>\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0442\u0438\u043f\u0430<\/em>\u00a0\u0438 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c\u00a0<code>transform()<\/code>\u00a0\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"go\">func transform[A, B any]([]A, func(A) B) []B<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u0418 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u0441\u044f \u0441\u043e\u0432\u0441\u0435\u043c \u0447\u0443\u0442\u044c-\u0447\u0443\u0442\u044c!<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">func transform[A, B any](xs []A, f func(A) B) []B {     ret := make([]B, len(xs))     for i, x := range xs {         ret[i] = f(x)     }     return ret }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u00a0<code>transform()<\/code>\u00a0\u0434\u043b\u044f \u043b\u044e\u0431\u044b\u0445 \u0442\u0438\u043f\u043e\u0432 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0438 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 (\u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e\u00a0<code>square(int) int<\/code>\u00a0\u0438\u00a0<code>isEven(int) bool<\/code>\u00a0\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u044b \u0433\u0434\u0435-\u0442\u043e \u0432\u044b\u0448\u0435 \u0432 \u043f\u0430\u043a\u0435\u0442\u0435):<\/p>\n<pre><code class=\"go\">transform([]int{1, 2, 3}, square)       \/\/ [1, 4, 9] transform([]int{1, 2, 3}, strconv.Itoa) \/\/ [\"1\", \"2\", \"3\"] transform([]int{1, 2, 3}, isEven)       \/\/ [false, true, false]<\/code><\/pre>\n<h2>\u041f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0442\u0435\u043c\u0435 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u0438 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0448\u0430\u0431\u043b\u043e\u043d\u044b \u043c\u043d\u043e\u0433\u043e\u0437\u0430\u0434\u0430\u0447\u043d\u043e\u0441\u0442\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u043d\u043e \u0441 \u0434\u0436\u0435\u043d\u0435\u0440\u0438\u043a\u0430\u043c\u0438, \u0438 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0442\u0430\u043d\u0443\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043b\u0443\u0447\u0448\u0435 \u043e\u0442 \u0442\u0430\u043a\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<h3>\u041f\u0430\u043a\u0435\u0442\u00a0x\/sync\/errgroup<\/h3>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043f\u043e\u0433\u0440\u0443\u0437\u0438\u0442\u044c\u0441\u044f \u0432 \u043a\u043e\u0434, \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043e\u0442\u0432\u043b\u0435\u0447\u0441\u044f \u0438 \u0432\u0437\u0433\u043b\u044f\u043d\u0443\u0442\u044c \u043d\u0430 \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u0443\u044e \u0432 Go \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443\u00a0<a href=\"https:\/\/pkg.go.dev\/golang.org\/x\/sync\/errgroup\" rel=\"noopener noreferrer nofollow\">golang.org\/x\/sync\/errgroup<\/a>. \u0412\u043a\u0440\u0430\u0442\u0446\u0435, \u043e\u043d\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0433\u043e\u0440\u0443\u0442\u0438\u043d\u044b \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0440\u0430\u0437\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447 \u0438 \u043e\u0436\u0438\u0434\u0430\u0442\u044c \u0438\u0445 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u0438\u043b\u0438 \u043e\u0448\u0438\u0431\u043a\u0438.<\/p>\n<p>\u041f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"go\">\/\/ Create workers group and a context which will get canceled if any of the \/\/ tasks fails. g, gctx := errgroup.WithContext(ctx) g.Go(func() error { return doSomeFun(gctx) }) g.Go(func() error { return doEvenMoreFun(gctx) }) if err := g.Wait(); err != nil { \/\/ handle error }<\/code><\/pre>\n<p>\u041f\u0440\u0438\u0447\u0438\u043d\u0430, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u044f \u0443\u043f\u043e\u043c\u044f\u043d\u0443\u043b\u00a0<code>errgroup<\/code>\u00a0\u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0441 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0434\u0440\u0443\u0433\u043e\u0439 \u0438 \u0431\u043e\u043b\u0435\u0435 \u043e\u0431\u0449\u0435\u0439 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f, \u0442\u043e \u043c\u044b \u0443\u0432\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u043f\u043e \u0441\u0443\u0442\u0438, \u043e\u043d\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0442\u0435\u043c \u0436\u0435 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u043e\u043c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f.\u00a0<code>errgroup<\/code>\u00a0<em>\u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442<\/em>\u00a0\u043d\u0430\u0431\u043e\u0440 \u0437\u0430\u0434\u0430\u0447 (\u0444\u0443\u043d\u043a\u0446\u0438\u0439) \u0441 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u043c \u0438\u0445 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f, \u0430 \u0442\u0430\u043a \u0436\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0431\u043e\u0431\u0449\u0435\u043d\u043d\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u201c\u0432\u0441\u043f\u043b\u044b\u0442\u0438\u044f\u201d \u043e\u0448\u0438\u0431\u043e\u043a c \u043f\u0440\u0435\u0440\u044b\u0432\u0430\u043d\u0438\u0435\u043c \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0449\u0438\u0445\u0441\u044f \u0437\u0430\u0434\u0430\u0447 (\u0447\u0435\u0440\u0435\u0437 \u043e\u0442\u043c\u0435\u043d\u0443\u00a0<code>context.Context<\/code>).<\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0435, \u0438, \u043a\u0430\u043a \u043d\u0430\u043c\u0435\u043a\u0430\u0435\u0442 \u0447\u0430\u0441\u0442\u043e\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u043b\u043e\u0432 \u201c\u043e\u0431\u0449\u0438\u0439\u201d \u0438 \u201c\u043e\u0431\u043e\u0431\u0449\u0435\u043d\u043d\u044b\u0439\u201d, \u043c\u044b \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c\u0441\u044f \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u0432 \u043e\u0431\u0449\u0435\u043c \u0432\u0438\u0434\u0435.<\/p>\n<h3>\u041d\u0430\u0438\u0432\u043d\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f<\/h3>\n<p>\u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044f\u0441\u044c \u043a \u0444\u0443\u043d\u043a\u0446\u0438\u0438\u00a0<code>transform()<\/code>. \u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u0447\u0442\u043e \u0432\u0441\u0435 \u0432\u044b\u0437\u043e\u0432\u044b\u00a0<code>f()<\/code>\u00a0\u043c\u043e\u0433\u0443\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e, \u043d\u0435 \u043b\u043e\u043c\u0430\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043d\u0430\u0448\u0435\u0439 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b. \u0422\u043e\u0433\u0434\u0430 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043d\u0430\u0447\u0430\u0442\u044c \u0441 \u044d\u0442\u043e\u0439 \u043d\u0430\u0438\u0432\u043d\u043e\u0439\u00a0<em>\u043c\u043d\u043e\u0433\u043e\u0437\u0430\u0434\u0430\u0447\u043d\u043e\u0439<\/em>\u00a0\u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438:<\/p>\n<pre><code class=\"go\">func transform[A, B any](as []A, f func(A) B) []B { bs := make([]B, len(as))  var wg sync.WaitGroup for i := 0; i &lt; len(as); i++ { wg.Add(1) go func(i int) { defer wg.Done() bs[i] = f(as[i]) }(i) } wg.Wait()  return bs }<\/code><\/pre>\n<p>\u041c\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0433\u043e\u0440\u0443\u0442\u0438\u043d\u0443 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0432\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c\u00a0<code>f(elem)<\/code>. \u0417\u0430\u0442\u0435\u043c \u043c\u044b \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043f\u043e \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u043c\u0443 \u0438\u043d\u0434\u0435\u043a\u0441\u0443 \u0432 \u201c\u043e\u0431\u0449\u0435\u043c\u201d \u0441\u043b\u0430\u0439\u0441\u0435\u00a0<code>bs<\/code>. \u041d\u0438\u043a\u0430\u043a\u043e\u0433\u043e \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430, \u043f\u0440\u0435\u0440\u044b\u0432\u0430\u043d\u0438\u044f, \u0438\u043b\u0438 \u043e\u0448\u0438\u0431\u043e\u043a \u2013 \u0442\u0430\u043a\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043d\u0435 \u043a\u0430\u0436\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u0435\u0437\u043d\u043e\u0439 \u0432 \u0447\u0435\u043c-\u043b\u0438\u0431\u043e, \u043a\u0440\u043e\u043c\u0435 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0439.<\/p>\n<h3>\u041e\u0442\u043c\u0435\u043d\u0430 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430<\/h3>\n<p>\u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u043c\u0438\u0440\u0435 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u0442\u0440\u0435\u0431\u0443\u044e\u0449\u0438\u0445 \u043c\u043d\u043e\u0433\u043e\u0437\u0430\u0434\u0430\u0447\u043d\u043e\u0441\u0442\u0438, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0442\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 i\/o, \u0431\u0443\u0434\u0443\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u043e\u043c\u00a0<code>context.Context<\/code>. \u0415\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442, \u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0435\u0433\u043e \u0442\u0430\u0439\u043c-\u0430\u0443\u0442 \u0438\u043b\u0438 \u043e\u0442\u043c\u0435\u043d\u0430. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u044d\u0442\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"go\">func transform[A, B any]( ctx context.Context, as []A, f func(context.Context, A) (B, error), ) ( []B, error, ) { bs := make([]B, len(as)) es := make([]error, len(as))  subctx, cancel := context.WithCancel(ctx) defer cancel()  var wg sync.WaitGroup for i := 0; i &lt; len(as); i++ { wg.Add(1) go func(i int) { defer wg.Done() bs[i], es[i] = f(subctx, as[i]) if es[i] != nil { cancel() } }(i) } wg.Wait()  err := errors.Join(es...) if err != nil { return nil, err } return bs, nil }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u201c\u043e\u0431\u0449\u0438\u0439\u201d \u0441\u043b\u0430\u0439\u0441\u00a0<code>es<\/code>\u00a0\u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u0442\u0435\u043d\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u043e\u0448\u0438\u0431\u043e\u043a\u00a0<code>f()<\/code>. \u0415\u0441\u043b\u0438 \u043a\u0430\u043a\u043e\u0439-\u043b\u0438\u0431\u043e \u0432\u044b\u0437\u043e\u0432\u00a0<code>f()<\/code>\u00a0\u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0435\u0442\u0441\u044f \u043e\u0448\u0438\u0431\u043a\u043e\u0439, \u043c\u044b \u043e\u0442\u043c\u0435\u043d\u044f\u0435\u043c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0432\u0441\u0435\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430\u00a0<code>transform()<\/code>\u00a0\u0438 \u043e\u0436\u0438\u0434\u0430\u0435\u043c, \u0447\u0442\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0449\u0438\u0439\u0441\u044f \u0432\u044b\u0437\u043e\u0432\u00a0<code>f()<\/code>\u00a0\u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442 \u043e\u0442\u043c\u0435\u043d\u0443 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430 \u0438, \u0435\u0441\u043b\u0438 \u043e\u043d\u0430 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442, \u043f\u0440\u0435\u0440\u044b\u0432\u0430\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0441\u043a\u043e\u0440\u0435\u0435.<\/p>\n<h3>\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u043c\u043d\u043e\u0433\u043e\u0437\u0430\u0434\u0430\u0447\u043d\u043e\u0441\u0442\u0438<\/h3>\n<p>\u041d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435, \u043c\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0442\u044c \u043e\u00a0<code>f()<\/code>. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438\u00a0<code>transform()<\/code>\u00a0\u043c\u043e\u0433\u0443\u0442 \u0437\u0430\u0445\u043e\u0442\u0435\u0442\u044c \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0432\u044b\u0437\u043e\u0432\u043e\u0432\u00a0<code>f()<\/code>. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440,\u00a0<code>f()<\/code>\u00a0\u043c\u043e\u0436\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c url \u043d\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 http \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u0411\u0435\u0437 \u043a\u0430\u043a\u0438\u0445-\u043b\u0438\u0431\u043e \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0439 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0435\u0440\u0435\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 \u0438\u043b\u0438 \u0437\u0430\u0431\u0430\u043d\u0438\u0442\u044c \u0441\u0430\u043c\u0438\u0445 \u0441\u0435\u0431\u044f.<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u043a\u0430 \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u0434\u0443\u043c\u0430\u0442\u044c \u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0438\u0437\u043c\u0430\u00a0<code>int<\/code>\u00a0\u0432 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0444\u0443\u043d\u043a\u0446\u0438\u0438.<\/p>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043e\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f\u00a0<code>sync.WaitGroup<\/code>\u00a0\u043a\u00a0<a href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%A1%D0%B5%D0%BC%D0%B0%D1%84%D0%BE%D1%80_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)\" rel=\"noopener noreferrer nofollow\">\u0441\u0435\u043c\u0430\u0444\u043e\u0440\u0443<\/a>\u00a0<code>chan<\/code>, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c (\u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435) \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0449\u0438\u0445\u0441\u044f \u0433\u043e\u0440\u0443\u0442\u0438\u043d, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u043e\u0442\u043c\u0435\u043d\u0443 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f\u00a0<code>select<\/code>.<\/p>\n<pre><code class=\"go\">func transform[A, B any]( ctx context.Context, parallelism int, as []A, f func(context.Context, A) (B, error), ) ( []B, error, ) { bs := make([]B, len(as)) es := make([]error, len(as))  \/\/ FIXME: if the given context is already cancelled, no worker will be \/\/ started but the transform() call will return bs, nil. subctx, cancel := context.WithCancel(ctx) defer cancel()  sem := make(chan struct{}, parallelism) sched: for i := 0; i &lt; len(as); i++ { \/\/ We are checking the sub-context cancellation here, in addition to \/\/ the user-provided context, to handle cases where f() returns an \/\/ error, which leads to the termination of transform. if subctx.Err() != nil { break } select { case &lt;-subctx.Done(): break sched  case sem &lt;- struct{}{}: \/\/ Being able to send a tick into the channel means we can start a \/\/ new worker goroutine. This could be either due to the completion \/\/ of a previous goroutine or because the number of started worker \/\/ goroutines is less than the given parallism value. } go func(i int) { defer func() { \/\/ Signal that the element has been processed and the worker \/\/ goroutine has completed. &lt;-sem }() bs[i], es[i] = f(subctx, as[i]) if es[i] != nil { cancel() } }(i) } \/\/ Since each goroutine reads off one tick from the semaphore before exit, \/\/ filling the channel with artificial ticks makes us sure that all started \/\/ goroutines completed their execution. \/\/ \/\/ FIXME: for the high values of parallelism this loop becomes slow. for i := 0; i &lt; cap(sem); i++ { \/\/ NOTE: we do not check the user-provided context here because we want \/\/ to return from this function only when all the started worker \/\/ goroutines have completed. This is to avoid surprising users with \/\/ some of the f() function calls still running in the background after \/\/ transform() returns. \/\/ \/\/ This implies f() should respect context cancellation and return as \/\/ soon as its context gets cancelled. sem &lt;- struct{}{} }  err := errors.Join(es...) if err != nil { return nil, err } return bs, nil }<\/code><\/pre>\n<blockquote>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0439 \u0438 \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0439\u00a0<code>tranform()<\/code>\u00a0\u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0432 \u0442\u043e\u043c \u0432\u0438\u0434\u0435, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043e\u043d\u0430 \u0441\u0435\u0439\u0447\u0430\u0441 \u0435\u0441\u0442\u044c, \u0438 \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0440\u0430\u0441\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u043d\u0430 \u043e\u0442\u043a\u0443\u043f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\u00a0<code>f()<\/code>. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c\u00a0<code>N<\/code>\u00a0\u0433\u043e\u0440\u0443\u0442\u0438\u043d \u0432\u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0439 \u043c\u043d\u043e\u0433\u043e\u0437\u0430\u0434\u0430\u0447\u043d\u043e\u0441\u0442\u0438 \u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e\u00a0<code>transform()<\/code>\u00a0\u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u0445 \u0442\u0430\u043a, \u043a\u0430\u043a \u043e\u043d \u0445\u043e\u0447\u0435\u0442. \u042d\u0442\u043e \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u043e \u0431\u044b \u043d\u0430\u043a\u043b\u0430\u0434\u043d\u044b\u0445 \u0440\u0430\u0441\u0445\u043e\u0434\u043e\u0432 \u043d\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u00a0<code>N<\/code>\u00a0\u0433\u043e\u0440\u0443\u0442\u0438\u043d \u0432\u043c\u0435\u0441\u0442\u043e\u00a0<code>P<\/code>\u00a0(\u0433\u0434\u0435\u00a0<code>P<\/code>\u00a0\u2013 \u044d\u0442\u043e \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u201c\u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0438\u0437\u043c\u0430\u201d, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u043c\u0435\u043d\u044c\u0448\u0435, \u0447\u0435\u043c\u00a0<code>N<\/code>). \u042d\u0442\u043e \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u0434\u0440\u0430\u0437\u0443\u043c\u0435\u0432\u0430\u043b\u043e \u0431\u044b \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0430\u043a\u043b\u0430\u0434\u043d\u044b\u0435 \u0440\u0430\u0441\u0445\u043e\u0434\u044b \u043d\u0430 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044e \u0433\u043e\u0440\u0443\u0442\u0438\u043d, \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u043e\u0433\u043e \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u0430. \u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u0432\u0441\u0435 \u044d\u0442\u043e \u0438\u0437\u043b\u0438\u0448\u043d\u0435, \u043c\u044b \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e the hard way, \u043d\u043e \u0432\u043e \u043c\u043d\u043e\u0433\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u044d\u0442\u0438 \u0443\u0441\u043b\u043e\u0436\u043d\u0435\u043d\u0438\u044f \u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043d\u0435\u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u043c\u0438.<\/p>\n<\/blockquote>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">\/\/ Initialised x\/time\/rate.Limiter instance. var lim *rate.Limiter transform(ctx, as, func(_ context.Context, url string) (int, error) {     if err := lim.Wait(ctx); err != nil {         return 0, err     }      \/\/ Process url.      return 42, nil })<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h3>\u041f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0433\u043e\u0440\u0443\u0442\u0438\u043d<\/h3>\n<p>\u0412 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0438 \u043c\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u043b\u0438 \u0433\u043e\u0440\u0443\u0442\u0438\u043d\u044b \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438, \u043d\u043e \u043d\u0435 \u0431\u043e\u043b\u0435\u0435\u00a0<code>parallelism<\/code>\u00a0\u0433\u043e\u0440\u0443\u0442\u0438\u043d \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e. \u042d\u0442\u043e \u0432\u044b\u044f\u0432\u043b\u044f\u0435\u0442 \u0435\u0449\u0451 \u043e\u0434\u043d\u0443 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0443\u044e \u0434\u0435\u0442\u0430\u043b\u044c \u2013 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u043c\u043e\u0433\u0443\u0442 \u0437\u0430\u0445\u043e\u0442\u0435\u0442\u044c \u0438\u043c\u0435\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0433\u043e\u0440\u0443\u0442\u0438\u043d\u044b. \u041f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u043c, \u0447\u0442\u043e \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c\u00a0<code>N<\/code>\u00a0\u0437\u0430\u0434\u0430\u0447 \u0441 \u043c\u0430\u043a\u0441\u0438\u043c\u0443\u043c\u043e\u043c\u00a0<code>P<\/code>\u00a0\u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0449\u0438\u0445\u0441\u044f \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e (\u0438\u00a0<code>P<\/code>\u00a0\u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043c\u0435\u043d\u044c\u0448\u0435\u00a0<code>N<\/code>). \u0415\u0441\u043b\u0438 \u043a\u0430\u0436\u0434\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043a\u0430\u043a\u043e\u0439-\u043b\u0438\u0431\u043e \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0438 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0430\u043b\u043b\u043e\u043a\u0430\u0446\u0438\u0438 \u043f\u0430\u043c\u044f\u0442\u0438, \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u0435\u0441\u0441\u0438\u0438 \u0431\u0430\u0437\u044b \u0434\u0430\u043d\u043d\u044b\u0445 \u0438\u043b\u0438, \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c, \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043e\u0434\u043d\u043e\u043f\u043e\u0442\u043e\u0447\u043d\u043e\u0439 Cgo \u201c\u0441\u043e\u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b\u201d, \u0442\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u043b\u043e\u0433\u0438\u0447\u043d\u043e \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e\u00a0<code>P<\/code>\u00a0\u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u0438 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u0445 \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442.<\/p>\n<p>\u041a\u0430\u043a \u0438 \u0432\u044b\u0448\u0435, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043e\u0441\u0442\u0430\u0432\u0438\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u043f\u0435\u0440\u0435\u0434\u0430\u0447\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0432 \u0441\u0442\u043e\u0440\u043e\u043d\u0435.<\/p>\n<pre><code class=\"go\">func transform[A, B any]( ctx context.Context, prepare func(context.Context) (context.Context, context.CancelFunc), parallelism int, as []A, f func(context.Context, A) (B, error), ) ( []B, error, ) { bs := make([]B, len(as)) es := make([]error, len(as))  \/\/ FIXME: if the given context is already cancelled, no worker will be \/\/ started but the transform() call will return bs, nil. subctx, cancel := context.WithCancel(ctx) defer cancel()  sem := make(chan struct{}, parallelism) wrk := make(chan int) sched: for i := 0; i &lt; len(as); i++ { \/\/ We are checking the sub-context cancellation here, in addition to \/\/ the user-provided context, to handle cases where f() returns an \/\/ error, which leads to the termination of transform. if subctx.Err() != nil { break } select { case &lt;-subctx.Done(): break sched  case wrk &lt;- i: \/\/ There is an idle worker goroutine that is ready to process the \/\/ next element. continue  case sem &lt;- struct{}{}: \/\/ Being able to send a tick into the channel means we can start a \/\/ new worker goroutine. This could be either due to the completion \/\/ of a previous goroutine or because the number of started worker \/\/ goroutines is less than the given parallism value. } go func(i int) { defer func() { \/\/ Signal that the element has been processed and the worker \/\/ goroutine has completed. &lt;-sem }()  \/\/ Capture the subctx from the dispatch loop. This prevents \/\/ overriding it if the given prepare() function is not nil. subctx := subctx if prepare != nil { var cancel context.CancelFunc subctx, cancel = prepare(subctx) defer cancel() } for { bs[i], es[i] = f(subctx, as[i]) if es[i] != nil { cancel() return } var ok bool i, ok = &lt;-wrk if !ok { \/\/ Work channel has been closed, which means we will not \/\/ get any new tasks for this worker and can return. break } } }(i) } \/\/ Since each goroutine reads off one tick from the semaphore before exit, \/\/ filling the channel with artificial ticks makes us sure that all started \/\/ goroutines completed their execution. \/\/ \/\/ FIXME: for the high values of parallelism this loop becomes slow. for i := 0; i &lt; cap(sem); i++ { \/\/ NOTE: we do not check the user-provided context here because we want \/\/ to return from this function only when all the started worker \/\/ goroutines have completed. This is to avoid surprising users with \/\/ some of the f() function calls still running in the background after \/\/ transform() returns. \/\/ \/\/ This implies f() should respect context cancellation and return as \/\/ soon as its context gets cancelled. sem &lt;- struct{}{} }  err := errors.Join(es...) if err != nil { return nil, err } return bs, nil }<\/code><\/pre>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u043c\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c\u00a0<em>\u0434\u043e<\/em>\u00a0<code>P<\/code>\u00a0\u0433\u043e\u0440\u0443\u0442\u0438\u043d \u0438 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0437\u0430\u0434\u0430\u0447\u0438 \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u0430\u043d\u0430\u043b\u0430\u00a0<code>wrk<\/code>\u00a0\u0431\u0435\u0437 \u0431\u0443\u0444\u0435\u0440\u0430. \u041c\u044b \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0431\u0443\u0444\u0435\u0440, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u043e\u0431\u0440\u0430\u0442\u043d\u0443\u044e \u0441\u0432\u044f\u0437\u044c \u043e \u0442\u043e\u043c, \u0435\u0441\u0442\u044c \u043b\u0438 \u0432 \u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u0431\u0435\u0437\u0434\u0435\u0439\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0433\u043e\u0440\u0443\u0442\u0438\u043d\u044b \u0438\u043b\u0438 \u0441\u0442\u043e\u0438\u0442 \u043b\u0438 \u043d\u0430\u043c \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0437\u0430\u043f\u0443\u0441\u043a\u0430 \u043d\u043e\u0432\u043e\u0439. \u041a\u0430\u043a \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u0441\u0435 \u0437\u0430\u0434\u0430\u0447\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u044b \u0438\u043b\u0438 \u043b\u044e\u0431\u043e\u0439 \u0438\u0437 \u0432\u044b\u0437\u043e\u0432\u043e\u0432\u00a0<code>f()<\/code>\u00a0\u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0435\u0442\u0441\u044f \u043e\u0448\u0438\u0431\u043a\u043e\u0439, \u043c\u044b \u0441\u0438\u0433\u043d\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u0432\u0441\u0435\u043c \u0433\u043e\u0440\u0443\u0442\u0438\u043d\u0430\u043c \u043e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u0440\u0435\u043a\u0440\u0430\u0442\u0438\u0442\u044c \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 (\u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e\u00a0<code>close(wrk)<\/code>).<\/p>\n<blockquote>\n<p>\u041a\u0430\u043a \u0438 \u0432 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u043c \u0440\u0430\u0437\u0434\u0435\u043b\u0435, \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u0434\u043e\u0431\u0438\u0442\u044c\u0441\u044f \u043d\u0430 \u0443\u0440\u043e\u0432\u043d\u0435\u00a0<code>f()<\/code>, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f\u00a0<code>sync.Pool<\/code>.\u00a0<code>f()<\/code>\u00a0\u043c\u043e\u0436\u0435\u0442 \u0432\u0437\u044f\u0442\u044c \u0440\u0435\u0441\u0443\u0440\u0441 (\u0438\u043b\u0438 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0435\u0433\u043e, \u0435\u0441\u043b\u0438 \u0432 \u043f\u0443\u043b\u0435 \u043d\u0435\u0442 \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u044b\u0445) \u0438 \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0435\u0433\u043e, \u043a\u043e\u0433\u0434\u0430 \u043e\u043d \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u043d\u0443\u0436\u0435\u043d. \u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043d\u0430\u0431\u043e\u0440 \u0433\u043e\u0440\u0443\u0442\u0438\u043d \u0444\u0438\u043a\u0441\u0438\u0440\u043e\u0432\u0430\u043d, \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e\u0441\u0442\u044c \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u0440\u0435\u0441\u0443\u0440\u0441\u044b \u0431\u0443\u0434\u0443\u0442 \u0438\u043c\u0435\u0442\u044c \u0445\u043e\u0440\u043e\u0448\u0443\u044e \u043b\u043e\u043a\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u044c CPU \u0432\u0435\u043b\u0438\u043a\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u043a\u043b\u0430\u0434\u043d\u044b\u0435 \u0440\u0430\u0441\u0445\u043e\u0434\u044b \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043c\u0438\u043d\u0438\u043c\u0430\u043b\u044c\u043d\u044b\u043c\u0438.<\/p>\n<\/blockquote>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043d\u0430 \u0441\u0442\u043e\u0440\u043e\u043d\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">\/\/ Note that this snippet assumes `transform()` can limit its concurrency. var pool sync.Pool transform(ctx, 8, as, func(_ context.Context, userID string) (int, error) {     sess := pool.Get().(*db.Session)     if sess == nil {         \/\/ Initialise database session.     }     defer pool.Put(sess)      \/\/ Process userID.      return 42, nil })<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u041e\u0431\u043e\u0431\u0449\u0435\u043d\u0438\u0435\u00a0transform()<\/h2>\n<p>\u0414\u043e \u0441\u0438\u0445 \u043f\u043e\u0440 \u043d\u0430\u0448 \u0444\u043e\u043a\u0443\u0441 \u0431\u044b\u043b \u043d\u0430 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0438 \u0441\u043b\u0430\u0439\u0441\u043e\u0432, \u0447\u0442\u043e \u0432\u043e \u043c\u043d\u043e\u0433\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e. \u041e\u0434\u043d\u0430\u043a\u043e, \u0447\u0442\u043e, \u0435\u0441\u043b\u0438 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u0442\u0438\u043f\u044b\u00a0<code>map<\/code>\u00a0\u0438\u043b\u0438\u00a0<code>chan<\/code>? \u041c\u043e\u0436\u0435\u043c \u043b\u0438 \u043c\u044b \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u0432\u0441\u0435, \u043f\u043e \u0447\u0435\u043c\u0443 \u043c\u043e\u0436\u043d\u043e \u0438\u0442\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e\u00a0<code>range<\/code>? \u0418, \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u044f \u0441 \u0446\u0438\u043a\u043b\u043e\u043c\u00a0<code>for<\/code>, \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043b\u0438 \u043d\u0430\u043c \u0432\u0441\u0435\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e\u00a0<em>\u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c<\/em>\u00a0\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f?<\/p>\n<p>\u042d\u0442\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u0432\u043e\u043f\u0440\u043e\u0441\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u0438\u0432\u043e\u0434\u044f\u0442 \u043d\u0430\u0441 \u043a \u043c\u044b\u0441\u043b\u0438 \u043e \u0442\u043e\u043c, \u0447\u0442\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043e\u0431\u043e\u0431\u0449\u0438\u0442\u044c \u043d\u0430\u0448 \u043f\u043e\u0434\u0445\u043e\u0434 \u043a \u043c\u043d\u043e\u0433\u043e\u0437\u0430\u0434\u0430\u0447\u043d\u043e\u0439 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0438. \u041c\u044b \u043c\u043e\u0436\u0435\u043c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0431\u043e\u043b\u0435\u0435 \u201c\u043d\u0438\u0437\u043a\u043e\u0433\u043e \u0443\u0440\u043e\u0432\u043d\u044f\u201d, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0443\u0434\u0435\u0442 \u0432\u0435\u0441\u0442\u0438 \u0441\u0435\u0431\u044f \u043f\u043e\u0447\u0442\u0438 \u0442\u0430\u043a \u0436\u0435, \u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0434\u0435\u043b\u0430\u0442\u044c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043c\u0435\u043d\u044c\u0448\u0435 \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u043e \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0438 \u0432\u044b\u0445\u043e\u0434\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445. \u0422\u043e\u0433\u0434\u0430 \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043d\u0435 \u0442\u0430\u043a \u043c\u043d\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u0441\u043f\u0435\u0446\u0438\u0444\u0438\u0447\u043d\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e\u00a0<code>transform()<\/code>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0430 \u0431\u044b \u201c\u043d\u0438\u0437\u043a\u043e\u0443\u0440\u043e\u0432\u043d\u0435\u0432\u0443\u044e\u201d \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0438. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u0437\u043e\u0432\u0435\u043c \u0442\u0430\u043a\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e\u00a0<code>iterate()<\/code>\u00a0\u0438 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043c \u0432\u0445\u043e\u0434\u043d\u044b\u0435 \u0438 \u0432\u044b\u0445\u043e\u0434\u043d\u044b\u0435 \u0434\u0430\u043d\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438, \u0430 \u043d\u0435 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u044b\u043c\u0438 \u0442\u0438\u043f\u0430\u043c\u0438 \u0434\u0430\u043d\u043d\u044b\u0445. \u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u0442\u044c \u0432\u0445\u043e\u0434\u043d\u044b\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e\u00a0<code>pull()<\/code>\u00a0\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u043e\u0431\u0440\u0430\u0442\u043d\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e\u00a0<code>push()<\/code>. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u00a0<code>iterate()<\/code>\u00a0\u0441\u043c\u043e\u0436\u0435\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432.<\/p>\n<p>\u041c\u044b \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u043b\u0436\u043d\u044b \u043f\u043e\u0434\u0443\u043c\u0430\u0442\u044c \u043e \u0442\u043e\u043c, \u043a\u0430\u043a\u0438\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b\u00a0<code>iterate()<\/code>\u00a0\u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e. \u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043c\u044b \u043f\u043b\u0430\u043d\u0438\u0440\u0443\u0435\u043c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u043e\u043f\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u044b\u043c,\u00a0<code>(B, error)<\/code>\u00a0\u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u043a\u0430\u0436\u0435\u0442\u0441\u044f \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u044b\u043c \u0438 \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u043c \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u043c. \u041d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435 \u044d\u0442\u043e \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u043d\u0435\u043e\u0434\u043d\u043e\u0437\u043d\u0430\u0447\u043d\u044b\u0439 \u0432\u043e\u043f\u0440\u043e\u0441, \u0438, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0432 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0435 \u0441\u043b\u0443\u0447\u0430\u0435\u0432 \u044f\u0432\u043d\u043e\u0435 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0431\u044b\u043b\u043e \u0431\u044b \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0442\u0438\u0442\u0435\u043b\u044c\u043d\u0435\u0435. \u041e\u0434\u043d\u0430\u043a\u043e, \u0441\u0435\u043c\u0430\u043d\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u044d\u0442\u043e \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u043e\u0433\u043e \u0441\u043c\u044b\u0441\u043b\u0430, \u0442\u0430\u043a \u043a\u0430\u043a \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u00a0<code>f()<\/code>\u00a0\u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442\u0441\u044f \u0432 \u0432\u044b\u0437\u043e\u0432\u00a0<code>push()<\/code>\u00a0\u0431\u0435\u0437 \u043a\u0430\u043a\u043e\u0439-\u043b\u0438\u0431\u043e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438, \u0447\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u0443\u00a0<code>iterate()<\/code>\u00a0\u043d\u0435\u0442 \u043d\u0438\u043a\u0430\u043a\u0438\u0445 \u043e\u0436\u0438\u0434\u0430\u043d\u0438\u0439 \u0438\u043b\u0438 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430. \u0414\u0440\u0443\u0433\u0438\u043c\u0438 \u0441\u043b\u043e\u0432\u0430\u043c\u0438, \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0438\u043c\u0435\u0435\u0442 \u0441\u043c\u044b\u0441\u043b \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0438\u00a0<code>push()<\/code>, \u043a\u043e\u0442\u043e\u0440\u0443\u044e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0435\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0439 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u0431\u0443\u0434\u0435\u0442 \u043b\u0443\u0447\u0448\u0435 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u0438\u0442\u0435\u0440\u0430\u0442\u043e\u0440\u0430\u043c\u0438 Go, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0432 \u043a\u043e\u043d\u0446\u0435 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438. \u0418\u043c\u0435\u044f \u044d\u0442\u043e \u0432 \u0432\u0438\u0434\u0443, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0443\u043c\u0435\u043d\u044c\u0448\u0438\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0445 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0434\u043e \u043e\u0434\u043d\u043e\u0433\u043e. \u0422\u0430\u043a \u0436\u0435, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043c\u044b \u043f\u043b\u0430\u043d\u0438\u0440\u0443\u0435\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0447\u0435\u0440\u0435\u0437 \u0432\u044b\u0437\u043e\u0432 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043d\u0430\u043c \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e \u043d\u0443\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u2013\u00a0<code>transform()<\/code>\u00a0\u0438\u00a0<code>iterate()<\/code>\u00a0\u0443\u0436\u0435 \u0438\u043c\u0435\u044e\u0442 \u0432\u0441\u044e \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0443\u044e \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u044e \u0432\u043d\u0443\u0442\u0440\u0438, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0438\u0437\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043e\u0442 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u0438 \u0441\u0431\u043e\u0440\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432.<\/p>\n<p>\u0415\u0449\u0435 \u043e\u0434\u0438\u043d \u043c\u043e\u043c\u0435\u043d\u0442, \u043e \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u043f\u043e\u0434\u0443\u043c\u0430\u0442\u044c, \u044d\u0442\u043e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043e\u0448\u0438\u0431\u043e\u043a \u2013 \u0432\u044b\u0448\u0435 \u043c\u044b \u043d\u0435 \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u043b\u0438 \u043e\u0448\u0438\u0431\u043a\u0443 \u0441 \u0432\u0445\u043e\u0434\u043d\u044b\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u043c, \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0435\u0451 \u0432\u044b\u0437\u044b\u0432\u0430\u043b\u0430. \u041e\u0447\u0435\u0432\u0438\u0434\u043d\u043e, \u0447\u0442\u043e\u00a0<code>f()<\/code>\u00a0\u043c\u043e\u0436\u0435\u0442 \u043e\u0431\u0435\u0440\u043d\u0443\u0442\u044c \u043e\u0448\u0438\u0431\u043a\u0443 \u0437\u0430 \u043d\u0430\u0441, \u043d\u043e \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u0434\u043b\u044f\u00a0<code>f()<\/code>\u00a0\u0431\u044b\u043b\u043e \u0431\u044b \u043d\u0435 \u0438\u043c\u0435\u0442\u044c \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0439 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0435\u0451 \u0431\u0443\u0434\u0443\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c. \u0414\u0440\u0443\u0433\u0438\u043c\u0438 \u0441\u043b\u043e\u0432\u0430\u043c\u0438,\u00a0<code>f()<\/code>\u00a0\u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u0430 \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0442\u044c, \u0447\u0442\u043e \u0435\u0451 \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0442 \u043a\u0430\u043a \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u00a0<code>iterate()<\/code>. \u0415\u0441\u043b\u0438 \u043e\u043d\u0430 \u0431\u044b\u043b\u0430 \u0432\u044b\u0437\u0432\u0430\u043d\u0430 \u0441 \u043e\u0434\u043d\u0438\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u043c\u00a0<code>a<\/code>, \u0432\u043d\u0443\u0442\u0440\u0438\u00a0<code>f()<\/code>\u00a0\u043d\u0435\u0442 \u0441\u043c\u044b\u0441\u043b\u0430 \u043e\u0431\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u0442\u044c\u00a0<code>a<\/code>\u00a0\u0432 \u043e\u0448\u0438\u0431\u043a\u0443, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u0447\u0435\u0432\u0438\u0434\u043d\u043e, \u0447\u0442\u043e \u0438\u043c\u0435\u043d\u043d\u043e \u044d\u0442\u043e\u0442\u00a0<code>a<\/code>\u00a0\u0438 \u0432\u044b\u0437\u0432\u0430\u043b \u043e\u0448\u0438\u0431\u043a\u0435. \u042d\u0442\u043e\u0442 \u043f\u0440\u0438\u043d\u0446\u0438\u043f \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043d\u0430\u0441 \u043a \u0434\u0440\u0443\u0433\u043e\u043c\u0443 \u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044e \u2013 \u043b\u044e\u0431\u043e\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0435 \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u043d\u0438\u0435 \u0432\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u0441 \u043e\u0448\u0438\u0431\u043a\u043e\u0439 (\u0438\u043b\u0438 \u043b\u044e\u0431\u044b\u043c \u0434\u0440\u0443\u0433\u0438\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u043c) \u0442\u0430\u043a\u0436\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442\u044c \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f\u00a0<code>push()<\/code>. \u041f\u043e \u0442\u0435\u043c \u0436\u0435 \u043f\u0440\u0438\u0447\u0438\u043d\u0430\u043c, \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u00a0<code>push()<\/code>\u00a0\u0434\u043e\u043b\u0436\u043d\u0430 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044e \u0438 \u0440\u0435\u0448\u0430\u0442\u044c, \u0434\u043e\u043b\u0436\u043d\u0430 \u043b\u0438 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044f \u0431\u044b\u0442\u044c \u043f\u0440\u0435\u0440\u0432\u0430\u043d\u0430 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0448\u0438\u0431\u043a\u0438.<\/p>\n<p>\u0422\u0430\u043a\u043e\u0439 \u0434\u0438\u0437\u0430\u0439\u043d\u00a0<code>iterate()<\/code>\u00a0\u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043e\u0431\u0435\u0441\u043f\u0435\u0447\u0438\u0432\u0430\u0435\u0442 flow control \u2013 \u0435\u0441\u043b\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0434\u0435\u043b\u0430\u0435\u0442 \u0447\u0442\u043e-\u0442\u043e \u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e\u0435 \u0432\u043d\u0443\u0442\u0440\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u0438\u00a0<code>push()<\/code>, \u0442\u043e \u0434\u0440\u0443\u0433\u0438\u0435 \u0433\u043e\u0440\u0443\u0442\u0438\u043d\u044b \u0432 \u043a\u043e\u043d\u0435\u0447\u043d\u043e\u043c \u0438\u0442\u043e\u0433\u0435 \u043f\u0440\u0438\u043e\u0441\u0442\u0430\u043d\u043e\u0432\u044f\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u043d\u043e\u0432\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432. \u042d\u0442\u043e \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0434\u0435\u0442 \u043f\u043e\u0442\u043e\u043c\u0443, \u0447\u0442\u043e \u043e\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u044b \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u044f \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0441\u0432\u043e\u0435\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430\u00a0<code>f()<\/code>\u00a0\u0432 \u043a\u0430\u043d\u0430\u043b\u00a0<code>res<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0447\u0438\u0442\u0430\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u0432\u044b\u0437\u044b\u0432\u0430\u044e\u0449\u0430\u044f\u00a0<code>push()<\/code>.<\/p>\n<p>\u041f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043a\u043e\u0434 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u0441\u044f \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0431\u043e\u043b\u044c\u0448\u0438\u043c, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0435\u0433\u043e \u043f\u043e \u0447\u0430\u0441\u0442\u044f\u043c.<\/p>\n<h3>\u0418\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441<\/h3>\n<pre><code class=\"go\">func iterate[A, B any]( ctx context.Context, prepare func(context.Context) (context.Context, context.CancelFunc), parallelism int, pull func() (A, bool), f func(context.Context, A) B, push func(A, B) bool, ) (err error) {<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c\u044b\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u0438\u043c\u0435\u044e\u0442 \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0438 \u0432\u044b\u0445\u043e\u0434\u043d\u044b\u0445 \u0441\u043b\u0430\u0439\u0441\u043e\u0432, \u043a\u0430\u043a \u044d\u0442\u043e \u0431\u044b\u043b\u043e \u0434\u043b\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0438\u00a0<code>transform()<\/code>. \u0412\u043c\u0435\u0441\u0442\u043e \u044d\u0442\u043e\u0433\u043e \u0432\u0445\u043e\u0434\u043d\u044b\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0438\u0437\u0432\u043b\u0435\u043a\u0430\u044e\u0442\u0441\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0432\u044b\u0437\u043e\u0432\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u0438\u00a0<code>pull()<\/code>, \u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0442\u0441\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0432\u044b\u0437\u043e\u0432\u0430 \u0444\u0443\u043d\u043a\u0446\u0438\u0438\u00a0<code>push()<\/code>. \u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0447\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u00a0<code>push()<\/code>\u00a0\u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u00a0<code>bool<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u0443\u0435\u0442 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044e \u2013 \u043a\u0430\u043a \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442\u0441\u044f\u00a0<code>false<\/code>, \u043d\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430\u00a0<code>push()<\/code>\u00a0\u0431\u043e\u043b\u044c\u0448\u0435 \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0434\u0435\u0442, \u0430 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0432\u0441\u0435\u0445 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0449\u0438\u0445\u0441\u044f\u00a0<code>f()<\/code>\u00a0\u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u043c\u0435\u043d\u0435\u043d. \u0424\u0443\u043d\u043a\u0446\u0438\u044f\u00a0<code>iterate()<\/code>\u00a0\u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0448\u0438\u0431\u043a\u0443, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c\u00a0<code>non-nil<\/code>\u00a0\u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0438 \u043f\u0440\u0435\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u0438 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0438 \u0438\u0437-\u0437\u0430 \u043e\u0442\u043c\u0435\u043d\u044b\u00a0<code>ctx<\/code>\u00a0\u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0432 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u0445\u00a0<code>iterate()<\/code>. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0441\u043f\u043e\u0441\u043e\u0431\u0430 \u0443\u0437\u043d\u0430\u0442\u044c, \u043f\u043e\u0447\u0435\u043c\u0443 \u043f\u0440\u0435\u043a\u0440\u0430\u0442\u0438\u043b\u0430\u0441\u044c \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044f.<\/p>\n<blockquote>\n<p>\u041d\u0435 \u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u0435\u0441\u0442\u044c \u0432\u0441\u0435\u0433\u043e \u0442\u0440\u0438 \u0441\u043b\u0443\u0447\u0430\u044f, \u043a\u043e\u0433\u0434\u0430 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044f \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u043f\u0440\u0435\u0440\u0432\u0430\u043d\u0430:<\/p>\n<ul>\n<li>\n<p><code>pull()<\/code>\u00a0\u0432\u0435\u0440\u043d\u0443\u043b\u00a0<code>false<\/code>, \u0447\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435\u0442 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u0434\u043b\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438.<\/p>\n<\/li>\n<li>\n<p><code>push()<\/code>\u00a0\u0432\u0435\u0440\u043d\u0443\u043b\u00a0<code>false<\/code>, \u0447\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u0431\u043e\u043b\u044c\u0448\u0435 \u043d\u0435 \u043d\u0443\u0436\u043d\u044b \u043d\u0438\u043a\u0430\u043a\u0438\u0435 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b.<\/p>\n<\/li>\n<li>\n<p><code>ctx<\/code>\u00a0\u043f\u0435\u0440\u0435\u0434\u0430\u043d\u043d\u044b\u0439 \u0432 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u0430\u0445\u00a0<code>iterate()<\/code>\u00a0\u0431\u044b\u043b \u043e\u0442\u043c\u0435\u043d\u0451\u043d.<\/p>\n<\/li>\n<\/ul>\n<p>\u0411\u0435\u0437 \u0443\u0441\u043b\u043e\u0436\u043d\u0435\u043d\u0438\u0439 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0433\u043e \u043a\u043e\u0434\u0430 \u0442\u0440\u0443\u0434\u043d\u043e \u0441\u043a\u0430\u0437\u0430\u0442\u044c, \u0431\u044b\u043b\u0438 \u043b\u0438 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u044b \u0432\u0441\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0434\u043e \u043e\u0442\u043c\u0435\u043d\u044b\u00a0<code>ctx<\/code>.<\/p>\n<\/blockquote>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440<\/summary>\n<div class=\"spoiler__content\">\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u043c, \u0447\u0442\u043e \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043c\u043d\u043e\u0433\u043e\u0437\u0430\u0434\u0430\u0447\u043d\u044b\u0439 <code>forEach()<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 <code>iterate()<\/code>:<\/p>\n<pre><code class=\"go\">func forEach[A any]( ctx context.Context, in []A, f func(context.Context, A) error, ) (err error) { var i int iterate(ctx, nil, 0, func() (_ A, ok bool) { if i == len(in) { return } i++ return in[i-1], true }, f, func(_ A, e error) bool { err = e return e == nil }, ) if err == nil { \/\/ BUG: if we returned from `iterate()` call we either processed all \/\/ input _or_ ctx got cancelled and the iteration got interrupted. \/\/ \/\/ Simply checking if ctx.Err() is non-nil here is racy, and may \/\/ provide false faulty result in case when we processed all the input \/\/ and _then_ context got cancelled. \/\/ \/\/ On the other hand, checking here if i == len(in) as a condition of \/\/ completeness is incorrect, as we might pull the last element to \/\/ process and _then_ got interrupted by the context cancelation. \/\/ \/\/ So if iterate() doesn't return an error, one should track each \/\/ element processing state in `f()` call wrapper to correctly \/\/ distinguish cases above. err = ctx.Err() } return }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h3>\u041f\u0440\u043e\u043b\u043e\u0433<\/h3>\n<pre><code class=\"go\">\/\/ Create sub-context for the dispatch loop goroutine so we can stop it \/\/ once the user wants to stop the iteration. subctx, cancel := context.WithCancel(ctx) defer cancel()  \/\/ result represents input element A and the result B caused by applying \/\/ the given function f() to A. type result struct { a A b B } \/\/ loopInfo contains the dispatch loop state. \/\/ \/\/ The dispatch goroutine below signals current goroutine about the loop \/\/ termination by sending loopInfo to the term channel below. The current \/\/ goroutine uses it to understand how many elements have been dispatched \/\/ for processing to decide for how many results to await. type loopInfo struct { dispatched int err        error } \/\/ These channels are receive-only for the current goroutine and send-only \/\/ in the dispatch goroutine. For the sake of readability there is no type \/\/ constraints added. var ( res  = make(chan result) term = make(chan loopInfo, 1) )  \/\/ This wait group is used to track completion of worker goroutines started \/\/ by the dispatch goroutine. var wg sync.WaitGroup<\/code><\/pre>\n<p>\u0412 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0445 \u0432\u0435\u0440\u0441\u0438\u044f\u0445\u00a0<code>transform()<\/code>\u00a0\u043c\u044b \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u043b\u0438 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043f\u043e \u0438\u043d\u0434\u0435\u043a\u0441\u0443, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u043c\u0443 \u0432\u0445\u043e\u0434\u043d\u043e\u043c\u0443 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0443 \u0432 \u0441\u043b\u0430\u0439\u0441 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432, \u0447\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u043b\u043e \u043a\u0430\u043a\u00a0<code>bs[i] = f(as[i])<\/code>. \u0422\u0435\u043f\u0435\u0440\u044c, \u043f\u0440\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u043c \u0432\u0432\u043e\u0434\u0435 \u0438 \u0432\u044b\u0432\u043e\u0434\u0435 \u0442\u0430\u043a\u043e\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 \u043d\u0435\u0432\u043e\u0437\u043c\u043e\u0436\u0435\u043d. \u041f\u043e\u044d\u0442\u043e\u043c\u0443, \u043a\u0430\u043a \u0442\u043e\u043b\u044c\u043a\u043e \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438\u00a0<em>\u043b\u044e\u0431\u043e\u0433\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430<\/em>, \u043d\u0430\u043c, \u0441\u043a\u043e\u0440\u0435\u0435 \u0432\u0441\u0435\u0433\u043e, \u043d\u0443\u0436\u043d\u043e \u043d\u0435\u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e\u00a0<code>push()<\/code>. \u0412\u043e\u0442 \u043f\u043e\u0447\u0435\u043c\u0443 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0438\u043c\u0435\u0442\u044c \u0434\u0432\u0435 \u0433\u043e\u0440\u0443\u0442\u0438\u043d\u044b \u0434\u043b\u044f \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432 \u043c\u0435\u0436\u0434\u0443 \u0432\u043e\u0440\u043a\u0435\u0440\u0430\u043c\u0438 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u0438 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u2013 \u043f\u043e\u043a\u0430 \u043c\u044b \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0432\u0445\u043e\u0434\u043d\u044b\u0435 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b, \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0443\u0436\u0435 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0445 \u0440\u0430\u043d\u0435\u0435.<\/p>\n<h3>\u0413\u043e\u0440\u0443\u0442\u0438\u043d\u0430 \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432<\/h3>\n<pre><code class=\"go\">\/\/ Start the dispatch goroutine. Its purpose is to control the worker \/\/ goroutines, dispatch input elements among the workers, and eventually \/\/ signal the current goroutine about the dispatch loop termination. go func() { \/\/ wrk is a channel of input elements. It is send-only for the dispatch \/\/ gorouine and receive-only for the worker goroutines. wrk := make(chan A)  var loop loopInfo defer func() { \/\/ Signal the workers there are no more elements to dispatch. close(wrk) \/\/ Report the dispatch loop state to the parent goroutine. term &lt;- loop }()  var workersCount int \/\/ We use a _closed_ channel here to make the select below to always be \/\/ able to receive from it and start up to the given parallelism number \/\/ of goroutines. Once workersCount == parallelism, we set the variable \/\/ to nil so that the select cannot read from it after. \/\/ \/\/ This is needed to: \/\/ - Support the special case when parallelism is 0, so that there are \/\/   no limits on the number of workers. \/\/ - Awoid wasting time \"corking\" the semaphore channel while waiting \/\/   for all started goroutines to complete, especially if given a \/\/   large parallelism value. sem := make(chan struct{}) close(sem)<\/code><\/pre>\n<h3>\u0426\u0438\u043a\u043b \u0440\u0430\u0441\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u0438\u044f \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432<\/h3>\n<pre><code class=\"go\">for { if err := subctx.Err(); err != nil { loop.err = err return } a, ok := pull() if !ok { \/\/ No more input elements. return } if parallelism != 0 &amp;&amp; workersCount == parallelism { \/\/ Prevent starting more workers. sem = nil } select { case &lt;-subctx.Done(): loop.err = ctx.Err() return  case wrk &lt;- a: \/\/ There is an idle worker goroutine that is ready to process \/\/ the next element. loop.dispatched++ continue  case &lt;-sem: \/\/ Being able to _receive_ a tick from the channel means we can \/\/ start a new worker goroutine. loop.dispatched++ }  workersCount++ wg.Add(1)<\/code><\/pre>\n<h3>\u0413\u043e\u0440\u0443\u0442\u0438\u043d\u0430 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u0438<\/h3>\n<pre><code class=\"go\">go func(a A) { defer wg.Done()  \/\/ Capture the subctx from the topmost scope. This prevents \/\/ overriding it if the given prepare() function is not nil. subctx := subctx if prepare != nil { var cancel context.CancelFunc subctx, cancel = prepare(subctx) defer cancel() } for { r := result{a: a} r.b = f(subctx, a) select { case res &lt;- r: case &lt;-subctx.Done(): \/\/ If the context is cancelled, it means no more \/\/ results are expected. return } var ok bool a, ok = &lt;-wrk if !ok { break } } }(a)<\/code><\/pre>\n<h3>\u0421\u0431\u043e\u0440 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432<\/h3>\n<pre><code class=\"go\">} }()  collect: \/\/ Wait for the results sent by the worker goroutines. \/\/ \/\/ Note the initial -1 value for the num variable since the number of \/\/ elements pulled and dispatched is unknown yet. We weill be notified by \/\/ the dispatch gorouine once the input ends or the iteration is \/\/ terminated. for i, num := 0, -1; num == -1 || i &lt; num; { select { case &lt;-ctx.Done(): \/\/ We need to explicitly handle _parent_ context cancellation here \/\/ because it's an external interruption for us. We ignore the \/\/ dispatch loop termination event and stop to receive and push \/\/ results unconditionally. if err == nil { err = ctx.Err() } break collect  case res := &lt;-res: if !push(res.a, res.b) { \/\/ The user wants to stop the iteration. Signal the dispatch \/\/ loop about this. Note that in this case, we ignore the term \/\/ channel message and not return any error. cancel() break collect } i++  case loop := &lt;-term: \/\/ Dispatch loop has now terminated, and we now know the maximum \/\/ number of results we need receive in this loop. num = loop.dispatched err = loop.err } }  \/\/ NOTE: we unconditionally wait for all goroutines to complete in order to \/\/ return to a clean state. To avoid uninterruptable sleep here users are \/\/ required to respect context cancellation in the provided f(). wg.Wait()  return err }<\/code><\/pre>\n<p>\u0421\u0442\u043e\u0438\u0442 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044e\u0442\u0441\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u0432 \u0441\u043b\u0443\u0447\u0430\u0439\u043d\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u2013 \u043d\u0435 \u0432 \u0442\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043e\u043d\u0438 \u0431\u044b\u043b\u0438 \u0438\u0437\u0432\u043b\u0435\u0447\u0435\u043d\u044b. \u042d\u0442\u043e \u043e\u0436\u0438\u0434\u0430\u0435\u043c\u043e, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043c\u044b \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u0438\u0445 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e.<\/p>\n<blockquote>\n<p>\u0421\u0443\u0431\u044a\u0435\u043a\u0442\u0438\u0432\u043d\u043e\u0435 \u0437\u0430\u043c\u0435\u0447\u0430\u043d\u0438\u0435: \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u043d\u043e\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u00a0<code>sync.WaitGroup<\/code>\u00a0\u0438 \u043a\u0430\u043d\u0430\u043b\u0430\u00a0<code>sem<\/code>\u00a0\u0432 \u0434\u0430\u043d\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0440\u0435\u0434\u043a\u0438\u043c \u0438\u0441\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\u043c, \u043a\u043e\u0433\u0434\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043e\u0431\u043e\u0438\u0445 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u043e\u0432 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438 \u0432 \u043e\u0434\u043d\u043e\u043c \u043a\u043e\u0434\u0435 \u043e\u043f\u0440\u0430\u0432\u0434\u0430\u043d\u043e. \u042f \u0441\u0447\u0438\u0442\u0430\u044e, \u0447\u0442\u043e \u0432 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u0435 \u0441\u043b\u0443\u0447\u0430\u0435\u0432, \u0435\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u043a\u0430\u043d\u0430\u043b,\u00a0<code>sync.WaitGroup<\/code>\u00a0\u0431\u0443\u0434\u0435\u0442 \u0438\u0437\u043b\u0438\u0448\u043d\u0438\u043c, \u0438 \u043d\u0430\u043e\u0431\u043e\u0440\u043e\u0442.<\/p>\n<\/blockquote>\n<p>\u0424\u0443\u0445, \u0432\u043e\u0442 \u0438 \u0432\u0441\u0435! \u042d\u0442\u043e \u0431\u044b\u043b\u043e \u043d\u0435 \u043f\u0440\u043e\u0441\u0442\u043e, \u043d\u043e \u044d\u0442\u043e \u0442\u043e, \u0447\u0442\u043e \u043c\u044b \u0445\u043e\u0442\u0435\u043b\u0438 \u0441\u0434\u0435\u043b\u0430\u0442\u044c. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c, \u043a\u0430\u043a \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u043b\u043d\u044b\u0439 \u043b\u0438\u0441\u0442\u0438\u043d\u0433 \u043a\u043e\u0434\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">func iterate[A, B any]( ctx context.Context, prepare func(context.Context) (context.Context, context.CancelFunc), parallelism int, pull func() (A, bool), f func(context.Context, A) B, push func(A, B) bool, ) (err error) { \/\/ Create sub-context for the dispatch loop goroutine so we can stop it \/\/ once the user wants to stop the iteration. subctx, cancel := context.WithCancel(ctx) defer cancel()  \/\/ result represents input element A and the result B caused by applying \/\/ the given function f() to A. type result struct { a A b B } \/\/ loopInfo contains the dispatch loop state. \/\/ \/\/ The dispatch goroutine below signals current goroutine about the loop \/\/ termination by sending loopInfo to the term channel below. The current \/\/ goroutine uses it to understand how many elements have been dispatched \/\/ for processing to decide for how many results to await. type loopInfo struct { dispatched int err        error } \/\/ These channels are receive-only for the current goroutine and send-only \/\/ in the dispatch goroutine. For the sake of readability there is no type \/\/ constraints added. var ( res  = make(chan result) term = make(chan loopInfo, 1) )  \/\/ This wait group is used to track completion of worker goroutines started \/\/ by the dispatch goroutine. var wg sync.WaitGroup  \/\/ Start the dispatch goroutine. Its purpose is to control the worker \/\/ goroutines, dispatch input elements among the workers, and eventually \/\/ signal the current goroutine about the dispatch loop termination. go func() { \/\/ wrk is a channel of input elements. It is send-only for the dispatch \/\/ gorouine and receive-only for the worker goroutines. wrk := make(chan A)  var loop loopInfo defer func() { \/\/ Signal the workers there are no more elements to dispatch. close(wrk) \/\/ Report the dispatch loop state to the parent goroutine. term &lt;- loop }()  var workersCount int \/\/ We use a _closed_ channel here to make the select below to always be \/\/ able to receive from it and start up to the given parallelism number \/\/ of goroutines. Once workersCount == parallelism, we set the variable \/\/ to nil so that the select cannot read from it after. \/\/ \/\/ This is needed to: \/\/ - Support the special case when parallelism is 0, so that there are \/\/   no limits on the number of workers. \/\/ - Awoid wasting time \"corking\" the semaphore channel while waiting \/\/   for all started goroutines to complete, especially if given a \/\/   large parallelism value. sem := make(chan struct{}) close(sem)  for { if err := subctx.Err(); err != nil { loop.err = err return } a, ok := pull() if !ok { \/\/ No more input elements. return } if parallelism != 0 &amp;&amp; workersCount == parallelism { \/\/ Prevent starting more workers. sem = nil } select { case &lt;-subctx.Done(): loop.err = ctx.Err() return  case wrk &lt;- a: \/\/ There is an idle worker goroutine that is ready to process \/\/ the next element. loop.dispatched++ continue  case &lt;-sem: \/\/ Being able to _receive_ a tick from the channel means we can \/\/ start a new worker goroutine. loop.dispatched++ }  workersCount++ wg.Add(1)  go func(a A) { defer wg.Done()  \/\/ Capture the subctx from the topmost scope. This prevents \/\/ overriding it if the given prepare() function is not nil. subctx := subctx if prepare != nil { var cancel context.CancelFunc subctx, cancel = prepare(subctx) defer cancel() } for { r := result{a: a} r.b = f(subctx, a) select { case res &lt;- r: case &lt;-subctx.Done(): \/\/ If the context is cancelled, it means no more \/\/ results are expected. return } var ok bool a, ok = &lt;-wrk if !ok { break } } }(a) } }()  collect: \/\/ Wait for the results sent by the worker goroutines. \/\/ \/\/ Note the initial -1 value for the num variable since the number of \/\/ elements pulled and dispatched is unknown yet. We weill be notified by \/\/ the dispatch gorouine once the input ends or the iteration is \/\/ terminated. for i, num := 0, -1; num == -1 || i &lt; num; { select { case &lt;-ctx.Done(): \/\/ We need to explicitly handle _parent_ context cancellation here \/\/ because it's an external interruption for us. We ignore the \/\/ dispatch loop termination event and stop to receive and push \/\/ results unconditionally. if err == nil { err = ctx.Err() } break collect  case res := &lt;-res: if !push(res.a, res.b) { \/\/ The user wants to stop the iteration. Signal the dispatch \/\/ loop about this. Note that in this case, we ignore the term \/\/ channel message and not return any error. cancel() break collect } i++  case loop := &lt;-term: \/\/ Dispatch loop has now terminated, and we now know the maximum \/\/ number of results we need receive in this loop. num = loop.dispatched err = loop.err } }  \/\/ NOTE: we unconditionally wait for all goroutines to complete in order to \/\/ return to a clean state. To avoid uninterruptable sleep here users are \/\/ required to respect context cancellation in the provided f(). wg.Wait()  return err }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<h2>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u00a0iterate()\u00a0\u0434\u043b\u044f\u00a0transform()<\/h2>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c, \u043a\u0430\u043a \u043e\u0431\u043e\u0431\u0449\u0435\u043d\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0438 \u043c\u043e\u0436\u0435\u0442 \u0440\u0435\u0448\u0430\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443\u00a0<em>\u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f<\/em>, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c\u00a0<code>transform()<\/code>\u00a0\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f\u00a0<code>iterate()<\/code>. \u041e\u0447\u0435\u0432\u0438\u0434\u043d\u043e, \u0447\u0442\u043e \u0441\u0435\u0439\u0447\u0430\u0441 \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u043d\u0430\u043c\u043d\u043e\u0433\u043e \u043a\u043e\u043c\u043f\u0430\u043a\u0442\u043d\u0435\u0435, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043c\u044b \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u043b\u0438 \u0432\u0441\u044e \u043b\u043e\u0433\u0438\u043a\u0443 \u043c\u043d\u043e\u0433\u043e\u0437\u0430\u0434\u0430\u0447\u043d\u043e\u0441\u0442\u0438 \u0432\u00a0<code>iterate()<\/code>, \u0430 \u0432\u00a0<code>transform()<\/code>\u00a0\u0441\u043e\u0441\u0440\u0435\u0434\u043e\u0442\u043e\u0447\u0438\u043b\u0438\u0441\u044c \u043d\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0438 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f.<\/p>\n<pre><code class=\"go\">func transform[A, B any]( ctx context.Context, prepare func(context.Context) (context.Context, context.CancelFunc), parallelism int, as []A, f func(context.Context, A) (B, error), ) ( []B, error, ) { bs := make([]B, len(as)) var ( i    int err1 error ) err0 := iterate(ctx, prepare, parallelism, func() (int, bool) { i++ return i - 1, i &lt;= len(as) }, func(ctx context.Context, i int) (err error) { bs[i], err = f(ctx, as[i]) return }, func(i int, err error) bool { err1 = err return err == nil }, ) if err := errors.Join(err0, err1); err != nil { return nil, err } return bs, nil }<\/code><\/pre>\n<h2>\u041d\u043e\u0432\u044b\u0439\u00a0errgroup<\/h2>\n<p>\u0427\u0442\u043e\u0431\u044b \u0437\u0430\u043a\u0440\u044b\u0442\u044c \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u044e \u0441 \u043f\u0430\u043a\u0435\u0442\u043e\u043c\u00a0<code>errgroup<\/code>, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0435 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c\u00a0<code>iterate()<\/code>.<\/p>\n<pre><code class=\"go\">type taskFunc func(context.Context) error<\/code><\/pre>\n<pre><code class=\"go\">func errgroup(ctx context.Context) ( g func(taskFunc), wait func() error, ) { task := make(chan taskFunc) done := make(chan struct{})  var ( err     error failure error ) go func() { defer close(done)  \/\/ NOTE: we ignore the context preparation here as we don't need it. We \/\/ also don't limit amount of goroutines running at the same time -- we \/\/ want each task to start to be executed as soon as possible. err = iterate(ctx, nil, 0, func() (f taskFunc, ok bool) { f, ok = &lt;-task return }, func(ctx context.Context, f taskFunc) error { return f(ctx) }, func(_ taskFunc, err error) bool { if err != nil { \/\/ Cancel the group work and stop taking new tasks. failure = err return false } return true }, ) }()  g = func(fn taskFunc) { \/\/ If wait() wasn't called yet, but a previously scheduled task has \/\/ failed already, we should ignore the task and avoid deadlock here. select { case task &lt;- fn: case &lt;-done: } } wait = func() error { close(task) &lt;-done return errors.Join(err, failure) } return }<\/code><\/pre>\n<p>\u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0431\u0443\u0434\u0435\u0442 \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u0445\u043e\u0436\u0435 \u043d\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u00a0<code>errgroup<\/code>:<\/p>\n<pre><code class=\"go\">\/\/ Create the workers group and a context which will be canceled if any of the \/\/ tasks fails. g, wait := errgroup(ctx) g(func(ctx context.Context) error { return doSomeFun(gctx) }) g(func(ctx context.Context) error { return doEvenMoreFun(gctx) }) if err := wait(); err != nil { \/\/ handle error }<\/code><\/pre>\n<h2>\u0418\u0442\u0435\u0440\u0430\u0442\u043e\u0440\u044b \u0432 Go<\/h2>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043a\u0440\u0430\u0442\u043a\u043e \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0435\u0435 \u0431\u0443\u0434\u0443\u0449\u0435\u0435 Go, \u0438 \u0442\u043e, \u043a\u0430\u043a \u043e\u043d\u043e \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u0432\u043b\u0438\u044f\u0442\u044c \u043d\u0430 \u0438\u0434\u0435\u0438 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u043d\u044b\u0435 \u0432\u044b\u0448\u0435.<\/p>\n<p>\u0421 \u043d\u0435\u0434\u0430\u0432\u043d\u0438\u043c\u00a0<a href=\"https:\/\/go.dev\/wiki\/RangefuncExperiment\" rel=\"noopener noreferrer nofollow\">\u044d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442\u043e\u043c range over func<\/a>\u00a0(\u043d\u0430\u0447\u0438\u043d\u0430\u044f \u0441 Go 1.22) \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u0430 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u044f\u00a0<code>range<\/code>\u00a0\u043f\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c, \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u044b\u043c\u0438 \u0441 \u0442\u0438\u043f\u0430\u043c\u0438\u00a0<a href=\"https:\/\/github.com\/golang\/go\/blob\/20f052c83c380b8b3700b7aca93017178a692d78\/src\/iter\/iter.go#L19-L27\" rel=\"noopener noreferrer nofollow\">\u0438\u0442\u0435\u0440\u0430\u0442\u043e\u0440\u0430 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0435\u0439<\/a>, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u043c\u0438 \u0432 \u043f\u0430\u043a\u0435\u0442\u0435\u00a0<code>iter<\/code>. \u042d\u0442\u043e \u043d\u043e\u0432\u0430\u044f \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u044f Go, \u043a\u043e\u0442\u043e\u0440\u0430\u044f, \u043d\u0430\u0434\u0435\u044e\u0441\u044c, \u0441\u0442\u0430\u043d\u0435\u0442 \u0447\u0430\u0441\u0442\u044c\u044e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0432 \u0431\u0443\u0434\u0443\u0449\u0438\u0445 \u0432\u0435\u0440\u0441\u0438\u044f\u0445. \u0414\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438, \u043f\u043e\u0436\u0430\u043b\u0443\u0439\u0441\u0442\u0430, \u043f\u0440\u043e\u0447\u0442\u0438\u0442\u0435\u00a0<a href=\"https:\/\/github.com\/golang\/go\/discussions\/56413\" rel=\"noopener noreferrer nofollow\">range over func proposal<\/a>, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043f\u0440\u0435\u0434\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u044e\u0449\u0443\u044e \u0441\u0442\u0430\u0442\u044c\u044e \u043e\u00a0<a href=\"https:\/\/research.swtch.com\/coro\" rel=\"noopener noreferrer nofollow\">\u0441\u043e\u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0430\u0445 \u0432 Go<\/a>\u00a0\u043e\u0442 Russ Cox, \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d \u043f\u0430\u043a\u0435\u0442\u00a0<code>iter<\/code>.<\/p>\n<p>\u0421\u0434\u0435\u043b\u0430\u0442\u044c\u00a0<code>iterate()<\/code>\u00a0\u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u044b\u043c \u0441\u00a0<code>iter<\/code>\u00a0\u2013 \u043f\u0440\u043e\u0449\u0435 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e:<\/p>\n<pre><code class=\"go\">func iterate[A, B any]( ctx context.Context, prepare func(context.Context) (context.Context, context.CancelFunc), parallelism int, seq iter.Seq[A], f func(context.Context, A) B, ) iter.Seq2[A, B] { return func(yield func(A, B) bool) { pull, stop := iter.Pull(seq) defer stop() iterate(ctx, prepare, parallelism, pull, f, yield) } }<\/code><\/pre>\n<p>\u042d\u043a\u0441\u043f\u0435\u0440\u0438\u043c\u0435\u043d\u0442 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043d\u0430\u043c \u0434\u0435\u043b\u0430\u0442\u044c \u043f\u043e\u0442\u0440\u044f\u0441\u0430\u044e\u0449\u0438\u0435 \u0432\u0435\u0449\u0438 \u2013 \u0438\u0442\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u043e \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u043c \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c\u044b\u0445 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432\u00a0<em>\u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438<\/em>\u00a0\u0432 \u043e\u0431\u044b\u0447\u043d\u043e\u043c \u0446\u0438\u043a\u043b\u0435\u00a0<code>for<\/code>!<\/p>\n<pre><code class=\"go\">\/\/ Assuming the standard library supports iterators. seq := slices.Sequence([]int{1, 2, 3})  \/\/ Output: [1, 4, 9] for a, b := range iterate(ctx, nil, 0, seq, square) { fmt.Println(a, b) }<\/code><\/pre>\n<h2>\u0412\u044b\u0432\u043e\u0434\u044b<\/h2>\n<p>\u042f \u0445\u043e\u0442\u0435\u043b \u0431\u044b, \u0447\u0442\u043e\u0431\u044b \u044d\u0442\u043e \u0431\u044b\u043b\u043e \u0447\u0430\u0441\u0442\u044c\u044e \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 Go.<\/p>\n<p>\u0418\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u044f \u0434\u0443\u043c\u0430\u043b, \u0447\u0442\u043e \u0431\u044b\u043b\u043e \u0431\u044b \u043a\u0440\u0443\u0442\u043e \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u0440\u0430\u0437\u0434\u0435\u043b \u0441 \u043e\u0434\u043d\u0438\u043c \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c \u0432\u044b\u0448\u0435. \u041d\u043e, \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e, \u0432\u0441\u0435 \u0436\u0435 \u0441\u0442\u043e\u0438\u0442 \u0441\u043a\u0430\u0437\u0430\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043b\u043e\u0432 \u043f\u043e\u0447\u0435\u043c\u0443. \u042f \u0441\u0447\u0438\u0442\u0430\u044e, \u0447\u0442\u043e \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u043e\u0431\u0449\u0435\u0433\u043e \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u043b\u0443\u0447\u0448\u0435 \u0432\u043e\u0441\u043f\u0440\u0438\u043d\u044f\u0442\u044b \u0438 \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u044b \u0432 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\u0445, \u0435\u0441\u043b\u0438 \u0431\u043e\u043b\u044c\u0448\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u0441\u0442\u0432\u0430 \u0441\u043e\u0433\u043b\u0430\u0441\u043d\u0430 \u0441 \u0438\u0445 \u0434\u0438\u0437\u0430\u0439\u043d\u043e\u043c \u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439. \u041a\u043e\u043d\u0435\u0447\u043d\u043e, \u0443 \u043d\u0430\u0441 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438, \u0440\u0435\u0448\u0430\u044e\u0449\u0438\u0435 \u043f\u043e\u0445\u043e\u0436\u0438\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b, \u043d\u043e \u043d\u0430 \u043c\u043e\u0439 \u0432\u0437\u0433\u043b\u044f\u0434, \u0432 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0431\u043e\u043b\u044c\u0448\u043e\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u00a0<em>\u043c\u043e\u0436\u0435\u0442<\/em>\u00a0\u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442\u044c \u043a \u0431\u043e\u043b\u044c\u0448\u043e\u043c\u0443 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u0443 \u0440\u0430\u0437\u043d\u043e\u0433\u043b\u0430\u0441\u0438\u0439 \u043e \u0442\u043e\u043c, \u0447\u0442\u043e, \u043a\u043e\u0433\u0434\u0430 \u0438 \u043a\u0430\u043a \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c. \u0412 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u043d\u0435\u0442 \u043d\u0438\u0447\u0435\u0433\u043e \u043f\u043b\u043e\u0445\u043e\u0433\u043e \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u0438\u043c\u0435\u0442\u044c \u0441\u043e\u0432\u0435\u0440\u0448\u0435\u043d\u043d\u043e \u0440\u0430\u0437\u043d\u044b\u0435 \u043f\u043e\u0434\u0445\u043e\u0434\u044b \u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438, \u043d\u043e \u0432 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u044d\u0442\u043e \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u0435\u0442 \u043e\u0437\u043d\u0430\u0447\u0430\u0442\u044c \u0438 \u0432\u043e\u0432\u0441\u0435 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u043f\u043e\u043b\u043d\u043e\u0446\u0435\u043d\u043d\u043e\u0433\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u044f. \u041e\u0447\u0435\u043d\u044c \u0447\u0430\u0441\u0442\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u043f\u043e\u044f\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u043a\u0430\u043a \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0439 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b, \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u0431\u043e\u043b\u0435\u0435 \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0435, \u0447\u0435\u043c \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u043e\u0431\u0449\u0435\u0433\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f. \u0427\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u043e\u0431\u0449ee \u0440\u0435\u0448\u0435\u043d\u0438\u0435, \u0434\u0438\u0437\u0430\u0439\u043d \u0438 \u043a\u043e\u043d\u0446\u0435\u043f\u0446\u0438\u044e \u0431\u044b\u043b\u043e \u0431\u044b \u0437\u0434\u043e\u0440\u043e\u0432\u043e \u043e\u0431\u0441\u0443\u0436\u0434\u0430\u0442\u044c\u00a0<em>\u0437\u0430\u0434\u043e\u043b\u0433\u043e \u0434\u043e<\/em>\u00a0\u043d\u0430\u0447\u0430\u043b\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438. \u0422\u0430\u043a \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0432 open-source foundations \u0438\u043b\u0438, \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0441 Go \u2013 \u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432 \u044f\u0437\u044b\u043a\u0430. \u041d\u0430\u043b\u0438\u0447\u0438\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u0434\u043b\u044f \u043c\u043d\u043e\u0433\u043e\u0437\u0430\u0434\u0430\u0447\u043d\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u043a\u0430\u0436\u0435\u0442\u0441\u044f \u0435\u0441\u0442\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u043c \u0440\u0430\u0437\u0432\u0438\u0442\u0438\u0435\u043c Go \u043f\u043e\u0441\u043b\u0435 \u043f\u0430\u043a\u0435\u0442\u043e\u0432\u00a0<code>slices<\/code>,\u00a0<code>coro<\/code>\u00a0\u0438\u00a0<code>iter<\/code>.<\/p>\n<h2>\u0421\u0441\u044b\u043b\u043a\u0438<\/h2>\n<ul>\n<li>\n<p><a href=\"https:\/\/sergey.kamardin.org\/ru\/articles\/generic-concurrency-in-go\/\" rel=\"noopener noreferrer nofollow\">Generic Concurrency \u0432 Go (\u0420\u0443\u0441\u0441\u043a\u0438\u0439)<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/sergey.kamardin.org\/articles\/generic-concurrency-in-go\/\" rel=\"noopener noreferrer nofollow\">Generic Concurrency in Go (English)<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/docs.scala-lang.org\/overviews\/parallel-collections\/overview.html\" rel=\"noopener noreferrer nofollow\">Parallel Collections \u0432 Scala<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/go.dev\/blog\/when-generics\" rel=\"noopener noreferrer nofollow\">When To Use Generics<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/go.dev\/wiki\/RangefuncExperiment\" rel=\"noopener noreferrer nofollow\">Rangefunc Experiment<\/a><\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/817003\/\"> https:\/\/habr.com\/ru\/articles\/817003\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><\/figure>\n<p>\u041f\u0440\u0438\u0432\u0435\u0442, \u0433\u043e\u0444\u0435\u0440\u044b!<\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0445\u043e\u0447\u0443 \u043f\u043e\u0434\u0435\u043b\u0438\u0442\u044c\u0441\u044f \u043c\u044b\u0441\u043b\u044f\u043c\u0438 \u0438 \u0438\u0434\u0435\u044f\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0443 \u043c\u0435\u043d\u044f \u043d\u0430\u043a\u043e\u043f\u0438\u043b\u0438\u0441\u044c \u0437\u0430 \u0432\u0440\u0435\u043c\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0434\u0436\u0435\u043d\u0435\u0440\u0438\u043a\u0430\u043c\u0438 \u0432 Go, \u0438 \u0432 \u0447\u0430\u0441\u0442\u043d\u043e\u0441\u0442\u0438 \u043e \u0442\u043e\u043c, \u043a\u0430\u043a \u0448\u0430\u0431\u043b\u043e\u043d\u044b \u043c\u043d\u043e\u0433\u043e\u0437\u0430\u0434\u0430\u0447\u043d\u043e\u0441\u0442\u0438 \u043c\u043e\u0433\u0443\u0442 \u0441\u0442\u0430\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u0443\u0434\u043e\u0431\u043d\u044b\u043c\u0438 \u0438 \u043f\u0435\u0440\u0435\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u043c\u0438 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0434\u0436\u0435\u043d\u0435\u0440\u0438\u043a\u043e\u0432.<\/p>\n<h2>TL;DR<\/h2>\n<p>\u0414\u0436\u0435\u043d\u0435\u0440\u0438\u043a\u0438 \u0438 \u0433\u043e\u0440\u0443\u0442\u0438\u043d\u044b (\u0438 \u0438\u0442\u0435\u0440\u0430\u0442\u043e\u0440\u044b \u0432 \u0431\u0443\u0434\u0443\u0449\u0435\u043c) \u2013 \u044d\u0442\u043e \u043e\u0442\u043b\u0438\u0447\u043d\u044b\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0432 \u043e\u0431\u0449\u0435\u043c \u0432\u0438\u0434\u0435.<\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u0438\u0445 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u043d\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<h2>\u0412\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0438 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432, \u0447\u0442\u043e\u0431\u044b \u0443\u0432\u0438\u0434\u0435\u0442\u044c, \u043a\u0430\u043a\u0443\u044e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u0440\u0435\u0448\u0430\u044e\u0442 \u0434\u0436\u0435\u043d\u0435\u0440\u0438\u043a\u0438 \u0438 \u043a\u0430\u043a \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0432\u0441\u0442\u0440\u043e\u0438\u0442\u044c \u0438\u0445 \u0432 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0443\u044e \u043c\u043e\u0434\u0435\u043b\u044c \u043c\u043d\u043e\u0433\u043e\u0437\u0430\u0434\u0430\u0447\u043d\u043e\u0441\u0442\u0438 \u0432 Go.<\/p>\n<blockquote>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u044c \u043e\u0431 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0438 (<code>map()<\/code>) \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0439 \u0438\u043b\u0438 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0435\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432. \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c,\u00a0<em>\u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435<\/em>\u00a0\u2013 \u044d\u0442\u043e \u043f\u0440\u043e\u0446\u0435\u0441\u0441, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044e \u043d\u043e\u0432\u043e\u0439 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u0432, \u0433\u0434\u0435 \u043a\u0430\u0436\u0434\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u043c \u0432\u044b\u0437\u043e\u0432\u0430 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438\u00a0<code>f()<\/code>\u00a0\u0441 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u043e\u043c \u0438\u0437 \u0438\u0441\u0445\u043e\u0434\u043d\u043e\u0439 \u043a\u043e\u043b\u043b\u0435\u043a\u0446\u0438\u0438.<\/p>\n<\/blockquote>\n<h3>\u042d\u0440\u0430 Pre-Generics<\/h3>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043f\u0440\u043e\u0441\u0442\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0446\u0435\u043b\u043e\u0447\u0438\u0441\u043b\u0435\u043d\u043d\u044b\u0445 \u0447\u0438\u0441\u0435\u043b (\u043a\u043e\u0442\u043e\u0440\u0443\u044e \u0432 \u043a\u043e\u0434\u0435 Go \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u043d\u0430\u0437\u044b\u0432\u0430\u0442\u044c\u00a0<code>transform()<\/code>, \u0447\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0435\u0436\u0430\u0442\u044c \u043f\u0443\u0442\u0430\u043d\u043d\u0438\u0446\u044b \u0441\u043e \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u043c \u0442\u0438\u043f\u043e\u043c\u00a0<code>map<\/code>):<\/p>\n<pre><code class=\"go\">func transform([]int, func(int) int) []int<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u041f\u0440\u0438\u043c\u0435\u0440 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">func transform(xs []int, f func(int) int) []int {     ret := make([]int, len(xs))     for i, x := range xs {         ret[i] = f(x)     }     return ret }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0442\u0430\u043a\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<pre><code class=\"go\">\/\/ Output: [1, 4, 9] transform([]int{1, 2, 3}, func(n int) int {     return n * n })<\/code><\/pre>\n<p>\u041f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u043c, \u0447\u0442\u043e \u0442\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043e\u0442\u043e\u0431\u0440\u0430\u0437\u0438\u0442\u044c \u0446\u0435\u043b\u044b\u0435 \u0447\u0438\u0441\u043b\u0430 \u0432 \u0441\u0442\u0440\u043e\u043a\u0438. \u0427\u0442\u043e\u0431\u044b \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043c\u043e\u0436\u0435\u043c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c\u00a0<code>transform()<\/code>\u00a0<em>\u043d\u0435\u043c\u043d\u043e\u0433\u043e<\/em>\u00a0\u0438\u043d\u0430\u0447\u0435:<\/p>\n<pre><code class=\"go\">func transform([]int, func(int) string) []string<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"go\">\/\/ Output: [\"1\", \"2\", \"3\"] transform([]int{1, 2, 3}, strconv.Itoa) <\/code><\/pre>\n<p>\u041a\u0430\u043a \u043d\u0430\u0441\u0447\u0435\u0442 \u0434\u0440\u0443\u0433\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0443\u0434\u0435\u0442 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043f\u0440\u0438\u0437\u043d\u0430\u043a \u0447\u0435\u0442\u043d\u043e\u0441\u0442\u0438 \u0447\u0438\u0441\u043b\u0430? \u041f\u0440\u043e\u0441\u0442\u043e\u00a0<em>\u0435\u0449\u0435 \u043e\u0434\u043d\u0430 \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0430\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u043a\u0430<\/em>:<\/p>\n<pre><code class=\"go\">func transform([]int, func(int) bool) []bool<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<pre><code class=\"go\">\/\/ Output: [false, true, false] transform([]int{1, 2, 3}, func(n int) bool {     return n % 2 == 0 })<\/code><\/pre>\n<p><em>\u041e\u0431\u043e\u0431\u0449\u0430\u044f<\/em>\u00a0\u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0432 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\u00a0<code>transform()<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0432\u044b\u0448\u0435 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043a\u043e\u043d\u043a\u0440\u0435\u0442\u043d\u043e\u0433\u043e \u0441\u043b\u0443\u0447\u0430\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f, \u043c\u043e\u0436\u043d\u043e \u0441\u043a\u0430\u0437\u0430\u0442\u044c, \u0447\u0442\u043e\u00a0<strong>\u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e<\/strong>\u00a0\u043e\u0442 \u0442\u0438\u043f\u043e\u0432, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c\u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u043e\u043d\u0430 \u0434\u0435\u043b\u0430\u0435\u0442 \u0440\u043e\u0432\u043d\u043e \u0442\u043e \u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u0441\u043d\u043e\u0432\u0430 \u0438 \u0441\u043d\u043e\u0432\u0430. \u0415\u0441\u043b\u0438 \u0431\u044b \u043c\u044b \u0445\u043e\u0442\u0435\u043b\u0438 \u0441\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u0434 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0442\u0438\u043f\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0448\u0430\u0431\u043b\u043e\u043d\u044b\u00a0<code>text\/template<\/code>, \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u0442\u0430\u043a:<\/p>\n<pre><code class=\"go\">func transform_{{ .A }}_{{ .B }}([]{{ .A }}, func({{ .A }}) {{ .B }}) []{{ .B }}  \/\/ transform_int_int([]int, func(int) int) []int \/\/ transform_int_string([]int, func(int) string) []string \/\/ transform_int_bool([]int, func(int) bool) []bool<\/code><\/pre>\n<blockquote>\n<p>\u0420\u0430\u043d\u044c\u0448\u0435 \u043f\u043e\u0434\u043e\u0431\u043d\u044b\u0435 \u0448\u0430\u0431\u043b\u043e\u043d\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438\u0441\u044c \u0434\u043b\u044f \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u0438 \u201cgeneric\u201d \u043a\u043e\u0434\u0430. \u041f\u0440\u043e\u0435\u043a\u0442\u00a0<a href=\"https:\/\/github.com\/cheekybits\/genny\" rel=\"noopener noreferrer nofollow\">genny<\/a>\u00a0\u2013 \u043e\u0434\u0438\u043d \u0438\u0437 \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432.<\/p>\n<\/blockquote>\n<h3>\u042d\u0440\u0430 Generics<\/h3>\n<p>\u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u0434\u0436\u0435\u043d\u0435\u0440\u0438\u043a\u0430\u043c, \u0442\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c\u00a0<em>\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0438\u0437\u043e\u0432\u0430\u0442\u044c<\/em>\u00a0\u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0438 \u0442\u0438\u043f\u044b \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e\u00a0<em>\u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0442\u0438\u043f\u0430<\/em>\u00a0\u0438 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c\u00a0<code>transform()<\/code>\u00a0\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"go\">func transform[A, B any]([]A, func(A) B) []B<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u0418 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u0441\u044f \u0441\u043e\u0432\u0441\u0435\u043c \u0447\u0443\u0442\u044c-\u0447\u0443\u0442\u044c!<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"go\">func transform[A, B any](xs []A, f func(A) B) []B {     ret := make([]B, len(xs))     for i, x := range xs {         ret[i] = f(x)     }     return ret }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u00a0<code>transform()<\/code>\u00a0\u0434\u043b\u044f \u043b\u044e\u0431\u044b\u0445 \u0442\u0438\u043f\u043e\u0432 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0438 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u0432 (\u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e\u00a0<code>square(int) int<\/code>\u00a0\u0438\u00a0<code>isEven(int) bool<\/code>\u00a0\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u044b \u0433\u0434\u0435-\u0442\u043e \u0432\u044b\u0448\u0435 \u0432 \u043f\u0430\u043a\u0435\u0442\u0435):<\/p>\n<pre><code class=\"go\">transform([]int{1, 2, 3}, square)       \/\/ [1, 4, 9] transform([]int{1, 2, 3}, strconv.Itoa) \/\/ [\"1\", \"2\", \"3\"] transform([]int{1, 2, 3}, isEven)       \/\/ [false, true, false]<\/code><\/pre>\n<h2>\u041f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0442\u0435\u043c\u0435 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u0438 \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0448\u0430\u0431\u043b\u043e\u043d\u044b \u043c\u043d\u043e\u0433\u043e\u0437\u0430\u0434\u0430\u0447\u043d\u043e\u0441\u0442\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u043d\u043e \u0441 \u0434\u0436\u0435\u043d\u0435\u0440\u0438\u043a\u0430\u043c\u0438, \u0438 \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0442\u0430\u043d\u0443\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u043b\u0443\u0447\u0448\u0435 \u043e\u0442 \u0442\u0430\u043a\u043e\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f.<\/p>\n<h3>\u041f\u0430\u043a\u0435\u0442\u00a0x\/sync\/errgroup<\/h3>\n<p>\u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u043f\u043e\u0433\u0440\u0443\u0437\u0438\u0442\u044c\u0441\u044f \u0432 \u043a\u043e\u0434, \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043e\u0442\u0432\u043b\u0435\u0447\u0441\u044f \u0438 \u0432\u0437\u0433\u043b\u044f\u043d\u0443\u0442\u044c \u043d\u0430 \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u0443\u044e \u0432 Go \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443\u00a0<a href=\"https:\/\/pkg.go.dev\/golang.org\/x\/sync\/errgroup\" rel=\"noopener noreferrer nofollow\">golang.org\/x\/sync\/errgroup<\/a>. \u0412\u043a\u0440\u0430\u0442\u0446\u0435, \u043e\u043d\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0433\u043e\u0440\u0443\u0442\u0438\u043d\u044b \u0434\u043b\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0440\u0430\u0437\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447 \u0438 \u043e\u0436\u0438\u0434\u0430\u0442\u044c \u0438\u0445 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f \u0438\u043b\u0438 \u043e\u0448\u0438\u0431\u043a\u0438.<\/p>\n<p>\u041f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0435\u0442\u0441\u044f, \u0447\u0442\u043e \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"go\">\/\/ Create workers group and a context which will get canceled if any of the \/\/ tasks fails. g, gctx := errgroup.WithContext(ctx) g.Go(func() error { return doSomeFun(gctx) }) g.Go(func() error { return doEvenMoreFun(gctx) }) if err := g.Wait(); err != nil { \/\/ handle error }<\/code><\/pre>\n<p>\u041f\u0440\u0438\u0447\u0438\u043d\u0430, \u043f\u043e \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u044f \u0443\u043f\u043e\u043c\u044f\u043d\u0443\u043b\u00a0<code>errgroup<\/code>\u00a0\u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0435\u0442\u044c \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0441 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0434\u0440\u0443\u0433\u043e\u0439 \u0438 \u0431\u043e\u043b\u0435\u0435 \u043e\u0431\u0449\u0435\u0439 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f, \u0442\u043e \u043c\u044b \u0443\u0432\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u043f\u043e \u0441\u0443\u0442\u0438, \u043e\u043d\u0430 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0442\u0435\u043c \u0436\u0435 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u043e\u043c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f.\u00a0<code>errgroup<\/code>\u00a0<em>\u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442<\/em>\u00a0\u043d\u0430\u0431\u043e\u0440 \u0437\u0430\u0434\u0430\u0447 (\u0444\u0443\u043d\u043a\u0446\u0438\u0439) \u0441 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u043e\u043c \u0438\u0445 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f, \u0430 \u0442\u0430\u043a \u0436\u0435 \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u043e\u0431\u043e\u0431\u0449\u0435\u043d\u043d\u044b\u0439 \u0441\u043f\u043e\u0441\u043e\u0431 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438 \u201c\u0432\u0441\u043f\u043b\u044b\u0442\u0438\u044f\u201d \u043e\u0448\u0438\u0431\u043e\u043a c \u043f\u0440\u0435\u0440\u044b\u0432\u0430\u043d\u0438\u0435\u043c \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0449\u0438\u0445\u0441\u044f \u0437\u0430\u0434\u0430\u0447 (\u0447\u0435\u0440\u0435\u0437 \u043e\u0442\u043c\u0435\u043d\u0443\u00a0<code>context.Context<\/code>).<\/p>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0435, \u0438, \u043a\u0430\u043a \u043d\u0430\u043c\u0435\u043a\u0430\u0435\u0442 \u0447\u0430\u0441\u0442\u043e\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u0441\u043b\u043e\u0432 \u201c\u043e\u0431\u0449\u0438\u0439\u201d \u0438 \u201c\u043e\u0431\u043e\u0431\u0449\u0435\u043d\u043d\u044b\u0439\u201d, \u043c\u044b \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c\u0441\u044f \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u0432 \u043e\u0431\u0449\u0435\u043c \u0432\u0438\u0434\u0435.<\/p>\n<h3>\u041d\u0430\u0438\u0432\u043d\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f<\/h3>\n<p>\u0412\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u044f\u0441\u044c \u043a \u0444\u0443\u043d\u043a\u0446\u0438\u0438\u00a0<code>transform()<\/code>. \u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u0447\u0442\u043e \u0432\u0441\u0435 \u0432\u044b\u0437\u043e\u0432\u044b\u00a0<code>f()<\/code>\u00a0\u043c\u043e\u0433\u0443\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c\u0441\u044f \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u044c\u043d\u043e, \u043d\u0435 \u043b\u043e\u043c\u0430\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043d\u0430\u0448\u0435\u0439 \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b. \u0422\u043e\u0433\u0434\u0430 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043d\u0430\u0447\u0430\u0442\u044c \u0441 \u044d\u0442\u043e\u0439 \u043d\u0430\u0438\u0432\u043d\u043e\u0439\u00a0<em>\u043c\u043d\u043e\u0433\u043e\u0437\u0430\u0434\u0430\u0447\u043d\u043e\u0439<\/em>\u00a0\u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438:<\/p>\n<pre><code class=\"go\">func transform[A, B any](as []A, f func(A) B) []B { bs := make([]B, len(as))  var wg sync.WaitGroup for i := 0; i &lt; len(as); i++ { wg.Add(1) go func(i int) { defer wg.Done() bs[i] = f(as[i]) }(i) } wg.Wait()  return bs }<\/code><\/pre>\n<p>\u041c\u044b \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c \u0433\u043e\u0440\u0443\u0442\u0438\u043d\u0443 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0432\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u0430 \u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c\u00a0<code>f(elem)<\/code>. \u0417\u0430\u0442\u0435\u043c \u043c\u044b \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043f\u043e \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u043c\u0443 \u0438\u043d\u0434\u0435\u043a\u0441\u0443 \u0432 \u201c\u043e\u0431\u0449\u0435\u043c\u201d \u0441\u043b\u0430\u0439\u0441\u0435\u00a0<code>bs<\/code>. \u041d\u0438\u043a\u0430\u043a\u043e\u0433\u043e \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430, \u043f\u0440\u0435\u0440\u044b\u0432\u0430\u043d\u0438\u044f, \u0438\u043b\u0438 \u043e\u0448\u0438\u0431\u043e\u043a \u2013 \u0442\u0430\u043a\u0430\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043d\u0435 \u043a\u0430\u0436\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u0435\u0437\u043d\u043e\u0439 \u0432 \u0447\u0435\u043c-\u043b\u0438\u0431\u043e, \u043a\u0440\u043e\u043c\u0435 \u043f\u0440\u043e\u0441\u0442\u044b\u0445 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0439.<\/p>\n<h3>\u041e\u0442\u043c\u0435\u043d\u0430 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430<\/h3>\n<p>\u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u043c\u0438\u0440\u0435 \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c \u0442\u0440\u0435\u0431\u0443\u044e\u0449\u0438\u0445 \u043c\u043d\u043e\u0433\u043e\u0437\u0430\u0434\u0430\u0447\u043d\u043e\u0441\u0442\u0438, \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0442\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u043a\u043b\u044e\u0447\u0430\u044e\u0442 \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 i\/o, \u0431\u0443\u0434\u0443\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u043e\u043c\u00a0<code>context.Context<\/code>. \u0415\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442, \u0442\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0435\u0433\u043e \u0442\u0430\u0439\u043c-\u0430\u0443\u0442 \u0438\u043b\u0438 \u043e\u0442\u043c\u0435\u043d\u0430. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u044d\u0442\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code class=\"go\">func transform[A, B any]( ctx context.Context, as []A, f func(context.Context, A) (B, error), ) ( []B, error, ) { bs := make([]B, len(as)) es := make([]error, len(as))  subctx, cancel := context.WithCancel(ctx) defer cancel()  var wg sync.WaitGroup for i := 0; i &lt; len(as); i++ { wg.Add(1) go func(i int) { defer wg.Done() bs[i], es[i] = f(subctx, as[i]) if es[i] != nil { cancel() } }(i) } wg.Wait()  err := errors.Join(es...) if err != nil { return nil, err } return bs, nil }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u201c\u043e\u0431\u0449\u0438\u0439\u201d \u0441\u043b\u0430\u0439\u0441\u00a0<code>es<\/code>\u00a0\u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043f\u043e\u0442\u0435\u043d\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0445 \u043e\u0448\u0438\u0431\u043e\u043a\u00a0<code>f()<\/code>. \u0415\u0441\u043b\u0438 \u043a\u0430\u043a\u043e\u0439-\u043b\u0438\u0431\u043e \u0432\u044b\u0437\u043e\u0432\u00a0<code>f()<\/code>\u00a0\u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0435\u0442\u0441\u044f \u043e\u0448\u0438\u0431\u043a\u043e\u0439, \u043c\u044b \u043e\u0442\u043c\u0435\u043d\u044f\u0435\u043c \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442 \u0432\u0441\u0435\u0433\u043e \u0432\u044b\u0437\u043e\u0432\u0430\u00a0<code>transform()<\/code>\u00a0\u0438 \u043e\u0436\u0438\u0434\u0430\u0435\u043c, \u0447\u0442\u043e \u043a\u0430\u0436\u0434\u044b\u0439 \u0443\u0436\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0449\u0438\u0439\u0441\u044f \u0432\u044b\u0437\u043e\u0432\u00a0<code>f()<\/code>\u00a0\u0443\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u0442 \u043e\u0442\u043c\u0435\u043d\u0443 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430 \u0438, \u0435\u0441\u043b\u0438 \u043e\u043d\u0430 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442, \u043f\u0440\u0435\u0440\u044b\u0432\u0430\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u0435 \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0441\u043a\u043e\u0440\u0435\u0435.<\/p>\n<h3>\u041e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u043c\u043d\u043e\u0433\u043e\u0437\u0430\u0434\u0430\u0447\u043d\u043e\u0441\u0442\u0438<\/h3>\n<p>\u041d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435, \u043c\u044b \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043c\u043d\u043e\u0433\u043e \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u0430\u0433\u0430\u0442\u044c \u043e\u00a0<code>f()<\/code>. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438\u00a0<code>transform()<\/code>\u00a0\u043c\u043e\u0433\u0443\u0442 \u0437\u0430\u0445\u043e\u0442\u0435\u0442\u044c \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445 \u0432\u044b\u0437\u043e\u0432\u043e\u0432\u00a0<code>f()<\/code>. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440,\u00a0<code>f()<\/code>\u00a0\u043c\u043e\u0436\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c url \u043d\u0430 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 http \u0437\u0430\u043f\u0440\u043e\u0441\u0430. \u0411\u0435\u0437 \u043a\u0430\u043a\u0438\u0445-\u043b\u0438\u0431\u043e \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0439 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043f\u0435\u0440\u0435\u0433\u0440\u0443\u0437\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0435\u0440 \u0438\u043b\u0438 \u0437\u0430\u0431\u0430\u043d\u0438\u0442\u044c \u0441\u0430\u043c\u0438\u0445 \u0441\u0435\u0431\u044f.<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u043a\u0430 \u043d\u0435 \u0431\u0443\u0434\u0435\u043c \u0434\u0443\u043c\u0430\u0442\u044c \u043e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 \u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0438\u0437\u043c\u0430\u00a0<code>int<\/code>\u00a0\u0432 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442\u044b \u0444\u0443\u043d\u043a\u0446\u0438\u0438.<\/p>\n<p>\u041d\u0430 \u044d\u0442\u043e\u043c \u044d\u0442\u0430\u043f\u0435 \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0439\u0442\u0438 \u043e\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f\u00a0<code>sync.WaitGroup<\/code>\u00a0\u043a\u00a0<a href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%A1%D0%B5%D0%BC%D0%B0%D1%84%D0%BE%D1%80_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)\" rel=\"noopener noreferrer nofollow\">\u0441\u0435\u043c\u0430\u0444\u043e\u0440\u0443<\/a>\u00a0<code>chan<\/code>, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c (\u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435) \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0449\u0438\u0445\u0441\u044f \u0433\u043e\u0440\u0443\u0442\u0438\u043d, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u043e\u0442\u043c\u0435\u043d\u0443 \u043a\u043e\u043d\u0442\u0435\u043a\u0441\u0442\u0430, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f\u00a0<code>select<\/code>.<\/p>\n<pre><code class=\"go\">func transform[A, B any]( ctx context.Context, parallelism int, as []A, f func(context.Context, A) (B, error), ) ( []B, error, ) { bs := make([]B, len(as)) es := make([]error, len(as))  \/\/ FIXME: if the given context is already cancelled, no worker will be \/\/ started but the transform() call will return bs, nil. subctx, cancel := context.WithCancel(ctx) defer cancel()  sem := make(chan struct{}, parallelism) sched: for i := 0; i &lt; len(as); i++ { \/\/ We are checking the sub-context cancellation here, in addition to \/\/ the user-provided context, to handle cases where f() returns an \/\/ error, which leads to the termination of transform. if subctx.Err() != nil { break } select { case &lt;-subctx.Done(): break sched  case sem &lt;- struct{}{}: \/\/ Being able to send a tick into the channel means we can start a \/\/ new worker goroutine. This could be either due to the completion \/\/ of a previous goroutine or because the number of started worker \/\/ goroutines is less than the given parallism value. } go func(i int) { defer func() { \/\/ Signal that the element has been processed and the worker \/\/ goroutine has completed. &lt;-sem }() bs[i], es[i] = f(subctx, as[i]) if es[i] != nil { cancel() } }(i) } \/\/ Since each goroutine reads off one tick from the semaphore before exit, \/\/ filling the channel with artificial ticks makes us sure that all started \/\/ goroutines completed their execution. \/\/ \/\/ FIXME: for the high values of parallelism this loop becomes slow. for i := 0; i &lt; cap(sem); i++ { \/\/ NOTE: we do not check the user-provided context here because we want \/\/ to return from this function only when all the started worker \/\/ goroutines have completed. This is to avoid surprising users with \/\/ some of the f() function calls still running in the background after \/\/ transform() returns. \/\/ \/\/ This implies f() should respect context cancellation and return as \/\/ soon as its context gets cancelled. sem &lt;- struct{}{} }  err := errors.Join(es...) if err != nil { return nil, err } return bs, nil }<\/code><\/pre>\n<blockquote>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0439 \u0438 \u043f\u043e\u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0445 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0439\u00a0<code>tranform()<\/code>\u00a0\u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0432 \u0442\u043e\u043c \u0432\u0438\u0434\u0435, \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043e\u043d\u0430 \u0441\u0435\u0439\u0447\u0430\u0441 \u0435\u0441\u0442\u044c, \u0438 \u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0440\u0430\u0441\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0435\u043c\u044b\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u043d\u0430 \u043e\u0442\u043a\u0443\u043f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438\u00a0<code>f()<\/code>. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c\u00a0<code>N<\/code>\u00a0\u0433\u043e\u0440\u0443\u0442\u0438\u043d \u0432\u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0439 \u043c\u043d\u043e\u0433\u043e\u0437\u0430\u0434\u0430\u0447\u043d\u043e\u0441\u0442\u0438 \u0438 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e\u00a0<code>transform()<\/code>\u00a0\u0447\u0430\u0441\u0442\u0438\u0447\u043d\u043e \u0441\u0435\u0440\u0438\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u0438\u0445 \u0442\u0430\u043a, \u043a\u0430\u043a \u043e\u043d \u0445\u043e\u0447\u0435\u0442. \u042d\u0442\u043e \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u043e \u0431\u044b \u043d\u0430\u043a\u043b\u0430\u0434\u043d\u044b\u0445 \u0440\u0430\u0441\u0445\u043e\u0434\u043e\u0432 \u043d\u0430 \u0437\u0430\u043f\u0443\u0441\u043a\u00a0<code>N<\/code>\u00a0\u0433\u043e\u0440\u0443\u0442\u0438\u043d \u0432\u043c\u0435\u0441\u0442\u043e\u00a0<code>P<\/code>\u00a0(\u0433\u0434\u0435\u00a0<code>P<\/code>\u00a0\u2013 \u044d\u0442\u043e \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435 \u201c\u043f\u0430\u0440\u0430\u043b\u043b\u0435\u043b\u0438\u0437\u043c\u0430\u201d, \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u043c\u0435\u043d\u044c\u0448\u0435, \u0447\u0435\u043c\u00a0<code>N<\/code>). \u042d\u0442\u043e \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u0434\u0440\u0430\u0437\u0443\u043c\u0435\u0432\u0430\u043b\u043e \u0431\u044b \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435<\/p>\n<\/blockquote>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-377025","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/377025","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=377025"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/377025\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=377025"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=377025"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=377025"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}