این اولین پست از یک سری مقدماتی به اسم Go Fundamentals است. برنامه این است که اول مبانی زبان را مرور کنیم و بعد برویم سراغ یک سری دوم با تمرکز بر بک‌اند: سرویس‌های HTTP، دیتابیس و هم‌روندی در سیستم‌های واقعی. قبلاً در پستی درباره‌ی NATS JetStream از Go استفاده کرده بودم، ولی آن پست فرض می‌کرد که کد Go می‌خوانید. این سری از صفر شروع می‌شود.

امروز Go را نصب می‌کنیم، یک ماژول می‌سازیم و چند دستوری را یاد می‌گیریم که هر روز تایپ خواهید کرد. همه‌چیز با Go 1.26.0 روی macOS (معماری arm64) اجرا شده است.

چرا Go برای بک‌اند

Go زبانی کامپایلی با تایپ ایستا و مشخصات کوچک است. چند دلیل که مردم برای بک‌اند انتخابش می‌کنند:

  • go build یک فایل باینری واحد می‌سازد، پس استقرار و ساخت ایمیج Docker ساده می‌شود.
  • هم‌روندی (goroutine و channel) بخشی از خود زبان است.
  • کتاب‌خانه‌ی استاندارد HTTP، JSON، تست و بسیاری چیزهای دیگر را پوشش می‌دهد.
  • ابزارها (فرمت‌کننده، اجراکننده‌ی تست، مدیریت وابستگی) همراه زبان می‌آیند و تیم‌ها کمتر سر تنظیمات بحث می‌کنند.

نصب

راهنمای رسمی در go.dev/doc/install است. روی macOS اگر Homebrew دارید، brew install go هم یک گزینه است. بعد نصب را بررسی کنید:

go version
go version go1.26.0 darwin/arm64

اولین ماژول

کد Go داخل ماژول (module) قرار می‌گیرد. ماژول یعنی پوشه‌ای که فایل go.mod دارد. یکی بسازیم:

mkdir hello && cd hello
go mod init example.com/hello

مسیر ماژول (example.com/hello) همان نامی است که بقیه‌ی کدها برای import کردن شما استفاده می‌کنند. برای آزمایش محلی هر اسمی جواب می‌دهد؛ برای چیزی که منتشر می‌کنید معمولاً مسیر مخزن کد است. حالا main.go:

package main

import "fmt"

func main() {
	fmt.Println("Hello, Go!")
}

package main به‌همراه تابع main یعنی این یک برنامه‌ی اجرایی است. اجرا کنید:

go run .
Hello, Go!

go run برنامه را در یک مسیر موقت کامپایل و اجرا می‌کند. برای نگه‌داشتن باینری از go build استفاده کنید:

go build -o hello .
./hello
Hello, Go!

داخل go.mod چیست

module example.com/hello

go 1.25.0

مسیر ماژول و یک خط go با نسخه‌ی Go که ماژول انتظار دارد. وقتی وابستگی اضافه کنید، خط‌های require هم اینجا ظاهر می‌شوند. نکته‌ی جالب: ابزار من نسخه‌ی 1.26.0 است ولی go mod init عدد go 1.25.0 را نوشت؛ این فایل نسخه‌ی زبان را ثبت می‌کند و لزوماً نسخه‌ی دقیق ابزار نصب‌شده نیست.

یک برنامه‌ی کمی بزرگ‌تر

می‌خواهیم یک نام اختیاری را از خط فرمان بخوانیم و برایش مقدار پیش‌فرض بگذاریم:

package main

import (
	"fmt"
	"os"
)

// greeting builds the message for a given name.
func greeting(name string) string {
	return fmt.Sprintf("Hello, %s!", name)
}

func main() {
	name := "world"
	if len(os.Args) > 1 {
		name = os.Args[1]
	}
	fmt.Println(greeting(name))
}

os.Args[0] خود برنامه است، پس اولین آرگومان واقعی در ایندکس ۱ قرار دارد. متغیرها و if را در پست بعد درست‌وحسابی بررسی می‌کنیم.

go run .
go run . Roham
Hello, world!
Hello, Roham!

دستورهای روزمره

  • go fmt ./... فایل‌ها را به قالب استاندارد درمی‌آورد و نام فایل‌های تغییرکرده را چاپ می‌کند. در Go کسی سر سبک کدنویسی بحث نمی‌کند؛ فرمت‌کننده تصمیم می‌گیرد.
  • go vet ./... کدهای مشکوکی را گزارش می‌دهد که کامپایل می‌شوند. اگر مشکلی نباشد چیزی چاپ نمی‌کند.
  • go test ./... تست‌ها را اجرا می‌کند. تست تابعی به شکل TestXxx(t *testing.T) در فایلی با پسوند _test.go است:
package main

import "testing"

func TestGreeting(t *testing.T) {
	got := greeting("Roham")
	want := "Hello, Roham!"
	if got != want {
		t.Fatalf("got %q, want %q", got, want)
	}
}
$ go test ./...
ok  	example.com/greet	0.273s
  • go mod tidy وابستگی‌های جاافتاده را اضافه و بی‌استفاده‌ها را از go.mod حذف می‌کند. وقتی وابستگی نداریم، مثل اینجا، چیزی عوض نمی‌شود.

go fmt را هم با یک فایل بدفرمت امتحان کردم: نام bad.go را چاپ کرد و func x( ){ } را به func x() {} تبدیل کرد.

Go چیزها را کجا نگه می‌دارد

go env GOPATH GOMODCACHE
/Users/hossein/go
/Users/hossein/go/pkg/mod

GOMODCACHE جایی است که وابستگی‌های دانلودشده ذخیره می‌شوند و بین همه‌ی پروژه‌ها مشترک است. به‌طور پیش‌فرض زیر GOPATH قرار دارد. لازم نیست کد خودتان را آنجا بگذارید.

چند نکته

  • go run main.go فقط همان یک فایل را کامپایل می‌کند. وقتی بیش از یک فایل دارید go run . را بزنید تا کل پکیج ساخته شود.
  • دستورها را از داخل ماژول (کنار go.mod) اجرا کنید، وگرنه خطای نبودن go.mod می‌گیرید.
  • import یا متغیر محلیِ بی‌استفاده خطای کامپایل است، نه هشدار.
  • go build باینری را در پوشه‌ی جاری می‌نویسد؛ آن را commit نکنید.
  • موفق بودن go vet یعنی کد از چند اشتباه رایج دور است، نه اینکه درست کار می‌کند.

تمرین

۱. یک ماژول تازه بسازید و برنامه‌ی خوش‌آمدگویی را طوری تغییر دهید که دو آرگومان (نام و نام خانوادگی) بگیرد و Hello, First Last! چاپ کند. ۲. منطق نام پیش‌فرض world را در یک تابع جدا بگذارید و برایش تست دوم بنویسید. ۳. پیش از تمام‌شدن، go fmt، go vet و go test را اجرا کنید.

قدم بعدی

در پست بعد، go-types-control-flow، سراغ تایپ‌ها، متغیرها و کنترل جریان می‌رویم: بلوک‌های سازنده‌ای که برای نوشتن برنامه‌های واقعی لازم دارید.