Lifetimes در Rust چیست؟ با مثال کد توضیح داده شده است
طول عمر مکانیسم های اساسی در Rust هستند. در هر پروژه Rust که دارای هر نوع پیچیدگی است، احتمال بسیار بالایی وجود دارد که باید با مادام العمر کار کنید.
حتی اگر آنها برای پروژه های Rust مهم هستند، طول عمر می تواند بسیار دشوار باشد. پس من این راهنما را ایجاد کردم تا وضوح بیشتری در مورد اینکه آنها چه هستند و چه زمانی باید از آنها استفاده کنید ارائه دهم.
پیش نیازهای این آموزش
برای استفاده بیشتر از این آموزش، به موارد زیر نیاز دارید:
حداقل آشنایی سطح مبتدی با Rust: این آموزش به یادگیری نحوه کدنویسی در Rust کمکی نمی کند. این فقط به درک طول عمر در Rust و نحوه کار آنها کمک می کند
آشنایی با ژنریک ها: Generics in Rust دقیقاً مانند زبان های برنامه نویسی محبوب عمل می کنند. آگاهی از نحوه عملکرد ژنریک ها در هر زبانی مفید خواهد بود.
دانستن اینکه چککننده قرض چگونه کار میکند به اندازه دو مورد آخر بالا الزامی نیست، اما مفید خواهد بود. آگاهی از نحوه کار مادام العمر نیز به درک نحوه عملکرد جستجوگر قرض کمک می کند.
پس ، Lifetimes در Rust چیست؟
برای اینکه بررسی کننده قرض Rust امنیت را در سراسر کد شما تضمین کند، باید بداند که تمام داده های برنامه در طول اجرای آن چه مدت زنده می مانند. انجام این کار در موقعیتهای خاص دشوار میشود، و در آن موقعیتها باید از حاشیهنویسی صریح مادام العمر استفاده کنید.
طول عمر در Rust مکانیسم هایی برای اطمینان از معتبر بودن همه قرض هایی است که در کد شما رخ می دهد. طول عمر یک متغیر مدت زمانی است که در اجرای برنامه زندگی می کند، از زمانی که مقداردهی اولیه می شود و زمانی که در برنامه از بین می رود به پایان می رسد.
چک کننده قرض می تواند طول عمر متغیرها را در بسیاری از موارد تشخیص دهد. اما در مواردی که نمی تواند، شما باید با حاشیه نویسی صریح مادام العمر به آن کمک کنید.
نحو برای حاشیه نویسی های صریح مادام العمر یک نقل قول است که با مجموعه ای از کاراکترها برای شناسایی همراه می شود (به عنوان مثال، 'static
، 'a
) مانند:
max< 'a >
حاشیه نویسی طول عمر نشان می دهد که max
باید حداکثر تا زمانی که 'a
.
استفاده از چندین طول عمر از یک نحو پیروی می کند:
max< 'a , 'b >
در این مورد، حاشیه نویسی های طول عمر نشان می دهد که max
باید حداکثر تا زمانی که 'a
و 'b
باشد.
حاشیه نویسی های صریح مادام العمر به طور مشابهی به کار می روند. بیایید به یک مثال نگاه کنیم:
fn max < 'a >(s1: & 'a str , s2: & 'a str ) -> & 'a str { // return the longest string out of the two }
در مثال، حاشیهنویسیهای طول عمر نشان میدهند که max
باید حداکثر به اندازه طول عمر s1
یا s2
باشد. همچنین نشان می دهد که max
مرجعی را برمی گرداند که تا زمانی که s1
زندگی می کند.
پروژه Rust موارد زیادی دارد که به حاشیهنویسی صریح مادام العمر نیاز دارد، و در چند بخش بعدی، به هر یک از آنها خواهیم پرداخت.
حاشیه نویسی مادام العمر در توابع
یک تابع تنها زمانی به یک حاشیه نویسی صریح در طول عمر نیاز دارد که یک مرجع را از هر یک از آرگومان های خود برمی گرداند. بیایید مثالی بزنیم:
fn max < 'a >(s1: & 'a str , s2: & 'a str ) -> & 'a str { if s1.len() > s2.len() { s1 } else { s2 } }
اگر حاشیه نویسی های مادام العمر را حذف کنید، یک اخطار LSP (پروتکل سرور زبان) دریافت خواهید کرد که حاشیه نویسی مادام العمر را نیز شامل می شود. اگر پیام هشدار LSP را نادیده بگیرید و کد را کامپایل کنید، پیامی مشابه خطای کامپایلر دریافت خواهید کرد. به عنوان مثال:
fn max (s1: & str , s2: & str ) -> & str { if s1.len() > s2.len() { s1 } else { s2 } } /** * Output -> * error[E0106]: missing lifetime specifier --> src/main.rs:44:31 | 44 | fn max(s1: &str, s2: &str) -> &str { | ---- ---- ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `s1` or `s2` help: consider introducing a named lifetime parameter | 44 | fn max<'a>(s1: &'a str, s2: &'a str) -> &'a str { | ++++ ++ ++ ++ For more information about this error, try `rustc --explain E0106`. error: could not compile `lifetime-test` (bin "lifetime-test") due to 1 previous error *********************** */
از سوی دیگر، اگر یک تابع در آرگومان های خود مرجعی را برنگرداند، به طول عمر واضح نیاز ندارد. به عنوان مثال:
fn print_longest (s1: & str , s2: & str ) { if s1.len() > s2.len() { println! ( "{s1} is longer than {s2}" ) } else { println! ( "{s2} is longer than {s1}" ) } }
تابعی که مقدار متفاوتی را برمی گرداند نیز نیازی به حاشیه نویسی صریح در طول عمر ندارد:
fn join_strs (s1: & str , s2: & str ) -> String { let mut joint_string = String ::from(s1); joint_string.push_str(s2); return joint_string; }
فقط زمانی باید طول عمر را مشخص کنید که تابعی از یکی از آرگومان های خود که یک مرجع قرضی است، مرجعی را برمی گرداند.
حاشیه نویسی مادام العمر در سازه ها
هنگامی که هر یک از فیلدهای آنها مرجع باشد، ساختارها به حاشیه نویسی های مادام العمر صریح نیاز دارند. این به تحلیل کننده قرض اجازه می دهد تا اطمینان حاصل کند که منابع موجود در فیلدهای ساختار بیشتر از ساختار عمر می کنند. به عنوان مثال:
struct Strs < 'a , 'b > { x: & 'a str , y: & 'b str , }
بدون حاشیه نویسی مادام العمر، یک پیام خطای LSP و کامپایلر مشابه آنچه در بخش قبل بود دریافت خواهید کرد:
struct OtherStruct { x: & str , y: & str , } /** * Output -> ********************** error[E0106]: missing lifetime specifier --> src/main.rs:7:8 | 7 | x: &str, | ^ expected named lifetime parameter | help: consider introducing a named lifetime parameter | 6 ~ struct OtherStruct<'a> { 7 ~ x: &'a str, | error[E0106]: missing lifetime specifier --> src/main.rs:8:8 | 8 | y: &str, | ^ expected named lifetime parameter | help: consider introducing a named lifetime parameter | 6 ~ struct OtherStruct<'a> { 7 | x: &str, 8 ~ y: &'a str, | For more information about this error, try `rustc --explain E0106`. error: could not compile `lifetime-test` (bin "lifetime-test") due to 2 previous errors ********************** */
حاشیه نویسی مادام العمر در روش ها
حاشیه نویسی مادام العمر در مورد روش ها می تواند به عنوان حاشیه نویسی برای روش های مستقل، بلوک های impl
یا ویژگی ها انجام شود. بیایید به هر یک از آنها نگاه کنیم:
روش های مستقل:
حاشیه نویسی طول عمر در روش های مستقل با حاشیه نویسی طول عمر در توابع یکسان است:
impl Struct { fn max < 'a >( self : & Self , s1: & 'a str , s2: & 'a str ) -> & 'a str { if s1.len() > s2.len() { s1 } else { s2 } } }
impl
Blocks
در صورتی که ساختاری که با آن مرتبط است دارای حاشیه نویسی مادام العمر در تعریف خود باشد، نوشتن حاشیه نویسی مادام العمر صریح برای بلوک های impl
الزامی است. این نحو برای نوشتن بلوکهای impl
با حاشیهنویسی صریح در طول عمر است:
struct Struct < 'a > { } impl < 'a > Struct< 'a > { }
این به هر روشی که در بلوک impl
می نویسید اجازه می دهد تا یک مرجع را از Struct
برگرداند. به عنوان مثال:
struct Strs < 'a > { x: & 'a str , y: & 'a str , } impl < 'a > Strs< 'a > { fn max ( self : & Self ) -> & 'a str { if self .y.len() > self .x.len() { self .y } else { self .x } } }
صفات
حاشیه نویسی مادام العمر در صفات به روش هایی که صفت تعریف می کند بستگی دارد.
بیایید به یک مثال نگاه کنیم. یک روش در داخل یک تعریف صفت میتواند از حاشیهنویسی صریح مادام العمر به عنوان یک روش مستقل استفاده کند، و تعریف صفت نیازی به حاشیهنویسی صریح طول عمر ندارد. اینطوری:
trait Max { fn longest_str < 'a >(s1: & 'a str , s2: & 'a str ) -> & 'a str ; } impl < 'a > Max for Struct< 'a > { fn longest_str (s1: & 'a str , s2: & 'a str ) { if s1.len() > s2.len() { s1 } else { s2 } } }
اگر یک روش صفت به ارجاعاتی از ساختار مرتبط با آن نیاز داشته باشد، تعریف صفت به حاشیهنویسی صریح در طول عمر نیاز دارد. به عنوان مثال:
trait Max < 'a > { fn max ( self : & Self ) -> & 'a str ; }
struct Strs < 'a > { x: & 'a str , y: & 'a str , } trait Max < 'a > { fn max ( self : & Self ) -> & 'a str ; } impl < 'a > Max< 'a > for Strs< 'a > { fn max ( self : & Self ) -> & 'a str { if self .y.len() > self .x.len() { self .y } else { self .x } } }
حاشیه نویسی مادام العمر در Enums
مشابه ساختارها، اگر هر یک از فیلدهای آن ها مرجع باشد، enum ها به حاشیه نویسی مادام العمر صریح نیاز دارند. به عنوان مثال:
enum Either < 'a > { Str( String ), Ref(& 'a String ), }
عمر 'static
در بسیاری از پروژه های Rust، احتمالاً با متغیرهایی مواجه خواهید شد که در طول عمر 'static
هستند. در این بخش، مروری کوتاه بر این خواهیم داشت که عمر 'static
چیست، چگونه کار میکند و معمولاً در کجا استفاده میشود.
'static
یک نام مادام العمر در Rust است. این نشان می دهد که داده هایی که یک مرجع به آنها اشاره می کند از جایی که مقداردهی اولیه شده تا انتهای برنامه زندگی می کند. این مقدار کمی با متغیرهای استاتیکی که مستقیماً در فایل باینری برنامه ذخیره می شوند متفاوت است. با این حال، همه متغیرهای ایستا یک عمر 'static
دارند.
متغیرهایی با طول عمر 'static
می توانند در زمان اجرا ایجاد شوند. اما نمی توان آنها را رها کرد، بلکه آنها را مجبور کرد تا عمر کوتاه تری داشته باشند. به عنوان مثال:
// The lifetime annotation 'a is the shorter lifetime of the // two arguments s1 and s2 fn max < 'a >(s1: & 'a str , s2: & 'a str ) -> & 'a str { if s1.len() > s2.len() { s1 } else { s2 } } fn main () { let first = "First string" ; // Longer lifetime { let second = "Second string" ; // Shorter lifetime // In the max function, the lifetime of first is // coerced into the lifetime of second println! ( "The biggest of {} and {} is {}" , first, second, max(first, second)); }; }
لفظ رشته ای نمونه هایی از مقادیر با طول عمر 'static
هستند. آنها همچنین در فایل باینری برنامه ذخیره می شوند و می توانند در زمان اجرا ایجاد شوند.
Rust به شما این امکان را می دهد که با استفاده از این نحو، متغیرهای استاتیک را با کلمه کلیدی static
اعلام کنید:
static IDENTIFIER: & 'static str = "value" ;
متغیرهای استاتیک را می توان در هر محدوده ای از جمله دامنه جهانی اعلام کرد. این بدان معناست که می توانید از متغیرهای ثابت به عنوان متغیرهای سراسری استفاده کنید. به عنوان مثال:
static FIRST_NAME: & 'static str = "John" ; static LAST_NAME: & 'static str = "Doe" ; fn main () { println! ( "First name: {}" , FIRST_NAME); println! ( "Last name: {}" , LAST_NAME); }
متغیرهای استاتیک نیز می توانند تغییر پذیر یا تغییر ناپذیر باشند. اما کار با متغیرهای ثابت قابل تغییر فقط در بلوک های unsafe
مجاز است زیرا آنها ناامن هستند.
static mut FIRST_NAME: & 'static str = "John" ; static LAST_NAME: & 'static str = "Doe" ; fn main () { unsafe { println! ( "First name: {}" , FIRST_NAME); } println! ( "Last name: {}" , LAST_NAME); unsafe { FIRST_NAME = "Jane" ; println! ( "First name changed to: {}" , FIRST_NAME); } }
خلاصه
Lifetimes in Go به تحلیل کننده قرض کمک می کند تا اطمینان حاصل کند که همه مراجع قرض گرفته شده معتبر هستند. جستجوگر قرض میتواند طول عمر متغیرها را در بسیاری از موارد تشخیص دهد، اما در مواردی که نمیتواند، باید با حاشیهنویسی صریح در طول عمر به آن کمک کنید.
حاشیه نویسی صریح مادام العمر 'a
چیزهای 'b
هستند 'static
در بسیاری از پروژه های Rust مشاهده می کنید. شما فقط باید از آنها در ساختارها (ساختارها، enums، صفات و مفاهیم) که با ارجاعات سر و کار دارند و در توابع یا روش هایی که مراجع را دریافت و برمی گرداند استفاده کنید.
در این راهنما، با حاشیه نویسی های صریح طول عمر آشنا شدید و نمونه هایی از نحوه استفاده از آنها را مشاهده کردید. من به شما مقداری وضوح در مورد موضوع دادم و به شما کمک کردم که طول عمر را بهتر درک کنید.
با تشکر برای خواندن!
ارسال نظر