مرحبا سكان! سيساعدك دليل لغة البرمجة الرسمي Rust على إنشاء برامج أسرع وأكثر موثوقية. غالبًا ما تتعارض بيئة العمل عالية المستوى وعناصر التحكم منخفضة المستوى مع بعضها البعض ، لكن Rust يتحدى هذا الصراع.
مؤلفو الكتاب هم جزء من فريق تطوير اللغة ، مما يعني أنك ستتلقى جميع المعلومات مباشرة - من تثبيت اللغة إلى إنشاء برامج موثوقة وقابلة للتطوير. من إنشاء الوظائف واختيار أنواع البيانات ومتغيرات الربط ، ستنتقل إلى مفاهيم أكثر تعقيدًا:
- التملك والاقتراض ودورة الحياة وأنواعها.
- أمان مضمون للبرنامج.
- الاختبار ومعالجة الأخطاء وإعادة البناء الفعال.
- العوامل العامة ، والمؤشرات الذكية ، وتعدد مؤشرات الترابط ، والكائنات القابلة للتداول والتعيينات.
- اعمل مع مدير الحزم المدمج Cargo لبناء واختبار وتوثيق التعليمات البرمجية وإدارة التبعيات.
- أدوات متقدمة للعمل مع الصدأ غير الآمن.
ستجد الكثير من أمثلة التعليمات البرمجية ، بالإضافة إلى ثلاثة فصول حول إنشاء مشاريع كاملة لترسيخ المعرفة: ألعاب التخمين ، إنشاء أداة سطر أوامر ، وخادم متعدد الخيوط.
لمن هذا الكتاب
نفترض أنك كتبت الكود الخاص بك بلغة برمجة مختلفة ، لكننا لا نفترض أيًا منها. لقد حاولنا جعل هذه المواد في متناول أولئك الذين لديهم مجموعة واسعة من مهارات البرمجة. لن نضيع الوقت في الحديث عن ماهية البرمجة. إذا كنت مبتدئًا تمامًا في البرمجة ، فاقرأ أولاً مقدمة البرمجة.
كيفية استخدام هذا الكتاب
-, , , . , ; .
: . . , , . 2, 12 20 , — .
1 , Rust, «Hello, World!» Cargo. 2 Rust. , . , . 3, Rust, , 4 Rust. , , 2, 3, 2, . , .
5 , 6 , match if let. Rust .
7 (API). 8 , , , -. 9 .
10 , , , . 11 , Rust . 12 grep, . , .
13 — , . 14 Cargo . 15 , , , .
16 , Rust . 17 Rust - , , , .
18 , Rust. 19 , , Rust, , , .
20 , !
, . Rust, Rust, , , , Rust.
: -, ! - , , , . , , .
Rust — , : . , , . , , ! , , , , . , .
: . . , , . 2, 12 20 , — .
1 , Rust, «Hello, World!» Cargo. 2 Rust. , . , . 3, Rust, , 4 Rust. , , 2, 3, 2, . , .
5 , 6 , match if let. Rust .
7 (API). 8 , , , -. 9 .
10 , , , . 11 , Rust . 12 grep, . , .
13 — , . 14 Cargo . 15 , , , .
16 , Rust . 17 Rust - , , , .
18 , Rust. 19 , , Rust, , , .
20 , !
, . Rust, Rust, , , , Rust.
: -, ! - , , , . , , .
Rust — , : . , , . , , ! , , , , . , .
أين يمكن استخدام الأنماط
في Rust ، تظهر الأنماط في العديد من الأماكن ، وقد استخدمتها كثيرًا دون أن تدرك ذلك! يناقش هذا القسم المواقف التي تكون فيها الأنماط صحيحة.
تطابق فروع التعبير
كما تمت مناقشته في الفصل 6 ، نستخدم أنماطًا في فروع تعبيرات المطابقة. بشكل رسمي ، يتم تعريف تعبيرات المطابقة على أنها الكلمة الرئيسية المطابقة ، ثم القيمة المراد مطابقتها ، وفرع واحد أو أكثر للمطابقة يتكون من النمط والتعبير المراد تنفيذه إذا كانت القيمة تتطابق مع نمط ذلك الفرع ، على سبيل المثال:
match {
=> ,
=> ,
=> ,
}
أحد متطلبات تعبيرات المطابقة هو أنها يجب أن تكون شاملة بمعنى أنه يجب مراعاة جميع القيم الممكنة في التطابق. لكي تفكر في جميع الخيارات الممكنة ، يجب أن يكون لديك نمط شامل في الفرع الأخير: على سبيل المثال ، سيتم إطلاق اسم متغير يطابق أي قيمة دائمًا وبالتالي يغطي جميع الحالات المتبقية.
سيتطابق النمط الخاص _ مع أي شيء ، لكنه لا يرتبط بمتغير ، ولذلك غالبًا ما يستخدم في آخر كم من المطابقة. يعد النمط _ مفيدًا ، على سبيل المثال ، إذا كنت تريد تجاهل أي قيمة غير محددة. سننظر في النمط بمزيد من التفصيل في قسم "تجاهل القيم في النمط".
إذا ترك الشرطية
في الفصل السادس ، ناقشنا عبارات if let بشكل أساسي كطريقة أقصر لكتابة ما يعادل تعبير تطابق يطابق حالة واحدة فقط. بدلاً من ذلك ، إذا كان يمكن أن يكون لدى let مطابقة أخرى تحتوي على الكود المطلوب تنفيذه إذا لم يتطابق النمط الموجود في if let.
تُظهر القائمة 18.1 أنه من الممكن أيضًا الخلط والمطابقة if let و if و else if let. يمنحنا هذا مرونة أكثر من استخدام تعبير المطابقة ، والذي يمكنه فقط التعبير عن قيمة واحدة للمقارنة مع الأنماط. بالإضافة إلى ذلك ، فإن الشروط في سلسلة if let و else if و else if لا تحتاج إلى الإشارة إلى بعضها البعض.
يُظهر الكود الموجود في القائمة 18.1 سلسلة من الاختبارات للعديد من الشروط التي تحدد لون الخلفية الذي يجب أن يكون. في هذا المثال ، أنشأنا متغيرات بقيم مشفرة يمكن لبرنامج حقيقي استردادها من مدخلات المستخدم.
قائمة 18.1. خلط إذا ترك ، وإلا إذا ، وإلا إذا تركت العبارات وغيرها
src/main.rs
fn main() {
let favorite_color: Option<&str> = None;
let is_tuesday = false;
let age: Result<u8, _> = "34".parse();
(1) if let Some(color) = favorite_color {
(2) println!(" , {}, ", color);
(3) } else if is_tuesday {
(4) println!(" - !");
(5) } else if let Ok(age) = age {
(6) if age > 30 {
(7) println!(" ");
} else {
(8) println!(" ");
}
(9) } else {
(10) println!(" ");
}
}
إذا حدد المستخدم لونًا مفضلًا (1) ، فهذا هو لون الخلفية (2). إذا كان اليوم هو الثلاثاء (3) ، يكون لون الخلفية أخضر (4). إذا حدد المستخدم عمره كسلسلة ويمكننا تحليله بنجاح كرقم (5) ، فسيكون اللون إما أرجواني (7) أو برتقالي (8) حسب قيمة الرقم (6). إذا لم تنطبق أي من هذه الشروط (9) ، فإن لون الخلفية يكون أزرق (10).
تسمح هذه البنية الشرطية بالمتطلبات المعقدة. مع القيم المشفرة الموجودة هنا ، سيتم إخراج هذا المثال
.
يمكنك أن ترى أن تعبير if let يمكنه أيضًا تقديم متغيرات مظللة بنفس الطريقة مثل أكمام تعبير المطابقة: إذا تركت Ok (age) = age (5) سطر من التعليمات البرمجية يقدم عمرًا متغيرًا مظللًا جديدًا يحتوي على القيمة داخل متغير Ok. هذا يعني أننا بحاجة إلى وضع الشرط إذا كان العمر أكبر من 30 في هذه المجموعة (6): لا يمكننا الجمع بين هذين الشرطين في العبارة إذا سمحنا Ok (age) = age && age> 30. العمر المتغير المظلل الذي نريد مقارنته بـ 30 ، سيكون غير صالح حتى يبدأ النطاق الجديد بقوس مجعد.
عيب استخدام عبارات if let هو أن المترجم لا يتحقق من الشمولية ، بينما يفعل لبيانات المطابقة. إذا كنا قد تخطينا آخر كتلة (9) أخرى ، وبالتالي ، معالجة بعض الحالات ، فلن يحذرنا المترجم من خطأ منطقي محتمل.
بينما دع الحلقات الشرطية
مشابه في البناء لعبارة if let ، تسمح حلقة while let الشرطية بتشغيل حلقة while طالما أن النمط مطابق. يوضح المثال في القائمة 18.2 حلقة let التي تستخدم متجهًا كمكدس وتخرج القيم الموجودة في المتجه بالترتيب العكسي للترتيب الذي تمت إضافتها به.
قائمة 18.2. يؤدي استخدام while let loop لطباعة القيم بينما يقوم stack.pop () بإرجاع بعض
let mut stack = Vec::new();
stack.push(1);
stack.push(2);
stack.push(3);
while let Some(top) = stack.pop() {
println!("{}", top);
}
يطبع هذا المثال 3 ، 2 ، ثم 1. تأخذ طريقة pop العنصر الأخير من المتجه وتُرجع Some (قيمة). إذا كان المتجه فارغًا ، فإن pop ترجع بلا. تستمر حلقة while في تنفيذ الكود في كتلتها حتى ترجع pop إلى Some. عندما ترجع فرقعة بلا ، تتوقف الحلقة. يمكننا استخدام حلقة شرطية while let لإزالة كل عنصر من المكدس.
للحلقات
ذكرنا في الفصل 3 أن الحلقة for هي أكثر إنشاءات الحلقات شيوعًا في كود Rust ، لكننا لم نناقش النمط الذي يستغرقه الأمر حتى الآن. في حلقة for ، يكون النمط هو القيمة التي تلي الكلمة المفتاحية مباشرة ، لذا في حالة x في y ، يكون النمط هو x.
تُظهر القائمة 18.3 استخدام نمط في حلقة for لتدمير أو تحلل مجموعة داخل مجموعة for.
قائمة 18.3. استخدام نمط في حلقة for لتدمير بنية tuple
let v = vec!['a', 'b', 'c'];
for (index, value) in v.iter().enumerate() {
println!("{} {}", value, index);
}
يعرض الكود في القائمة 18.3 ما يلي:
0
b 1
2
نستخدم طريقة التعداد لإعادة كتابة المكرر لإنتاج قيمة وفهرس تلك القيمة في المكرر ، الموضوعة في بنية tuple. ينتج عن الاستدعاء الأول لطريقة التعداد مجموعة (0، 'a'). عندما يتم دمج هذه القيمة مع نمط (الفهرس ، القيمة) ، يكون الفهرس 0 والقيمة "أ" ، يتم إخراج أول صف من البيانات.
دع البيانات
قبل هذا الفصل ، ناقشنا بشكل مباشر استخدام الأنماط فقط مع عبارات match و if ، لكننا في الواقع استخدمنا أنماطًا في مكان آخر ، بما في ذلك العبارات let. فكر في طريقة بسيطة لتمرير قيمة المتغير باستخدام let:
let x = 5;
لقد استخدمنا هذا النوع من العبارات مئات المرات في هذا الكتاب ، وعلى الرغم من أنك ربما لم تكن قد أدركت ذلك ، فأنت تستخدم الأنماط! بشكل أكثر رسمية ، يبدو بيان Let كما يلي:
let = ;
في عبارات مثل let x = 5 ؛ مع اسم متغير في فتحة PATTERN ، يكون اسم المتغير مجرد شكل بسيط من النموذج. يقارن Rust التعبير بالنمط ويعين أي أسماء يعثر عليها. لذلك ، في المثال ، دع x = 5 ؛ النمط هو x ، مما يعني "ربط ما يتطابق هنا مع المتغير x". نظرًا لأن الاسم x يمثل النمط بأكمله ، فإن هذا النمط يعني بشكل فعال "ربط كل شيء بالمتغير x ، مهما كانت القيمة".
لرؤية التعيين مقابل نمط بيان let بشكل أكثر وضوحًا ، ضع في اعتبارك القائمة 18.4 ، التي تستخدم نمط let لتدمير مجموعة.
قائمة 18.4. استخدام نمط لتدمير مجموعة وإنشاء ثلاثة متغيرات في وقت واحد
let (x, y, z) = (1, 2, 3);
هنا نقوم بتعيين مجموعة إلى نمط. يقارن Rust (1 ، 2 ، 3) بـ (x ، y ، z) ويرى أن هذه القيمة تطابق النمط ، لذلك يربط Rust 1 بـ x ، و 2 بـ y ، و 3 بـ z. يمكنك التفكير في نمط المجموعة هذا على أنه يضم ثلاثة أنماط متغيرة منفصلة فيه.
إذا كان عدد العناصر في النموذج لا يتطابق مع عدد العناصر في المجموعة ، فلن يتطابق النوع التجميعي وسنحصل على خطأ في المترجم. على سبيل المثال ، تُظهر القائمة 18.5 محاولة إتلاف ثلاث مجموعات إلى متغيرين ، والتي لن تعمل.
قائمة 18.5. بناء غير صحيح للنمط ، المتغيرات التي لا تتطابق مع عدد العناصر في المجموعة
let (x, y) = (1, 2, 3);
تؤدي محاولة تجميع هذا الرمز إلى حدوث خطأ مثل:
error[E0308]: mismatched types
--> src/main.rs:2:9
|
2 | let (x, y) = (1, 2, 3);
| ^^^^^^ expected a tuple with 3 elements, found one with 2 elements
|
= note: expected type `({integer}, {integer}, {integer})`
found type `(_, _)`
إذا أردنا تجاهل قيمة واحدة أو أكثر في المجموعة ، فيمكننا استخدام _ أو .. ، كما سترى في قسم "تجاهل القيم في نمط". إذا كانت المشكلة هي وجود العديد من المتغيرات في النمط ، فأنت بحاجة إلى جعل الأنواع متطابقة عن طريق حذف المتغيرات بحيث يكون عدد المتغيرات مساويًا لعدد العناصر في المجموعة.
معلمات الوظيفة
يمكن أن تكون معلمات الوظيفة أيضًا أنماطًا. إن الكود الموجود في القائمة 18.6 والذي يعلن الدالة foo التي تأخذ معلمة واحدة x من النوع i32 مألوف لك الآن.
قائمة 18.6. يستخدم توقيع الوظيفة الأنماط في المعلمات
fn foo(x: i32) {
//
}
الجزء العاشر هو نمط! كما هو الحال مع let ، يمكننا مطابقة tuple في وسيطات الدالة بالنمط. سرد 18.7 يكسر القيم الموجودة في المجموعة عندما نمررها داخل الدالة.
قائمة 18.7. تعمل مع المعلمات التي تدمر المجموعة
src/main.rs
fn print_coordinates(&(x, y): &(i32, i32)) {
println!(" : ({}, {})", x, y);
}
fn main() {
let point = (3, 5);
print_coordinates(&point);
}
نواتج هذا الرمز
: (3, 5)
تتطابق القيم & (3 ، 5) مع النمط & (x ، y) ، لذا فإن x تساوي 3 و y تساوي 5.
بالإضافة إلى ذلك ، يمكننا استخدام الأنماط في قوائم معلمات الإغلاق بنفس الطريقة كما في قوائم معلمات الوظيفة ، نظرًا لأن تشبه عمليات الإغلاق الوظائف ، كما هو موضح في الفصل 13.
لقد رأيت بالفعل عدة طرق لاستخدام الأنماط ، لكنها لا تعمل بالطريقة نفسها في كل مكان تستخدم فيه. في بعض المواقف ، يجب أن تكون هذه الأنماط غير قابلة للدحض ؛ وفي حالات أخرى ، يمكن دحضها. سنناقش هذين المفهومين أدناه.
القابلية للدحض: إمكانية عدم تطابق الأنماط
هناك نوعان من الأنماط: قابلة للدحض ولا تقبل الجدل. الأنماط التي ستطابق أي قيمة ممكنة تم تمريرها لا يمكن دحضها. مثال على ذلك هو x في let x = 5 ؛ العبارة ، لأن x تطابق كل شيء تمامًا ، وبالتالي لا يمكن إلا أن تتطابق. النماذج التي لا تتطابق مع بعض المعاني المحتملة قابلة للدحض. مثال على ذلك هو Some (x) في if let Some (x) = a_value بيان ، لأنه إذا كانت القيمة في a_value هي None وليس Some ، فلن تتطابق بعض (x).
يمكن لمعلمات الوظيفة ، وعبارات let ، و for loops قبول الأنماط غير القابلة للدحض فقط لأن البرنامج لا يمكنه فعل أي شيء ذي معنى عندما لا تتطابق القيم. إن تعبيرات if let and while let تقبل فقط الأنماط القابلة للدحض ، لأنها مصممة للتعامل مع خطأ محتمل بحكم التعريف: وظيفة التعبير الشرطي هي قدرته على تنفيذ إجراءات مختلفة اعتمادًا على النجاح أو الفشل.
بشكل عام ، لا يجب أن تقلق بشأن التمييز بين الأنماط القابلة للدحض والأنماط التي لا تقبل الجدل. ومع ذلك ، ما زلت بحاجة إلى أن تكون على دراية بمفهوم الطعن من أجل الرد عند رؤيته في رسالة خطأ. في هذه الحالات ، ستحتاج إلى تغيير النمط أو البنية التي تستخدم بها النمط ، اعتمادًا على السلوك المقصود للشفرة.
دعونا نلقي نظرة على ما يحدث عندما نحاول استخدام نمط لا يمكن دحضه في مكان يتطلب فيه الصدأ نمطًا لا يمكن دحضه ، والعكس صحيح. تُظهر القائمة 18.8 بيان let ، ولكن بالنسبة للنمط الذي حددناه Some (x) ، نمط قابل للدحض. كما قد تتوقع ، لا يتم تجميع هذا الرمز.
قائمة 18.8. محاولة استخدام نمط قابل للدحض مع let
let Some(x) = some_option_value;
إذا كانت some_option_value لا شيء ، فلن تتطابق مع نمط Some (x) ، أي أن النمط قابل للدحض. ومع ذلك ، يمكن أن تقبل تعليمة let فقط نمطًا لا يمكن دحضه ، نظرًا لأن الكود لا يمكنه فعل أي شيء صالح مع القيمة None. في وقت الترجمة ، سيشتكي Rust من أننا حاولنا استخدام نمط قابل للدحض حيث يتطلب نمط غير قابل للدحض:
error[E0005]: refutable pattern in local binding: `None` not covered
-->
|
3 | let Some(x) = some_option_value;
| ^^^^^^^ pattern `None` not covered
نظرًا لأننا لم نغطي (ولم نتمكن من التغطية!) كل قيمة صالحة بنمط Some (x) ، فإن Rust يلقي خطأ في المترجم.
لإصلاح المشكلة عندما يكون لدينا نمط قابل للدحض بدلاً من نمط لا يمكن دحضه ، يمكننا تغيير الكود الذي يستخدم النمط: بدلاً من let ، يمكننا استخدام if let. بعد ذلك ، إذا لم يتطابق النمط ، فسيتم تخطي الرمز الموجود في الأقواس المتعرجة وسيستمر العمل بشكل صحيح. توضح القائمة 18.9 كيفية إصلاح الكود في القائمة 18.8.
قائمة 18.9. استخدام تعليمة if let وكتلة نمط قابلة للدحض بدلاً من let
if let Some(x) = some_option_value {
println!("{}", x);
}
الكود جاهز! هذا رمز صحيح تمامًا ، على الرغم من أنه يعني أنه لا يمكننا استخدام نمط لا يمكن دحضه بدون خطأ. إذا أعطينا التعبير if ، نمطًا يتطابق دائمًا ، مثل x ، كما هو موضح في القائمة 18-10 ، فلن يتم تجميعه.
قائمة 18.10. محاولة استخدام نمط لا يمكن دحضه مع بيان if let
if let x = 5 {
println!("{}", x);
};
يشكو المترجم من أنه ليس من المنطقي استخدام تعبير if let بنمط لا يمكن دحضه:
error[E0162]: irrefutable if-let pattern
--> <anon>:2:8
|
2 | if let x = 5 {
| ^ irrefutable pattern
لهذا السبب ، يجب أن تستخدم أكمام تعبير المطابقة أنماطًا قابلة للدحض ، باستثناء الكم الأخير ، الذي يجب أن يتطابق مع أي قيم متبقية مقابل نمط لا يمكن دحضه. يسمح الصدأ باستخدام النمط الذي لا يمكن دحضه في تعبير تطابق مع جلبة واحدة فقط ، ولكن هذا التركيب غير مفيد بشكل خاص ويمكن استبداله بعبارة let أبسط.
الآن بعد أن عرفت مكان استخدام الأنماط ومدى اختلاف الأنماط القابلة للدحض والتي لا يمكن دحضها ، دعنا نتعرف على البنية التي يمكننا استخدامها لإنشاء أنماط.
عن المؤلفين
يقود Steve Klabnik فريق توثيق Rust وهو أحد المطورين الرئيسيين للغة. إنه محاضر متكرر ويكتب الكثير من التعليمات البرمجية مفتوحة المصدر. عمل سابقًا في مشاريع مثل Ruby و Ruby on Rails.
كارول نيكولز عضو في فريق تطوير Rust Core ومؤسس مشارك لـ Integer 32، LLC ، أول شركة استشارات لتطوير البرمجيات تركز على الصدأ في العالم. نيكولز هو منظم مؤتمر Rust Belt حول لغة الصدأ.
»يمكن العثور على مزيد من التفاصيل حول الكتاب على موقع الناشر
» جدول المحتويات
» مقتطفات من
هابيتانز خصم 25٪ على القسيمة - الصدأ
عند الدفع مقابل النسخة الورقية من الكتاب ، يتم إرسال كتاب إلكتروني إلى البريد الإلكتروني.