اشاره‌گر (Pointer)های هوشمند (Smart Pointers)

اشاره‌گر (Pointer) یک مفهوم کلی برای یک متغیر است که شامل یک آدرس در حافظه می‌شود. این آدرس به برخی داده‌های دیگر ارجاع می‌دهد یا به‌اصطلاح “اشاره می‌کند”. رایج‌ترین نوع اشاره‌گر (Pointer) در Rust یک ارجاع است که در فصل ۴ با آن آشنا شدید. ارجاعات با نماد & مشخص می‌شوند و مقدار مورد اشاره را قرض می‌گیرند. آن‌ها قابلیت‌های خاص دیگری به‌جز ارجاع به داده ندارند و هیچ سرباری ندارند.

از سوی دیگر، اشاره‌گر (Pointer)های هوشمند ساختارهای داده‌ای هستند که مانند یک اشاره‌گر (Pointer) عمل می‌کنند، اما همچنین دارای فرا داده و قابلیت‌های اضافی هستند. مفهوم اشاره‌گر (Pointer)های هوشمند منحصراً به Rust اختصاص ندارد: اشاره‌گر (Pointer)های هوشمند در ابتدا در C++ معرفی شدند و در زبان‌های دیگر نیز وجود دارند. Rust مجموعه‌ای از اشاره‌گر (Pointer)های هوشمند در کتابخانه استاندارد خود دارد که عملکردی فراتر از آنچه که ارجاعات فراهم می‌کنند، ارائه می‌دهند. برای بررسی مفهوم کلی، به چند مثال مختلف از اشاره‌گر (Pointer)های هوشمند نگاهی خواهیم انداخت، از جمله نوع اشاره‌گر (Pointer) هوشمند شمارش ارجاعات. این اشاره‌گر (Pointer) به شما امکان می‌دهد تا داده‌ها مالکیت‌های متعددی داشته باشند، با ردیابی تعداد مالکان و پاک کردن داده هنگامی که هیچ مالکی باقی نماند.

Rust با مفهوم مالکیت و قرض گرفتن خود، تفاوت اضافی بین ارجاعات و اشاره‌گر (Pointer)های هوشمند دارد: در حالی که ارجاعات فقط داده‌ها را قرض می‌گیرند، در بسیاری از موارد اشاره‌گر (Pointer)های هوشمند مالک داده‌ای هستند که به آن اشاره می‌کنند.

اگرچه در آن زمان آن‌ها را به این صورت نام نبردیم، اما قبلاً با چند اشاره‌گر (Pointer) هوشمند در این کتاب آشنا شده‌ایم، از جمله String و Vec<T> در فصل ۸. هر دوی این نوع‌ها به‌عنوان اشاره‌گر (Pointer)های هوشمند در نظر گرفته می‌شوند زیرا آن‌ها مقداری حافظه را مالک می‌شوند و به شما امکان می‌دهند آن را دست‌کاری کنید. آن‌ها همچنین دارای فرا داده و قابلیت‌ها یا تضمین‌های اضافی هستند. برای مثال، String ظرفیت خود را به‌عنوان فرا داده ذخیره می‌کند و دارای قابلیت اضافی برای اطمینان از این است که داده‌های آن همیشه یک UTF-8 معتبر خواهد بود.

اشاره‌گر (Pointer)های هوشمند معمولاً با استفاده از ساختارها (structs) پیاده‌سازی می‌شوند. برخلاف یک ساختار عادی، اشاره‌گر (Pointer)های هوشمند ویژگی‌های Deref و Drop را پیاده‌سازی می‌کنند. ویژگی Deref به نمونه‌ای از ساختار اشاره‌گر (Pointer) هوشمند امکان می‌دهد که مانند یک ارجاع عمل کند، بنابراین می‌توانید کد خود را بنویسید تا با ارجاعات یا اشاره‌گر (Pointer)های هوشمند کار کند. ویژگی Drop به شما امکان می‌دهد کدی را که هنگام خارج شدن یک نمونه از اشاره‌گر (Pointer) هوشمند از محدوده اجرا می‌شود، سفارشی‌سازی کنید. در این فصل، هر دو ویژگی را بررسی خواهیم کرد و نشان خواهیم داد که چرا برای اشاره‌گر (Pointer)های هوشمند مهم هستند.

از آنجا که الگوی اشاره‌گر (Pointer) هوشمند یک الگوی طراحی کلی است که به‌طور مکرر در Rust استفاده می‌شود، این فصل تمام اشاره‌گر (Pointer)های هوشمند موجود را پوشش نمی‌دهد. بسیاری از کتابخانه‌ها اشاره‌گر (Pointer)های هوشمند خاص خود را دارند و حتی می‌توانید اشاره‌گر (Pointer) هوشمند خود را بنویسید. ما رایج‌ترین اشاره‌گر (Pointer)های هوشمند در کتابخانه استاندارد را پوشش خواهیم داد:

  • Box<T> برای تخصیص مقادیر در heap
  • Rc<T>، یک نوع شمارش ارجاعات که امکان مالکیت چندگانه را فراهم می‌کند
  • Ref<T> و RefMut<T>، که از طریق RefCell<T> قابل دسترسی هستند، نوعی که قوانین قرض گرفتن را در زمان اجرا به‌جای زمان کامپایل اعمال می‌کند

علاوه بر این، الگوی تغییرپذیری داخلی را پوشش خواهیم داد، جایی که یک نوع غیرقابل تغییر یک API برای تغییر یک مقدار داخلی ارائه می‌دهد. ما همچنین در مورد حلقه‌های ارجاع بحث خواهیم کرد: چگونه می‌توانند حافظه را نشت دهند و چگونه می‌توان از آن‌ها جلوگیری کرد.

بیایید شروع کنیم!