اگر از زبان‌ هایی مثل جاوا، پایتون یا سی‌شارپ به دنیای Go (Golang) اومده باشی، احتمالاً اولین چیزی که باعث تعجبت میشه اینه که: "صبر کن ببینم... پس try-catch کجاست؟!" حق داری تعجب کنی! زبان گو کلاً یک فلسفه متفاوت برای مدیریت خطاها (Error Handling) داره. توی گو، خطا ها اتفاقات عجیب و غریب و استثنایی (Exceptions) نیستند؛ بلکه دقیقاً مثل بقیه متغیر ها، یک مقدار (Value) عادی محسوب میشن. بیا با هم توی این مقاله ، پرونده مدیریت خطا در گو رو ببندیم و ببینیم چطور باید باهاشون رفیق بشیم!

فلسفه گو: خطاها دوست ما هستند!

توی خیلی از زبان ‌ها، وقتی خطایی رخ میده، برنامه اصطلاحاً یک Exception پرتاب میکنه (Throw) و اگر تو هوا نگیریش (Catch)، کل برنامه میترکه! اما طراحان گو معتقد بودن که خطاها بخش طبیعی از جریان برنامه هستند. به همین دلیل، توابع توی گو معمولاً دو تا خروجی برمیگردونن:

  1. نتیجه اصلی کار
  2. یک متغیر از نوع error

ساختار کلاسیک if err != nil

این همون کدیه که قراره توی گو هزاران بار بنویسی و ببینی. بهش میگن الگوی بررسی خطا:

package main
import (
	"fmt"
	"os"
)
func main() {
	// تلاش برای باز کردن یک فایل
	file, err := os.Open("secret_passwords.txt")
// بررسی اینکه آیا خطایی رخ داده یا نه
	if err != nil {
		fmt.Println("اوه اوه! فایل پیدا نشد. دلیل:", err)
		return
	}
fmt.Println("فایل با موفقیت باز شد!", file.Name())
}

چطور خودمون خطا بسازیم؟

گاهی اوقات توابع خودمون نیاز دارن خطا تولید کنن. برای این کار دو تا راه خیلی ساده داریم:

۱. استفاده از errors.New

وقتی فقط میخوای یک پیام خطای ساده و ثابت برگردونی:

import "errors"
func divide(a, b float64) (float64, error) {
	if b == 0 {
		return 0, errors.New("نمیتونی عدد رو تقسیم بر صفر کنی رفیق!")
	}
	return a / b, nil
}

۲. استفاده از fmt.Errorf

وقتی میخوای پیام خطات داینامیک باشه و متغیرها رو توش جا بدی (مثل Printf):

import "fmt"
func checkAge(age int) error {
	if age < 18 {
		return fmt.Errorf("شما %d سالته و هنوز به سن قانونی نرسیدی", age)
	}
	return nil
}

 پیچیدن خطاها (Error Wrapping)

از نسخه ۱.۱۳ به بعد، گو یک قابلیت خفن به اسم Error Wrapping معرفی کرد. قضیه چیه؟ فرض کن یک تابع، خطای دیتابیس میده. تابع بالایی اون خطا رو میگیره و میخواد بگه "خطا در ثبت ‌نام کاربر". ما دلمون نمیخواد خطای اصلی دیتابیس گم بشه! پس خطا ها رو  تو دل هم قرار میدیم. برای این کار از %w توی fmt.Errorf استفاده میکنیم:

func queryDatabase() error {
	return errors.New("connection timeout")
}
func getUser() error {
	err := queryDatabase()
	if err != nil {
		// خطای اصلی رو با %w می‌پیچیم (Wrap می‌کنیم)
		return fmt.Errorf("خطا در گرفتن اطلاعات کاربر: %w", err)
	}
	return nil
}

پس Panic و Recover چی میشن؟

شاید بپرسی: "یعنی گو هیچ راهی برای ترکوندن برنامه نداره؟" چرا داره! بهش میگن panic. وقتی برنامه به یک باگ غیرقابل حل (مثل دسترسی به ایندکسی از آرایه که وجود نداره) میرسه، Panic میکنه. اما نکته طلایی اینه: از panic و recover (که شبیه catch عمل میکنه) نباید برای مدیریت خطا های روزمره مثل "پیدا نشدن فایل" یا "قطعی اینترنت" استفاده کنی. Panic فقط برای مواقعیه که برنامه واقعاً نمیتونه و نباید به کارش ادامه بده.

چند تا توصیه دوستانه (Best Practices)

  • هیچ‌وقت خطاها رو سایلنت نکن! هرگز از _ برای نادیده گرفتن خطا استفاده نکن (مگر اینکه ۱۰۰٪ مطمئن باشی مهم نیست).
// ❌ کار خیلی بد
file, _ := os.Open("file.txt") 

✅ کار درست

file, err := os.Open("file.txt") if err != nil { ... }

جمع‌بندی

به طور خلاصه، زبان گو به جای اینکه خطاها رو مثل یه بمب ساعتی ببینه که هر لحظه ممکنه بترکه (Exception)، اون‌ها رو به عنوان یه بخش عادی و قابل انتظار از مسیر برنامه در نظر میگیره. شاید نوشتن مداوم if err != nil اولش کمی تکراری به نظر برسه، اما نتیجه‌اش داشتن یک کد فوق‌العاده خوانا، قابل پیش‌بینی و امنه! بیا مهم‌ترین چیزهایی که یاد گرفتیم رو تو چند تا نکته سریع مرور کنیم:

  • خطاها مقدار (Value) هستند: تو گو خبری از try-catch نیست؛ ما خطا ها رو مثل یه متغیر معمولی از توابع میگیریم و همونجا بررسیشون میکنیم.
  • ساخت و شخصی‌سازی: با errors.New خطا های ساده میسازیم و با fmt.Errorf خطا های داینامیک و متن تولید میکنیم.
  • Wrapping: یاد گرفتیم با %w خطاها رو تو دل هم بپیچیم تا کانتکست (Context) گم نشه و با errors.Is و errors.As به راحتی پیداشون کنیم.
  • پَنیک (Panic) ممنوع: فهمیدیم که panic و recover جایگزین try-catch نیستند و فقط باید برای فاجعه‌ های غیر قابل جبران برنامه استفاده بشن، نه خطا های روزمره.
  • قوانین طلایی (Best Practices): خطا ها رو هرگز سایلنت نمیکنیم (از _ استفاده نمیکنیم)، بهشون توضیح اضافه می‌کنیم و با استفاده از Early Return (برگشت سریع)، کدمون رو تمیز و بدون تورفتگی‌های اضافه نگه میداریم. مدیریت خطا تو گو بیشتر از اینکه یک سینتکس باشه، یک «سبک برنامه‌نویسی» است که مجبورت میکنه به تمام سناریو های شکست برنامه‌ات فکر کنی.