مدل مالکیت رست نگرش طراحی سیستم‌های هم‌زمان را از «کدام شیء داده را دارد؟» به «چه کسی مسئول چرخهٔ حیات و اشتراک‌گذاری داده است؟» منتقل می‌کند. همین تغییر نگاه انتخاب‌های معماری و ابزارهای همگام‌سازی را تعیین می‌کند.

قواعد ساده؛ پیامدهای مهم

دو قاعدهٔ بنیادین در مدل مالکیت رست، رفتار کل سیستم را شکل می‌دهند:

  • در هر لحظه یا چند مرجع غیرقابل‌تغییر (&T) وجود دارد یا یک مرجع قابل‌تغییر (&mut T) — هرگز هر دو هم‌زمان نیستند.
  • مراجع نباید طول‌عمر داده را پشت‌سر بگذارند.

همان تحلیل‌های borrow checker که خطاهای حافظه را حذف می‌کنند، به‌طور هم‌زمان از شرایط مسابقهٔ داده جلوگیری می‌کنند. به همین دلیل، ایمنی حافظه و ایمنی هم‌زمانی در رست از یک مکانیزم مشترک ناشی می‌شوند. منابع مرجع شامل مستندات رسمی رست و صفحهٔ ویکی‌پدیا درباره Rust هستند.

صفات کلیدی: Send و Sync

دو trait اصلی، Send و Sync، مرزهای بین‌ریسه‌ای را مشخص می‌کنند: اگر نوعی Send باشد، می‌توان آن را به رشتهٔ دیگر منتقل کرد؛ اگر Sync باشد، امکان ارجاع هم‌زمان از چند رشته وجود دارد. کامپایلر اغلب این صفات را استنتاج می‌کند و انتقال یا اشتراک‌گذاری انواع ناایمن را مسدود می‌کند — برای مثال Rc نه Send است و نه Sync.

نگاشت قواعد زمانِ کامپایل به پرمیت‌های زمانِ اجرا

قواعد استاتیک راهنمای انتخاب سازوکارهای زمانِ اجرا هستند:

  • &TRwLock::read() (چند خواننده)
  • &mut TRwLock::write() یا Mutex::lock() (نویسندهٔ منحصر به‌فرد)
  • اتمیک‌ها ⇄ عملیات سخت‌افزاری اتمیک (مثلاً CAS)
  • Arc<T> ⇄ مالکیت اشتراکی با شمارش مرجع اتمیک

انتخاب بین Mutex، RwLock، اتمیک‌ها یا wrapper‌هایی مبتنی بر Arc یک تصمیم معماری است و بازتاب‌دهندهٔ رفتار مالکیت در زمان اجرا محسوب می‌شود.

محدودیت‌های سخت‌افزاری که طراحی را شکل می‌دهند

معماران باید اثرات سطح سخت‌افزار را در نظر بگیرند:

  • بافرهای ذخیره (store buffers): نوشتن یک هسته ممکن است قبل از مشاهده‌شدن توسط هسته‌های دیگر در بافر بماند.
  • انسجام حافظهٔ نهان (مثل پروتکل MESI): انتشار یک نوشتن می‌تواند باعث بی‌اعتبارسازی یا به‌روزرسانی خطوط کش در هسته‌های دیگر شود که هزینه و تأخیر دارد.
  • ترتیب‌دهی حافظه: حفظ ترتیب نیازمند انتخاب سطح ordering اتمیک‌ها است (مثلاً Relaxed، Release/Acquire، SeqCst) — هر کدام هزینه و تأثیر متفاوتی دارند. برای بررسی بیشتر، مطلب ویکی‌پدیا دربارهٔ Memory ordering مفید است.

قفل/آزادسازی‌ها ماهیت جادویی ندارند؛ آن‌ها مبتنی بر اتمیک‌ها و قابلیت‌های سخت‌افزار هستند. SeqCst ترتیب قوی‌تری تضمین می‌کند اما هزینهٔ تأخیر و پهنای‌باند کش بیشتری تحمیل می‌کند؛ اتمیک‌های Relaxed ارزان‌ترند اما تنها در الگوهای معتبر قابل‌استفاده‌اند.

