הטיפוסים הבסיסיים של Go הם מספרים, ערכים בוליאניים ומחרוזות. לכל משתנה יש בדיוק טיפוס אחד, שנקבע בזמן קומפילציה, ו-Go אף פעם לא ממירה בין טיפוסים בשבילכם. %T מדפיס את הטיפוס של כל ערך:
שימו לב ש-'G' מודפס כ-71 עם הטיפוס int32: תו בגרשיים בודדים הוא rune, כלומר מספר (נקודת הקוד ב-Unicode). rune הוא כינוי של int32 (אותו טיפוס בשם נוסף), ולכן %T מדווח int32. באותו אופן byte מדווח uint8.
מספרים שלמים
| טיפוס | גודל | טווח |
|---|---|---|
int8 | 8 סיביות | -128 עד 127 |
int16 | 16 סיביות | -32,768 עד 32,767 |
int32 | 32 סיביות | בערך -2.1 מיליארד עד 2.1 מיליארד |
int64 | 64 סיביות | בערך -9.2 קווינטיליון עד 9.2 קווינטיליון |
uint8 (byte) | 8 סיביות | 0 עד 255 |
uint16 | 16 סיביות | 0 עד 65,535 |
uint32 | 32 סיביות | 0 עד בערך 4.3 מיליארד |
uint64 | 64 סיביות | 0 עד בערך 18.4 קווינטיליון |
int, uint | 32 או 64 סיביות | גודל המילה של הפלטפורמה |
uintptr | גודל הפלטפורמה | מחזיק ערך של מצביע, לקוד ברמה נמוכה |
השתמשו ב-int כברירת מחדל. len() מחזירה int, אינדקסים של slices הם int, ומוני לולאות הם int. בכל פלטפורמה של 64 סיביות int הוא 64 סיביות. פנו לטיפוסים עם גודל מוגדר כשהגודל הוא חלק מחוזה: int32 בפורמט קובץ בינארי, uint8 לבתים גולמיים, int64 לחותמת זמן Unix בננו-שניות.
הימנעו מטיפוסים לא מסומנים (unsigned) עבור כמויות ש"לא יכולות להיות שליליות", כמו ספירות. חיסור מתחת לאפס מתגלגל למספר ענק במקום לייצר מספר שלילי שאפשר היה לתפוס.
גבולות ו-overflow
לחבילת math יש קבוע לכל גבול. בזמן ריצה, חשבון שלמים שחורג מהגבול מתגלגל חזרה בשקט:
בלי panic ובלי שגיאה: 127 + 1 הופך ל--128. רק ביטויים קבועים נבדקים בזמן קומפילציה (var b int8 = 128 לא מתקמפל). אם overflow אפשרי בנתונים שלכם, בדקו לפני הפעולה או השתמשו ב-math/big.
ליטרלים של מספרים שלמים
million := 1_000_000 // underscores for readability (Go 1.13+)
mask := 0xFF // hexadecimal
perm := 0o755 // octal (also the older form 0755)
flags := 0b1010 // binary
מספרים עשרוניים (floating-point)
| טיפוס | גודל | דיוק |
|---|---|---|
float32 | 32 סיביות | בערך 6 עד 9 ספרות משמעותיות |
float64 | 64 סיביות | בערך 15 עד 17 ספרות משמעותיות |
ליטרל עם נקודה עשרונית או מעריך (2.5, 1e6) הוא float64 כברירת מחדל, וחבילת math מקבלת ומחזירה float64. השתמשו בו אלא אם הזיכרון במערך גדול חשוב.
מספרים עשרוניים הם בינאריים, ולכן רוב השברים העשרוניים הם קירובים:
מכאן נובעים שני כללים. אף פעם אל תשוו מספרים עשרוניים עם == אחרי חישוב. ואף פעם אל תשמרו כסף במספר עשרוני: השתמשו במספר שלם של אגורות, או בספרייה עשרונית.
חילוק מספר עשרוני באפס נותן +Inf, -Inf או NaN במקום panic. חילוק מספר שלם באפס גורם ל-panic בזמן ריצה (runtime error: integer divide by zero), ומחלק שהוא אפס קבוע הוא שגיאת קומפילציה.
ערכים בוליאניים
bool מחזיק true או false, וערך האפס שלו הוא false. Go לא מתייחסת למספרים, למחרוזות ריקות או ל-nil כערכים בוליאניים. if count {} היא שגיאת קומפילציה; כתבו if count > 0 {}. האופרטורים הם &&, || ו-!, ו-&& ו-|| מבצעים הערכה מקוצרת.
מחרוזות
string הוא רצף בתים בלתי ניתן לשינוי, בדרך כלל טקסט UTF-8. len(s) סופר בתים, לא תווים:
é תופס שני בתים ב-UTF-8, ולכן len מדווח 6 עבור חמישה תווים. מחרוזות במירכאות כפולות מעבדות תווי escape כמו \n; מחרוזות גולמיות בגרש הפוך (backquote) לא מעבדות אותם, ויכולות להשתרע על כמה שורות. הדף על מחרוזות מכסה את חבילת strings, והדף על runes ו-bytes מסביר את UTF-8 לעומק.
byte ו-rune
| כינוי | זהה ל- | מחזיק |
|---|---|---|
byte | uint8 | בית אחד של נתונים גולמיים |
rune | int32 | נקודת קוד אחת של Unicode |
אלה כינויים, לא טיפוסים חדשים: byte ו-uint8 ניתנים להחלפה בלי המרה. השמות אומרים מה המשמעות של המספר.
מספרים מרוכבים
ל-Go יש טיפוסים מרוכבים מובנים, complex64 ו-complex128, עם real, imag וחבילת math/cmplx:
מעט תוכניות צריכות אותם, אבל עיבוד אותות וחלק מהקוד הנומרי כן.
טיפוסים מורכבים וטיפוסי הפניה
הטיפוסים הבסיסיים מתחברים לטיפוסים מורכבים. לכל אחד מהם יש דף משלו:
| טיפוס | דוגמה | ערך אפס |
|---|---|---|
| מערך | [3]int | שלושה אפסים |
| slice | []int | nil |
| map | map[string]int | nil |
| struct | struct{ X, Y int } | כל שדה מאופס |
| מצביע | *int | nil |
| פונקציה | func(int) int | nil |
| channel | chan int | nil |
| interface | error, any | nil |
any הוא כינוי של interface{} (החל מ-Go 1.18) ויכול להחזיק ערך מכל טיפוס.
טיפוסים בעלי שם
אפשר להגדיר טיפוס חדש מתוך טיפוס קיים. יש לו אותו ייצוג, אבל הוא טיפוס נפרד, ויכולות להיות לו מתודות:
type Celsius float64
type UserID int64
var t Celsius = 21.5
var f float64 = t // compile error: cannot use t (variable of float64 type Celsius) as float64 value in variable declaration
ההקפדה הזאת היא העיקר: אי אפשר להעביר בטעות UserID למקום שבו מצופה OrderID. המירו במפורש כשזו הכוונה: float64(t). ראו את הדף על המרת טיפוסים לגבי הכללים, ואת פונקציות strconv למעבר בין מספרים למחרוזות.
ערכי אפס
לכל טיפוס יש ערך אפס, שהוא מה שמשתנה מחזיק לפני שהשמתם בו משהו: 0 למספרים, false ל-bool, "" למחרוזות, ו-nil למצביעים, slices, maps, channels, פונקציות ו-interfaces. אין ב-Go משתנים לא מאותחלים.
שאלות נפוצות
מה הגודל של int ב-Go?
int ו-uint הם 64 סיביות בפלטפורמות של 64 סיביות (amd64, arm64) ו-32 סיביות בפלטפורמות של 32 סיביות. אם אתם צריכים גודל קבוע, עבור פורמט קובץ או פרוטוקול רשת, השתמשו ב-int32, int64, uint8 וכן הלאה. למונים, אינדקסים ואורכים רגילים השתמשו ב-int.
איך מדפיסים את הטיפוס של משתנה ב-Go?
השתמשו ב-verb %T: fmt.Printf("%T\n", x) מדפיס int, float64, []string, main.User וכן הלאה. בקוד, reflect.TypeOf(x) מחזירה את אותו מידע כערך שאפשר לבחון.
מה ההבדל בין byte ל-rune ב-Go?
byte הוא כינוי (alias) של uint8 ומחזיק בית אחד של נתונים גולמיים. rune הוא כינוי של int32 ומחזיק נקודת קוד אחת של Unicode. מחרוזת היא רצף של בתים; מעבר עליה עם for range מפענח אותה ל-runes.
האם להשתמש ב-float32 או ב-float64 ב-Go?
השתמשו ב-float64 אלא אם יש לכם סיבה שלא. זה טיפוס ברירת המחדל של ליטרלים עשרוניים, הטיפוס שחבילת math עובדת איתו, ויש לו דיוק של בערך 15 עד 17 ספרות משמעותיות לעומת בערך 6 עד 9 ב-float32. בחרו ב-float32 רק כדי לחסוך חצי מהזיכרון במערכים גדולים או כדי להתאים לפורמט חיצוני.
מה הערך המקסימלי של int ב-Go?
השתמשו בקבועים של חבילת math: math.MaxInt (9223372036854775807 בפלטפורמות של 64 סיביות), math.MinInt, math.MaxInt64, math.MaxUint32 וכן הלאה. חשבון שעובר את הגבול מתגלגל חזרה בשקט בזמן ריצה; הוא לא גורם ל-panic.