תיעוד Go
מדריך עיון תמציתי ל-Go, מבוסס דוגמאות. קוראים את המושג, רואים את הקוד, ואז מתרגלים אותו במסלול של Coddy.
להתחיל מסלול Go מודרךצעדים ראשונים
- מה זה Go?Go (שנקראת לעיתים קרובות Golang) היא שפה מהודרת עם טיפוסים סטטיים שתוכננה ב-Google לבניית שרתים וכלים מהירים ואמינים. הדף מסביר מה היא, למה משתמשים בה ואיפה היא פחות מתאימה.
- התקנת Goאיך מורידים ומתקינים Go ב-Windows, ב-macOS וב-Linux, בודקים את ההתקנה עם go version, מבינים מה זה GOPATH ומתקנים את השגיאה "go: command not found".
- Hello Worldתוכנית hello world ב-Go שורה אחר שורה: package main, בלוק ה-import, func main ו-fmt.Println, וגם איך מריצים אותה ואילו שגיאות קומפילציה מתחילים פוגשים ראשונות.
- go run ו-go buildמה כל אחת מהפקודות go run, go build ו-go install עושה, איך Go קובעת את שם הקובץ הבינארי, איך מבצעים cross-compile עם GOOS ו-GOARCH, והבדיקות go fmt ו-go vet שכדאי להריץ לפני commit.
- מודולים ב-Goאיך מודולים ב-Go עובדים: יצירת מודול עם go mod init, בחירת נתיב מודול, הוספת תלויות עם go get, ניקוי עם go mod tidy, למה משמש go.sum, ו-replace לפיתוח מקומי.
- Packages ו-Importsאיך חבילות עובדות ב-Go: פסוקית ה-package, ייבוא חבילות מהספרייה הסטנדרטית ומהמודול, שמות מיוצאים מול לא מיוצאים, פיצול חבילה לכמה קבצים, חבילות internal, כינויים ל-import, blank imports והשגיאה "undefined" מ-go run main.go.
יסודות
- משתניםאיך מצהירים על משתנים ב-Go עם var ועם הצורה הקצרה :=, מה הם ערכי אפס, הצבה מרובה, ושגיאות ה-shadowing והמשתנה שלא בשימוש שתופסות מתכנתי Go חדשים.
- קבועים (const)איך const עובד ב-Go: הצהרה על קבועים, ההבדל בין קבועים עם טיפוס לקבועים בלי טיפוס, ביטויים קבועים בדיוק שרירותי, ולמה אין ב-Go קבועים מסוג slice, map או struct.
- Enums ו-iotaל-Go אין מילת מפתח enum. הדף הזה מראה את התחליף המקובל: טיפוס בעל שם יחד עם בלוק const ו-iota, ואיך מוסיפים String(), ולידציה, פענוח, דגלי ביטים ותמיכה ב-JSON.
- טיפוסי נתוניםהטיפוסים המובנים של Go: גדלים וטווחים של מספרים שלמים, float32 ו-float64, bool, string, byte ו-rune, מספרים מרוכבים, ערכי אפס, ואיך מדפיסים את הטיפוס של ערך עם %T.
- המרת טיפוסיםאיך ממירים בין טיפוסים ב-Go: int למחרוזת ומחרוזת ל-int עם strconv, מספרים עשרוניים למספרים שלמים, המרות מספריות עם T(v), bytes ו-runes, המלכודת של string(65), וטיפול בשגיאות המרה.
- מחרוזותעבודה עם מחרוזות ב-Go: החבילה strings (Split, Join, Contains, Replace, Fields, TrimSpace, ToUpper), בניית מחרוזות ביעילות עם strings.Builder, raw strings מרובות שורות, immutability, ולמה len סופרת בתים.
- Runes ו-Bytesמה המשמעות של rune ו-byte ב-Go, איך מחרוזות שומרות UTF-8, למה len סופרת בתים, איך range מפענחת runes, המרה בין string, []byte ו-[]rune, ועבודה עם החבילה bytes.
- fmt.Printf ו-Sprintfאיך חבילת fmt של Go מדפיסה ומעצבת ערכים: Println מול Printf מול Sprintf מול Errorf, הטבלה המלאה של format verbs (%v, %+v, %d, %s, %q, %f, %T, %w ועוד), רוחב, דיוק וריפוד.
בקרת זרימה
- if / elseאיך if, else if ו-else עובדים ב-Go: בלי סוגריים, עם סוגריים מסולסלים חובה, if עם פסוקית אתחול וה-scope שלה, הביטוי if err != nil, ו-return מוקדם במקום קינון עמוק.
- לולאת forל-Go יש מילת מפתח אחת ללולאות, for, והיא מכסה הכול: לולאת מונה עם שלושה חלקים, לולאת while עם תנאי בלבד, לולאה אינסופית, ו-range על slices, maps, מחרוזות, channels, מספרים שלמים ופונקציות. ובנוסף break ו-continue עם תוויות והשינוי במשתני הלולאה ב-Go 1.22.
- לולאת whileב-Go אין מילת מפתח while. לולאת for עם תנאי בלבד היא לולאת ה-while של Go, for בלי תנאי היא לולאה אינסופית, ו-do-while היא לולאה אינסופית שהבדיקה שלה נמצאת בסוף.
- לולאות rangeמה for range מחזירה עבור slices, מערכים, מחרוזות, maps, channels, מספרים שלמים ופונקציות iterator, למה משתנה הערך הוא עותק, מה קורה כשמשנים slice או map בזמן range, ו-range על פונקציות ב-Go 1.23.
- הפקודה switchאיך switch עובדת ב-Go: מקרים לא נופלים הלאה כברירת מחדל, מקרה אחד יכול לפרט כמה ערכים, switch בלי תנאי מחליפה שרשראות if/else, switch עם פקודת אתחול, מילת המפתח fallthrough, ומבט קצר על type switches.
- אופרטור טרנריל-Go אין אופרטור טרנרי (cond ? a : b). הדף מסביר למה, מראה את התחליף האידיומטי עם if/else, את cmp.Or לערכי ברירת מחדל, ופונקציית עזר גנרית יחד עם המלכודת שמבדילה אותה מטרנרי אמיתי.
פונקציות
- פונקציותאיך מצהירים על פונקציות וקוראים להן ב-Go: פרמטרים, פרמטרים שחולקים טיפוס, ערכים מוחזרים, העברה לפי ערך, ופונקציות כערכים שאפשר לשמור ולהעביר.
- החזרת כמה ערכיםפונקציות ב-Go יכולות להחזיר כמה ערכים. העמוד הזה מסביר את התחביר, את המוסכמה (value, error), את הביטוי comma-ok, תוצאות בעלות שם (named results), return חשוף והמזהה הריק.
- פונקציות variadicפונקציה variadic ב-Go מקבלת כל מספר של ארגומנטים אחרונים מטיפוס אחד. למדו את התחביר ...T, איך מעבירים slice עם s..., המלכודת של ה-aliasing, ואיך fmt.Println משתמשת ב-...any.
- Closuresפונקציות אנונימיות ב-Go יכולות ללכוד משתנים מההיקף שסביבן. כך נוצרים closures: מונים, גנרטורים, middleware ו-callbacks ששומרים על מצב משלהם.
- ההוראה deferdefer מתזמן קריאה שתרוץ כשהפונקציה העוטפת חוזרת. למדו על סדר LIFO, מתי הארגומנטים מוערכים, סגירת קבצים ושחרור mutexes, defer בתוך לולאות ושינוי תוצאות בעלות שם.
- מתודות (Methods)מתודה היא פונקציה עם receiver. למדו איך מצהירים על מתודות, מתי להשתמש ב-value receiver ומתי ב-pointer receiver, מה הם method sets ואיך הם קשורים ל-interfaces, ואיך מגדירים מתודות על טיפוסים שהם לא structs.
אוספים
- מערכים (Arrays)למערך ב-Go יש אורך קבוע שהוא חלק מהטיפוס שלו, והוא מועתק בכל השמה. למדו איך מצהירים על מערכים, עוברים עליהם, משווים ומעבירים אותם, ולמה רוב קוד ה-Go משתמש ב-slices במקומם.
- Slicesslices הם טיפוס הרשימה היומיומי של Go. למדו make, append וגדילה, len מול cap, איך חיתוך משתף את המערך שמתחת (והבאג שזה גורם), copy, החבילה slices, slices דו ממדיים, ו-nil מול ריק.
- Mapsmaps ב-Go שומרים זוגות של מפתח וערך עם חיפוש מהיר. למדו איך יוצרים אותם, בודקים אם מפתח קיים עם comma-ok, מוחקים, עוברים עליהם (בסדר אקראי), ממיינים מפתחות, שומרים structs, ונמנעים מה-panic של map שהוא nil ושל כתיבות מקביליות.
- Setsל-Go אין טיפוס set מובנה. הניב המקובל הוא map עם ערכים של struct ריק. למדו הוספה, בדיקת שייכות והסרה, איחוד, חיתוך והפרש, ואיך כותבים Set גנרי קטן.
- מיוןמיינו slices ב-Go עם slices.Sort ו-slices.SortFunc, מיינו structs לפי שדה אחד או כמה שדות עם cmp.Compare, שמרו על הסדר של איברים שווים עם מיון יציב, וקראו קוד ישן שמשתמש ב-sort.Slice.
מבנים, ממשקים וגנריקה
- Structsstructs מקבצים שדות עם שמות לטיפוס אחד. למדו איך מגדירים ומאתחלים אותם, ערכי אפס, מצביעים ל-structs, structs אנונימיים, השוואה, struct tags ומוסכמת הבנאי NewX.
- Pointerspointer מחזיק את הכתובת של ערך. למדו על & ו-*, על new, על pointers ל-structs, למה בטוח להחזיר pointer למשתנה מקומי, מתי להשתמש ב-pointers, ועל ה-panic של גישה דרך pointer שהוא nil.
- Interfacesב-Go מממשים interfaces באופן מרומז: כל טיפוס עם המתודות הנכונות מממש אותם. למדו על interfaces קטנים כמו io.Reader ו-fmt.Stringer, על ה-interface הריק any, על מלכודת ה-interface שהוא nil ועל הכלל לקבל interfaces ולהחזיר structs.
- הטמעת structsהטמעה (embedding) שמה טיפוס אחד בתוך אחר בלי שם שדה, כך שהשדות והמתודות שלו מקודמים לטיפוס החיצוני. למדו איך קידום עובד, הטמעת interfaces, התנגשויות שמות, ולמה הטמעה היא לא ירושה.
- Type Assertionstype assertion מוציא את הערך הקונקרטי מתוך interface. למדו את x.(T), את צורת ה-comma-ok שאף פעם לא גורמת ל-panic, assertion ל-interface אחר, type switches ו-errors.As לשגיאות עטופות.
- GenericsGenerics (Go 1.18 ומעלה) מאפשרים לפונקציה או לטיפוס אחד לעבוד עם הרבה טיפוסים ועדיין לשמור על בטיחות טיפוסים. למדו על פרמטרי טיפוס, על האילוצים any, comparable ו-cmp.Ordered, על אילוצים מותאמים עם ~, על טיפוסים גנריים ועל מתי לא להשתמש בהם.
שגיאות
- טיפול בשגיאותGo מטפלת בשגיאות כערכים רגילים שפונקציות מחזירות. למדו על הממשק error, על if err != nil, על errors.New ו-fmt.Errorf, על החזרת שגיאות עם הקשר, על בדיקתן עם errors.Is ו-errors.As, ועל טיפול בכל שגיאה פעם אחת.
- שגיאות מותאמות אישיתהגדירו שגיאות sentinel וטיפוסי שגיאה מותאמים, עטפו שגיאות עם %w, בדקו אותן עם errors.Is ו-errors.As, שלבו כמה שגיאות עם errors.Join, וכתבו מתודות Unwrap ו-Is כשצריך.
- panic ו-recoverpanic עוצר את הביצוע הרגיל ומגולל את המחסנית תוך הרצת הקריאות שנדחו עם defer. למדו מה גורם ל-panics, איך recover בפונקציה שנדחתה עוצר אותם, אילו הודעות שגיאה של זמן ריצה תראו, ומתי panic הוא הבחירה הנכונה.
מקביליות
- Goroutinesאיך מריצים פונקציות במקביל עם מילת המפתח go, מחכים שיסתיימו, מקבלים מהן תוצאות ונמנעים מ-data races, מדליפות ומקריסות שקל כל כך ליצור עם goroutines.
- Channelsאיך channels ב-Go מעבירים ערכים בין goroutines: channels עם buffer ובלי buffer, סגירה ומעבר עם range, טיפוסי כיוון, שגיאת ה-deadlock ו-pipeline שנבנה מהם.
- הפקודה selectאיך select ממתינה לכמה פעולות channel בבת אחת: בחירה בין מקרים מוכנים, שליחה וקבלה בלי חסימה עם default, timeouts עם time.After, ועצירת לולאות עם quit channel או context.
- WaitGroupאיך sync.WaitGroup ממתין שקבוצה של goroutines תסתיים: הכללים של Add, Done ו-Wait, למה חייבים להעביר אותו לפי מצביע, איסוף תוצאות ושגיאות, ו-worker pool שבנוי עליו.
- Mutexאיך מגנים על state משותף בין goroutines עם sync.Mutex ו-sync.RWMutex, מתי sync/atomic מספיק, איך sync.Once מריץ אתחול בדיוק פעם אחת, ואילו טעויות נעילה גורמות ל-deadlocks.
- חבילת contextאיך context.Context מעביר ביטול, deadlines וערכים ששייכים לבקשה לאורך תוכנית Go: Background, WithCancel, WithTimeout, WithValue, ctx.Done בתוך select, ו-context בשרתים ובלקוחות HTTP.
הספרייה הסטנדרטית
- החבילה timeאיך עובדים עם תאריכים ושעות ב-Go: time.Now ו-time.Sleep, חשבון עם Duration, פריסת הייחוס 2006-01-02 15:04:05 עבור Format ו-Parse, אזורי זמן, Unix timestamps, ו-timers ו-tickers.
- JSONאיך מקודדים ומפענחים JSON ב-Go עם encoding/json: Marshal ו-Unmarshal, struct tags כמו omitempty ו-omitzero, הדפסה מעוצבת, פענוח לתוך map[string]any, דחיית שדות לא מוכרים ו-streaming עם Decoder.
- קריאה וכתיבה של קבציםאיך קוראים וכותבים קבצים ב-Go: os.ReadFile ו-os.WriteFile, קריאה שורה אחרי שורה עם bufio.Scanner, הוספה לסוף קובץ עם os.OpenFile, בדיקה אם קובץ קיים ועבודה עם תיקיות.
- HTTP Serverאיך בונים שרת web עם החבילה הסטנדרטית net/http של Go: handlers, ניתוב עם ServeMux לפי מתודה ו-wildcards בנתיב (Go 1.22), תשובות JSON, קודי סטטוס, middleware, timeouts וכיבוי מסודר.
- HTTP Clientאיך שולחים בקשות HTTP ב-Go עם net/http: http.Get, קריאה וסגירה של ה-body, בדיקת קודי סטטוס, timeouts ב-http.Client, בקשות עם context ו-headers, פרמטרים של query ושליחת JSON ב-POST.
- ארגומנטים משורת הפקודהאיך תוכנית Go קוראת את שורת הפקודה שלה: os.Args, חבילת flag לאפשרויות עם טיפוסים, תת-פקודות עם FlagSet, משתני סביבה עם os.Getenv ו-os.LookupEnv, וקודי יציאה עם os.Exit.
- ביטויים רגולרייםאיך משתמשים בביטויים רגולריים ב-Go עם החבילה regexp: MustCompile, MatchString, FindString ו-FindAllString, קבוצות לכידה וקבוצות עם שם, ReplaceAllString והמגבלות של תחביר RE2, כמו היעדר lookbehind.
- Loggingאיך כותבים לוגים ב-Go: חבילת log הקלאסית עם ה-flags שלה ו-log.Fatal, ו-log/slog (Go 1.21) ללוגים מובנים עם רמות, מאפייני key-value, handlers של טקסט ו-JSON, ו-loggers שנושאים הקשר עם With.
בדיקות ומבנה פרויקט
- בדיקותאיך בודקים קוד Go עם החבילה הסטנדרטית testing ועם go test: קובצי _test.go, פונקציות TestXxx, t.Errorf מול t.Fatalf, בדיקות מבוססות טבלה עם t.Run, פונקציות עזר ותיקיות זמניות, כיסוי, benchmarks עם b.Loop ובדיקות example.
- מבנה פרויקטאיך בונים את המבנה של פרויקט Go: מתחילים שטוח, מפצלים לחבילות כשיש סיבה, משתמשים ב-cmd/ לכמה קובצי הרצה וב-internal/ לקוד שאף אחד אחר לא אמור לייבא, נותנים לחבילות שמות טובים ומשאירים את הבדיקות ליד הקוד.