הפרמטר ...T
שימו ... לפני הטיפוס של הפרמטר האחרון. הקוראים יכולים אז להעביר כל מספר של ארגומנטים מהטיפוס הזה, כולל אף אחד. בתוך הפונקציה הפרמטר הוא slice.
זה מדפיס 0, 5 ו-10. בלי ארגומנטים, nums הוא slice שהוא nil באורך 0, כך שהלולאה פשוט לא רצה.
פרמטרים רגילים יכולים לבוא קודם. רק האחרון יכול להיות variadic:
func logf(level string, format string, args ...any)
העברת slice עם s...
אם כבר יש לכם slice, אי אפשר להעביר אותו ישירות: sum(nums) נכשל עם cannot use nums (variable of type []int) as int value in argument to sum. פרסו אותו עם ... אחרי הארגומנט:
גם append היא variadic, ולכן חיבור של שני slices נכתב append(a, b...).
הפריסה חייבת להיות הדבר היחיד במקום ה-variadic. sum(1, nums...) נכשל עם too many arguments in call to sum. בנו קודם את ה-slice המשולב עם append.
ה-slice משותף, לא מועתק
כשקוראים עם ארגומנטים בודדים, Go יוצרת בשבילם slice חדש. כשפורסים slice קיים עם s..., הפונקציה מקבלת את אותו slice. כתיבה לאיברים שלו משנה את הנתונים של הקורא.
פלט:
[7 8 9]
[0 8 9]
פונקציה variadic שמשנה את הפרמטר שלה צריכה לתעד את זה, או להעתיק קודם עם slices.Clone. רוב הפונקציות ה-variadic רק קוראות את הארגומנטים שלהן, כך שזה נדיר שזה פוגע, אבל זה הבדל אמיתי משפות שבהן rest parameters הם תמיד מערך חדש.
...any ואיך fmt.Println עובדת
fmt.Println מוצהרת כ-func Println(a ...any) (n int, err error). any (כינוי ל-interface{} מאז Go 1.18) מקבל כל טיפוס, כך שאפשר להעביר מחרוזות, מספרים ו-structs בקריאה אחת.
העברה הלאה היא הפרט החשוב: logf מעבירה את args... ל-Printf. בלי הנקודות, Printf מקבלת ארגומנט אחד, []any, ושורת האזהרה מדפיסה [WARN] [3 8] of %!d(MISSING) workers idle. go vet מדווח missing ... in args forwarded to printf-like function רק כשפרמטר הפורמט מועבר הלאה בלי שינוי, כמו ב-fmt.Printf(format, args). כאן הפורמט נבנה עם +, כך ש-vet שותק והנקודות באחריותכם.
אי אפשר לפרוס []string לתוך ...any. טיפוסי האיברים חייבים להתאים בדיוק, אז המירו קודם:
names := []string{"a", "b"}
args := make([]any, len(names))
for i, n := range names {
args[i] = n
}
fmt.Println(args...)
דרישה של לפחות ארגומנט אחד
פרמטר variadic מקבל אפס ארגומנטים. כשהפונקציה צריכה לפחות אחד, הפכו את הראשון לפרמטר רגיל:
עכשיו maxOf() היא שגיאת הידור במקום בדיקה בזמן ריצה. הפונקציה המובנית max עצמה עובדת באותה צורה: היא דורשת לפחות ארגומנט אחד.
פרמטרי variadic כארגומנטים אופציונליים
ל-Go אין ערכי ברירת מחדל לפרמטרים. לפעמים משתמשים בפרמטר variadic כדי לזייף ארגומנט אופציונלי אחד:
func connect(addr string, timeout ...time.Duration)
זה עובד אבל נקרא גרוע, כי קוראים יכולים להעביר שלושה timeouts והחתימה לא אומרת איזה מהם מנצח. ליותר מאפשרות אחת, העדיפו struct של הגדרות, או את דפוס ה-functional options (...Option, שבו כל Option היא פונקציה שמציבה שדה). הדפוס הזה הוא variadic, אבל לכל ארגומנט יש משמעות ברורה.
תקציר הכללים
| כלל | דוגמה |
|---|---|
| רק הפרמטר האחרון יכול להיות variadic | func f(a string, b ...int) |
| בפנים, הפרמטר הוא slice | ל-b יש טיפוס []int |
| אפס ארגומנטים נותנים slice שהוא nil | f("x") גורם ל-b == nil |
פורסים slice עם ... | f("x", nums...) |
| פריסה משתפת את המערך שמתחת | כתיבה ל-b[i] משנה את nums |
| אין ערבוב של פריסה עם ערכים נוספים | f("x", 1, nums...) לא מתקמפל |
שאלות נפוצות
מה זו פונקציה variadic ב-Go?
פונקציה שהפרמטר האחרון שלה בצורה ...T, כמו func sum(nums ...int) int. אפשר לקרוא לה עם אפס ארגומנטים או יותר מטיפוס T, ובתוך הפונקציה הפרמטר הוא slice מסוג []T.
איך מעבירים slice לפונקציה variadic ב-Go?
הוסיפו ... אחרי ה-slice: sum(nums...). ה-slice מועבר כמו שהוא, בלי העתקה, כך שהפונקציה רואה את אותו מערך שמתחת. אי אפשר לערבב פריסה עם ארגומנטים בודדים: sum(1, nums...) לא מתקמפל.
האם יש ל-Go אופרטור spread?
רק לארגומנט האחרון של קריאה variadic: f(s...). אין spread כללי לבניית מערכים או structs, או לקריאה לפונקציות שאינן variadic עם slice. append(a, b...) הוא אותו כלל שמופעל על הפונקציה המובנית append.
האם לפונקציה ב-Go יכולים להיות שני פרמטרים variadic?
לא. רק הפרמטר האחרון יכול להיות variadic, ויכול להיות אחד כזה לכל פונקציה. העבירו רשימות נוספות כפרמטרים רגילים של slice.