الگوهای معماری سازگار با مدل مالکیت

  • ایزوله‌سازی حالت با پیام‌رسانی (Actor / message-passing): وقتی مالکیت روشن و جابه‌جایی پیام‌ها ممکن است، اشتراک‌گذاری حالت کاهش می‌یابد و نیاز به قفل‌ها کمتر می‌شود؛ این الگو با مدل رست هم‌خوانی خوبی دارد.
  • مالکیت اشتراکی محدود با Arc + Mutex: در صورت نیاز به اشتراک‌گذاری، استفاده از Arc<Mutex<T>> یا Arc<RwLock<T>> و کوچک نگه‌داشتن حوزهٔ قفل توصیه می‌شود.
  • الگوهای lock-free و اتمیک: برای شمارنده‌ها یا فلگ‌های ساده، اتمیک‌ها مناسب‌اند؛ اما طراحی lock-free پیچیده و حساس به ترتیب حافظه و معماری است.
  • مرزبندی ماژول‌ها و FFI: در مرزهای FFI یا unsafe، قراردادهای مالکیت را مستند کنید. The Rustonomicon منبع مفیدی برای الگوهای ایمنی در unsafe است.

استراتژی مهاجرت و تصمیم‌های عملی برای تیم‌ها

راهنمای مرحله‌ای برای بازنویسی یا مهاجرت به رست:

  1. با نقاط مرزی شروع کنید: بخش‌هایی با مسئولیت روشن و محدود مثل صف‌ها یا کش‌ها نقطهٔ ورود مناسبی‌اند.
  2. آزمایش و معیارگیری را از آغاز در برنامه قرار دهید: رفتار قفل و اتمیک‌ها در بار واقعی مشخص می‌شود. ابزارهایی مثل perf و پروفایلرهای سطح هسته کمک می‌کنند.
  3. ابتدا قرارداد مالکیت را تعیین کنید و سپس پرمیت زمان اجرا را انتخاب کنید: اگر borrow checker تضمین لازم را ارائه می‌دهد، از قفل‌های زمان اجرا اجتناب کنید؛ در غیر این صورت، سبک مناسب قفل را انتخاب نمایید.
  4. قواعد تیمی برای استفاده از unsafe و FFI تعریف کنید و مرزها را شفاف ثبت کنید.

نمونه‌های متداول تصمیم‌گیری

  • شمارندهٔ سادهٔ جهانی → اتمیک (با SeqCst یا Relaxed بسته به نیاز ترتیب).
  • دسترسی‌های خواندنی فراوان و نوشتن اندک → RwLock می‌تواند مناسب باشد؛ هزینهٔ تبدیل خواننده به نویسنده و اثر روی کش را در نظر بگیرید.
  • اشترک‌گذاری گستردهٔ ساختار دادهٔ پیچیده → طراحی بر پایهٔ پیام‌ها یا shard کردن داده اغلب بهتر از نگه‌داشتن یک قفل بزرگ است.

هشدارها و محدودیت‌ها

  • تکیهٔ صرف بر نوع‌ها کافی نیست؛ هزینه‌های زمان اجرا و جزئیات سطح پایین را اندازه‌گیری کنید.
  • unsafe می‌تواند قواعد را دور بزند؛ هر استفاده نیازمند بررسی دقیق، بازبینی و تست‌های قوی است.
  • الگوریتم‌های lock-free پیچیده و پرخطرند؛ بدون تجربه و آزمون کافی وارد آن‌ها نشوید.

منابع برای مطالعهٔ بیشتر

مستندات رسمی و منابع مرجع:

نمودار نگاشت borrow checker به پرمیت‌های زمان اجرا در رست

تصمیم‌های معماری باید مالکیت و مرزها را از ابتدا روشن کنند؛ رست ابزارهایی فراهم می‌آورد که این تصمیم‌ها را صریح‌تر می‌سازند، اما هزینه‌های سخت‌افزاری و پیچیدگی‌های زمان اجرا همچنان تعیین‌کننده‌اند.

نمونهٔ قفل و حافظه نهان در پردازنده‌های چند هسته‌ای

جمع‌بندی برای معماران

معمارانی که مالکیت را به‌عنوان قرارداد اصلی سیستم تعریف و انتخاب‌های همگام‌سازی را بر پایهٔ هزینه‌های سخت‌افزاری و سناریوهای واقعی وزن کنند، بیشترین بهره را از رست خواهند برد. در پروژه‌های بزرگ این رویکرد به کدی منجر می‌شود که هم ایمن است و هم کارا؛ اما مستلزم فرهنگ اندازه‌گیری، مرزبندی دقیق و بازبینی مداوم است.