مهاجرت از Go به Rust: راهنمای عملی برای بکاند
خلاصهٔ کاملتر
این مقاله یه مقایسه عمیق و صادقانه بین Go و Rust برای تیمهای بکاند ارائه میده. نویسنده از ابتدا موضع خودش رو شفاف میکنه: Go را زبان موفقی میدونه ولی از طراحیش خوشش نمیاد، و به عنوان مشاور Rust طبیعتاً بایاس داره. با این حال تلاش میکنه یه راهنمای بیطرفانه بنویسه.
یکی از اولین نکات مفید، جدول معادلسازی ابزارهاست. مثلاً go test ./... معادل cargo test، و go vet معادل cargo clippy هست. تفاوت اینجاست که Rust ابزارهای بیشتری به صورت built-in داره و کمتر به ابزارهای third-party مثل golangci-lint نیاز داری.
مهمترین دلیلی که تیمهای Go به Rust نگاه میکنن، تضمینهای کامپایلر هست، نه سرعت. سه مشکل اصلی در این مقاله بررسی میشه: اول، panic ناشی از nil در production. در Go وقتی تابعی یه pointer برمیگردونه، بررسی nil بودنش یه قرارداد فرهنگیه نه اجبار کامپایلر. در Rust، Option جای nil رو میگیره و کامپایلر مجبورت میکنه هر دو حالت Some و None رو handle کنی.
fn handle(&self, req: &Request) -> Result<(), ServiceError> {
let user = self.repo.find(req.user_id)?; // None رو به error تبدیل میکنه
user.notify()
}دوم، data raceهاست. go test -race یه ابزار runtime هست و فقط race هایی رو پیدا میکنه که در تستها اجرا بشن. در Rust، اشتراکگذاری mutable state بین threadها بدون Arc اصلاً کامپایل نمیشه، یعنی data race تبدیل به یه compile error میشه.
سوم، مدیریت خطاست. الگوی if err != nil { return err } در Go خودش مشکل نیست، اما add کردن context به خطاها یه قرارداد انضباطیه نه اجبار. در Rust، اپراتور ? این کار رو خودکار انجام میده و با thiserror، enum های خطا exhaustive match دارن یعنی اگه variant جدیدی اضافه کنی، کامپایلر همه جاهایی که باید handle بشه رو نشونت میده.
#[derive(Debug, thiserror::Error)]
pub enum UserError {
#[error("user {0} not found")]
NotFound(UserId),
#[error("user already exists")]
AlreadyExists,
#[error(transparent)]
Repo(#[from] RepoError),
}در آخر، نویسنده تأکید میکنه که Go برای اکثر سرویسهای بکاند کاملاً کافیه و مهاجرت به Rust رو نباید سبک بگیری. Rust وقتی ارزش داره که latency پیشبینیپذیر، تضمینهای بیشتر، یا کنترل بیشتر روی رفتار runtime بخوای.
نکات کلیدی:
- Rust مشکلات Go (nil panic، data race، خطای فراموششده) رو در زمان کامپایل میگیره، نه runtime
- اپراتور ? در Rust معادل فشردهشده if err != nil { return err } هست
- Option جایگزین nil میشه و بررسی حالت خالی رو اجباری میکنه
- toolchain هر دو زبان خوبه، اما cargo ابزارهای بیشتری built-in داره
- برای اکثر سرویسهای معمولی Go همچنان انتخاب منطقیه؛ Rust وقتی ارزش داره که latency یا تضمینهای بیشتر نیاز داشته باشی




