یادگیری در مورد این دو مفهوم به تقویت درک شما از نحوه عملکرد Rust و نحوه پیاده سازی ویژگی های OOP کمک می کند.
صفات و طول عمر اجزای اصلی Rust هستند. میتوانید از ویژگیها برای تعریف رفتارها و قابلیتهای انواع برای پیادهسازی استفاده کنید. آنها بسیار متنوع هستند و به شما امکان می دهند کدهای عمومی بیشتری بنویسید، تکرار را کاهش دهید و قابلیت نگهداری را بهبود بخشید.
Rust از مکانیسم دیگری – طول عمر – برای ردیابی مالکیت متغیرها در داخل و خارج از محدوده استفاده می کند. این مانع از آویزان شدن نشانگرها در حین جابجایی متغیر می شود.
ویژگیها و طول عمر با هم به اطمینان از ایمنی نوع، ایمنی حافظه و قابلیت اطمینان کد کمک میکنند.
درک صفات در زنگ
صفات مجموعه ای از روش ها هستند که انواع دیگر می توانند اجرا کنند. ویژگیها مشابه رابطها در زبانهایی مانند Java، Go و TypeScript هستند اما انعطافپذیرتر هستند.
شما از کلمه کلیدی trait برای تعریف صفات در Rust استفاده میکنید و به دنبال آن علامتهای متد را اعلام میکنید.
trait MyTrait {
fn my_method(&self);
}
کد یک صفت به نام MyTrait را با متد my_method تعریف می کند. پارامتر &self نشان می دهد که متد به شیء نوع پیاده سازی به عنوان اولین پارامتر آن اشاره می کند.
پس از تعریف یک صفت، می توانید آن را برای انواع سفارشی خود پیاده سازی کنید.
در اینجا نحوه اجرای یک ویژگی برای انواع ساختار خود آورده شده است.
struct Person {
name: String,
age: u32,
}
impl Info for Person {
fn summary(&self) {
println!("My name is {} and I am {} years old.", self.name, self.age);
}
}
ساختار Person Info را پیادهسازی میکند و میتوانید روش خلاصه را در نمونههایی از ساختار Person فراخوانی کنید.
fn main(){
let john = Person { name: String::from("John"), age: 30 };
john.summary(); // Output: My name is John, and I am 30 years old.
}
متغیر john نمونه ای از ساختار Person است.
تابع اصلی خلاصه فراخوانی می کند که پیامی را به کنسول چاپ می کند:
Enums می تواند صفات را پیاده سازی کند. در اینجا نحوه تعریف یک enum با انواعی که روش خلاصه را اجرا می کنند، آمده است:
enum MyEnum {
VariantA,
VariantB,
}
impl Info for MyEnum {
fn summary(&self) {
match self {
MyEnum::VariantA => {
// implementation for VariantA
}
MyEnum::VariantB => {
// implementation for VariantB
}
}
}
}
استفاده از Traits برای پارامترهای تابع و مقادیر بازگشتی
می توانید از ویژگی ها به عنوان پارامترهای تابع و مقادیر بازگشتی استفاده کنید. استفاده از صفات به عنوان پارامترهای تابع برای نوشتن کدهای عمومی با انواع مختلف مفید است.
در اینجا تابعی وجود دارد که هر نوع پارامتری را می گیرد که Info را پیاده سازی می کند.
fn do_something<T: Info>(value: T) {
value.summary();
}
نحو
طول عمر در Rust
ابزار بررسی قرض Rust برنامه ها را تجزیه و تحلیل می کند و استفاده مناسب از حافظه را تضمین می کند. در Rust، هر ارزش صاحبی دارد که مسئول انتقال ارزش است. وقتی متغیرها مقادیری را قرض می گیرند، ارجاع به مقدار ارسال شده را قرض می گیرند، اما مالک مالکیت را حفظ می کند.
طول عمر راهی برای اطمینان از استفاده صحیح از ارزش های قرض شده است. طول عمر برچسبی است که به یک مرجع چسبانده می شود و مدت اعتبار مرجع را توضیح می دهد.
در Rust، میتوانید با استفاده از حاشیهنویسی آپاستروف یک عمر را مشخص کنید:
func<'a>
هنگام ایجاد یک مرجع، یک مادام العمر به مرجع اختصاص داده می شود که مدت زمان اعتبار آن را توضیح می دهد. اگر تابعی دارید که ارجاع به یک مقدار را می گیرد، طول عمر باید بیشتر از فراخوانی تابع باشد تا اطمینان حاصل شود که هنگام بازگشت تابع، مقدار معتبر است.
در اینجا نمونه ای از مشخصات طول عمر در یک تابع آورده شده است.
fn do_something<'a>(x: &'a i32) -> &'a i32 {
x
}
fn main() {
let x = 42;
let result = do_something(&x);
println!("The result is: {}", result);
}
در تابع do_something، پارامتر ‘a lifetime نشان می دهد که ارجاع به x تا زمانی که فراخوانی تابع برقرار است معتبر است. مرجع بازگشتی نیز تا زمانی که فراخوانی تابع باشد معتبر است.
تابع main نتیجه را با ارسال یک مرجع به متغیر x در تابع اصلی به کنسول چاپ می کند.
نحو مادام العمر می تواند پرمخاطب باشد، اما برای مدیریت ایمنی و حافظه ضروری است. قوانین حذف سهعمر دستورالعملهایی ارائه میکنند که به Rust اجازه میدهد طول عمر مراجع را در موقعیتهای خاص استنباط کند.
قانون طول عمر ورودی
قانون طول عمر ورودی مشخص می کند که اگر یک تابع یا روش یک یا چند مرجع را به عنوان پارامترهای ورودی دریافت کند، Rust فرض می کند که تمام مراجع دارای طول عمر یکسان هستند.
به بیان ساده، طول عمر مراجع خروجی با منابع ورودی یکسان خواهد بود.
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
در طولانیترین تابع، Rust استنباط میکند که طول عمر مرجع خروجی با مرجع ورودی یکسان است زیرا هر دو پارامتر طول عمر یکسان ‘a دارند.
قانون طول عمر ورودی نوشتن توابع عمومی را که چندین مرجع را به عنوان ورودی می گیرند آسان می کند.
قانون طول عمر خروجی
قانون طول عمر خروجی مشخص می کند که اگر یک تابع یا روش یک مرجع را برگرداند، Rust فرض می کند که طول عمر مرجع خروجی با طول عمر هر مرجع ورودی متفاوت است.
fn first_word<'a>(s: &'a str) -> &'a str {
s.split_whitespace().next().unwrap()
}
در این تابع، Rust استنباط می کند که طول عمر مرجع خروجی با طول عمر مرجع ورودی متفاوت است، زیرا متد split_whitespace() یک مرجع خروجی ایجاد می کند که هیچ پارامتر مرجع ورودی را نمی گیرد.
قاعده Elision of Lifetimes
قانون حذف طول عمر زمانی اعمال می شود که یک تابع یا روش یک مرجع یا پارامتر ورودی را بگیرد و یک مرجع را برگرداند. در آن صورت، Rust فرض میکند که مرجع خروجی دارای طول عمر یکسانی با مرجع ورودی است.
fn longest<'a>(x: &'a str, y: &str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
در این تابع، Rust استنباط می کند که طول عمر مرجع خروجی با طول عمر مرجع ورودی یکسان است زیرا مرجع ورودی y پارامتر طول عمر ندارد. Rust پارامتر طول عمر را برای y حذف می کند و فرض می کند که طول عمر آن برابر با x است.
این قانون نوشتن توابعی را که یک مرجع ورودی و یک مرجع خروجی را برمیگردانند آسانتر میکند.
صفات و طول عمر
میتوانید صفات و طول عمر را برای ایجاد توابع عمومی که برای انواعی که یک صفت را پیادهسازی میکنند و طول عمر معتبری دارند، ترکیب کنید.
در اینجا یک صفت و یک تابع وجود دارد که به مقداری ارجاع می دهد که این صفت را پیاده سازی می کند.
trait ToString {
fn to_string(&self) -> String;
}
fn to_string<'a, T: ToString>(t: &'a T) -> String {
t.to_string()
}
در اینجا، پارامتر lifetime ‘a تضمین می کند که مرجع t برای طول عمر شی ای که به آن ارجاع می دهد معتبر است. میتوانید از تابع to_string با انواعی که ویژگی ToString را با طول عمر معتبر پیادهسازی میکنند، استفاده کنید.
صفات اساس پیاده سازی مفاهیم OOP در زنگ را تشکیل می دهند
ویژگی ها شما را قادر می سازد تا رفتارها را تعریف کنید. اگرچه Rust یک زبان برنامه نویسی شی گرا (OOP) نیست، اما می توانید از ویژگی ها برای پیاده سازی مفاهیم OOP از کپسولاسیون گرفته تا وراثت، چندشکلی و انتزاع استفاده کنید.
پیاده سازی این مفاهیم OOP با ویژگی ها، برنامه های Rust شما را مقیاس پذیر، قوی، قابل نگهداری و کارآمد می کند.